Rẽ nhánh là một trong các tính năng tôi thích của Git. Khi làm việc với Git, bạn nên tạm quên đi khái niệm nhánh mà bạn từng biết trong các hệ thống quản lý phiên bản khác, vì thực tế, mỗi nhánh trong Git gần giống như một ngữ cảnh (điều kiện hay hoàn cảnh làm việc). Việc rẽ vào một nhánh tương tự việc chuyển qua ngữ cảnh làm việc mới, và sau đó có thể nhanh chóng quay lại ngữ cảnh cũ.
ND: Việc rẽ nhánh giống như việc đang đi trên đường thì bạn muốn rẽ sang ngã khác (ngã ba, ngã tư chẳng hạn), để thực hiện một số việc như thăm người quen, tránh kẹt xe, rồi quay trở lại đường chính sau. Một nhánh trong Git gần như một đoạn đường hoặc con đường. Nếu các con đường ở Sài Gòn đã cố định, thì con đường trong Git dễ chịu hơn mà bạn có thể tạo ra bất kỳ lúc nào :)
Tóm tắt: bạn tạo ra một nhánh mới bằng
git branch tên_nhánh
, sau đó rẽ vào nhánh đó bằng
git checkout tên_nhánh
; sau lưu lại các thay đổi trong
ngữ cảnh này, bạn có thể rẽ qua nhánh khác một cách dễ dàng.
Mỗi khi rẽ vào một nhánh, Git sẽ thay thư mục làm việc cho tương ứng
với nhánh đó; vì vậy, bạn không phải xử lý nhiều thư mục khác nhau
cho nhiều nhánh (có nghĩa là, chỉ dùng một thư mục cho nhiều nhánh. Điều
này rất khác với cách tiếp cận của Subversion -- ND). Cuối cùng, để
tích hợp hai nhánh với nhau, dùng git merge
. Có thể dễ dàng
tích hợp nhiều lần hai nhánh với nhau, hoặc có thể xóa một nhánh sau khi
nó đã được tích hợp.
Lệnh git branch
là để quản lý các nhánh; nó có thể
làm nhiều việc khác nhau, nên chúng ta chỉ để cập một số việc cơ bản
của lệnh này: liệt kê, tạo hoặc xóa nhánh. Ta cũng đề cập tới lệnh
git checkout
để rẽ vào một nhánh bất kỳ.
Khi không có tham số, git branch
sẽ liệt kê các nhánh
sẵn có, riêng nhánh bạn đang rẽ vào được đánh dấu sao * hoặc
tô màu xanh lá).
$ git branch
* master
Cho biết bạn đang rẽ vào nhánh 'master'. Đó là nhánh được tạo ra
tự động khi bạn khởi tạo kho bằng lệnh git init
. Cái tên
'master' không phải là bắt buộc, nhưng nó được hầu hết dự án dùng do
sự lựa chọn mặc định của Git.
Bây gờ, ta thử tạo một nhánh mới rồi rẽ vào nhánh đó.
$ git branch testing
$ git branch
* master
testing
Như bạn thấy, sau khi tạo ta sẽ thấy thêm nhánh mới khi liệt kê.
Nhánh mới tạo ra sẽ chứa những thay đổi cuối cùng đã xác nhận. Vì thế,
sau thời điểm nhánh 'testing' được tạo ra, bạn xác nhận các thay đổi
mới (sẽ diễn ra ở nhánh 'master'), rồi lại rẽ vào nhánh 'testing',
thì thư mục làm việc sẽ trở lại trạng thái ngay trước khi nhánh đó
đuợc tạo ra (nghĩa là nó sẽ không chứa các thay đổi bạn vừa xác nhận).
Như vậy, nhánh giống như các đánh dấu trên đường đi cho biết bạn đang
ở đâu. Ta hãy xem việc rẽ nhánh sẽ diễn ra thế nào, với lệnh
git checkout tên_nhánh
$ ls README hello.rb $ echo 'test content' > test.txt $ echo 'more content' > more.txt $ git add *.txt $ git commit -m 'added two files' [master 8bd6d8b] added two files 2 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 more.txt create mode 100644 test.txt $ ls README hello.rb more.txt test.txt $ git checkout testing Switched to branch 'testing' $ ls README hello.rb
Bây giờ, ta rẽ vào nhánh 'testing'. Bạn sẽ thấy các tập tin vừa tạo ra ở trên sẽ bị xóa đi, nhưng chúng sẽ xuất hiện trở lại khi bạn quay lại nhánh ban đầu 'master'.
$ ls README hello.rb $ git checkout master Switched to branch 'master' $ ls README hello.rb more.txt test.txt
Nếu muốn xem xác nhận cuối của mỗi nhánh, bạn dùng lệnh git branch
-v
$ git branch -v
* master 54b417d fix javascript issue
development 74c111d modify component.json file
testing 62a557a update test scripts
Trong nhiều trường hợp bạn muốn rẽ vào một nhánh ngay sau khi nhánh
đó được tạo ra. Thay vì phải dùng cặp lệnh
git branch tên_nhán; git checkout tên_nhánh
,
bạn chỉ gõ tắt
git checkout -b tên_nhánh
.
$ git branch * master $ ls README hello.rb more.txt test.txt $ git checkout -b removals Switched to a new branch 'removals' $ git rm more.txt rm 'more.txt' $ git rm test.txt rm 'test.txt' $ ls README hello.rb $ git commit -am 'removed useless files' [removals 8f7c949] removed useless files 2 files changed, 0 insertions(+), 2 deletions(-) delete mode 100644 more.txt delete mode 100644 test.txt $ git checkout master Switched to branch 'master' $ ls README hello.rb more.txt test.txt
Như thấy ở trên, ta rẽ vào nhánh 'removals' ngay sau khi tạo ra nhánh đó; trong nhánh này, ta thực hiện xóa bớt một số tập tin, xác nhận việc xóa và rồi quay lại nhánh chính 'master', ở đó các tập tin vừa xóa sẽ xuất hiện trở lại. Như vậy, việc rẽ nhánh hiểu và phân chia thư mục làm việc theo đúng ngữ cảnh được chỉ định; việc chuyển đổi giữa các nhánh không làm mất đi tập tin hoặc không gây ra nhầm lẫn.
Khi bắt đầu một dự án, bạn nên rẽ vào một nhánh riêng của dự án đó (việc này cũng nhanh và dễ), và sau đó tích hợp nhánh của bạn với nhánh chính của dự án; sau khi tích hợp thì có thể xóa nhánh riêng bạn tạo ra. Bằng cách đó, bạn dễ dàng chuyển qua phiên bản (nhánh) ổn định của dự án khi xảy ra vấn đề trên nhánh bạn phát triển; hơn nữa, bạn cũng có thể dễ dàng tạm gác lại công việc của bạn trên nhánh riêng, để quay trở lại các nhánh cũ do yêu cầu bắt buộc của dự án.
Để xóa một nhánh, bạn chỉ việc dùng lệnh
git branch -d tên_nhánh
. Ví dụ, ta sẽ xóa nhánh
'testing' đã tạo ra ở ví dụ ở trên.
$ git branch * master testing $ git branch -d testing Deleted branch testing (was 78b2670). $ git branch * master
Khi nhánh đầu xa bạn làm việc đã được tích hợp vào nhánh chính đầu xa
master
hoặc bạn muốn xóa bỏ để dọn dẹp, bạn kèm theo vào sau lệnh
git push
một dấu hai chấm và tên nhánh cần xóa như ví dụ
sau:
$ git push origin :tidy-cutlery To git@github.com:octocat/Spoon-Knife.git - [deleted] tidy-cutlery
Ở ví dụ trên, chúng ta đã xóa nhánh "tidy-cutlery" khỏi kho đầu xa
"origin". Cách dễ nhớ nhất là bạn hãy nhớ lệnh dài ngoằng sau đây git
push remote-name local-branch:remote-branch
. Ý nghĩa của câu lệnh
này là bạn muốn đẩy lên nhánh của kho trên máy tính của bạn trùng với kho đầu
xa. Khi bạn xóa phần local-branch
, thì phần nhánh đầu xa cũng sẽ
được xóa đi để trùng với nhánh trên máy tính.
Một cách khác, lệnh git push remote-name --delete
branchname
với cặp source:destination
cũng được
dùng xóa nhánh đầu xa.
Tóm tắt: bạn dùng git branch
để liệt kê các nhánh
hiện có, rẽ nhánh mới hoặc xóa đi các nhánh đã tích hợp hoặc không còn cần thiết.
Sau khi chia công việc riêng ra từng nhánh, có lúc bạn sẽ cần tích hợp
các nhánh với nhau, hoặc với nhánh chính. Để tích hợp, bạn dùng lệnh
git merge
.
Xét ví dụ ở trên với nhánh 'removals'. Ta nhớ lại rằng sau khi rẽ vào
nhánh 'removals', ta xóa đi một vài tập tin và xác nhận sự thay đổi.
Việc xóa tập tin này chỉ diễn ra ở nhánh 'removals', còn ở nhánh chính
'master' không có tập tin nào được xóa. Bây giờ, để xác nhận rằng cả
các tập tin bị xóa trong 'removals' cũng bị xóa trong 'master', ta chỉ
việc tích hợp nhánh 'removals' với 'master'.
$ git branch * master removals $ ls README hello.rb more.txt test.txt $ git merge removals Updating 8bd6d8b..8f7c949 Fast-forward more.txt | 1 - test.txt | 1 - 2 files changed, 0 insertions(+), 2 deletions(-) delete mode 100644 more.txt delete mode 100644 test.txt $ ls README hello.rb
Tất nhiên, quá trình phát triển của mã nguồn không chỉ đơn giản là thêm và xóa tập tin. Git còn hỗ trợ thêm việc tích hợp các thay đổi (hoặc sự khác biệt) giữa của tập tin ở hai nhánh khác nhau. Lấy ví dụ, ta sẽ chỉnh nội dung tập tin ở một nhánh, trong khi ở nhánh kia ta đổi tên tập tin, rồi rồi tích hợp hai tập tin đó với nhau. Nghe có vẻ rối đây! Xem Git giải quyết thế nào nhé!
$ git branch * master $ cat hello.rb class HelloWorld def self.hello puts "Hello World" end end HelloWorld.hello
Bây giờ ta đang ở nhánh 'master'. Ta sẽ tạo nhánh 'change_class' và rẽ ngay vào nhánh đó: trong nhánh mới ta sẽ thực hiện đổi tên lớp từ 'HelloWorld' dthành 'HiWorld'.
$ git checkout -b change_class Switched to a new branch 'change_class' $ vim hello.rb $ head -1 hello.rb class HiWorld $ git commit -am 'changed the class name' [change_class 3467b0a] changed the class name 1 files changed, 2 insertions(+), 4 deletions(-)
Việc đổi tên lớp được xác nhận sau lệnh git commit
.
Ta sẽ trở lại nhánh chính 'master' và sẽ thấy rằng trong tên lớp
vẫn là 'HelloWorld' (như cũ!). Ta sẽ đổi xem có sự khác biệt nào
với thay đổi đã thực hiện ở nhánh 'change_class', đồng thời đổi tên
tập tin từ hello.rb
thành ruby.rb
.
$ git checkout master Switched to branch 'master' $ git mv hello.rb ruby.rb $ vim ruby.rb $ git diff diff --git a/ruby.rb b/ruby.rb index 2aabb6e..bf64b17 100644 --- a/ruby.rb +++ b/ruby.rb @@ -1,7 +1,7 @@ class HelloWorld def self.hello - puts "Hello World" + puts "Hello World from Ruby" end end $ git commit -am 'added from ruby' [master b7ae93b] added from ruby 1 files changed, 1 insertions(+), 1 deletions(-) rename hello.rb => ruby.rb (65%)
Như ban thấy, sự thay đổi tên tập tin và nội dung cũng vừa được xác nhận trong nhánh 'master'. Để ý rằng, tên lớp vẫn là 'HelloWorld'. Nếu muốn thay đổi cả tên của lớp thành 'HiWorld', ta chỉ việc tích hợp nội dung ở nhánh 'change_class' vào nhánh 'master'. Nhưng liệu Git có tích hợp được không, vì tên tập tin đã đổi? Hãy xem:
$ git branch change_class * master $ git merge change_class Renaming hello.rb => ruby.rb Auto-merging ruby.rb Merge made by recursive. ruby.rb | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) $ cat ruby.rb class HiWorld def self.hello puts "Hello World from Ruby" end end HiWorld.hello
Tốt quá, mọi chuyện diễn ra đúng như mong đợi. Như vậy trong quá trình tích hợp ta đã không gặp xung đột nào, kể cả việc đổi tên tập tin hoặc đổi tên lớp diễn ra ở nhánh khác
Như vậy, Git thật là khéo léo, nhưng ta chưa hề đụng tới trường hợp có xung đột. Không sao, khi xảy ra sự khác biệt về mã giữa hai nhánh mà Git không thể hình dung được, sẽ tới phiên chúng ta giải quyết. Trong ví dụ sau, ta sẽ thay đổi cùng một dòng của tập tin ở hai nhánh.
$ git branch * master $ git checkout -b fix_readme Switched to a new branch 'fix_readme' $ vim README $ git commit -am 'fixed readme title' [fix_readme 3ac015d] fixed readme title 1 files changed, 1 insertions(+), 1 deletions(-)
Sau khi rẽ vào nhánh 'fix_readme', ta thực hiện thay đổi một dòng trong tập tin 'README'. Tiếp theo ta sẽ thay đổi cùng dòng đó, nhưng ở tập tin của nhánh 'master'.
$ git checkout master Switched to branch 'master' $ vim README $ git commit -am 'fixed readme title differently' [master 3cbb6aa] fixed readme title differently 1 files changed, 1 insertions(+), 1 deletions(-)
Hãy xem có điều thú vị gì xảy ra khi tích hợp hai tập tin có xung đột với nhau:
$ git merge fix_readme Auto-merging README CONFLICT (content): Merge conflict in README Automatic merge failed; fix conflicts and then commit the result. $ cat README <<<<<<< HEAD Many Hello World Examples ======= Hello World Lang Examples >>>>>>> fix_readme This project has examples of hello world in nearly every programming language.
Như bạn thấy, Git chèn vào các ký hiệu đánh dấu sự xung đột xảy ra khi tích hợp. Git đã 'bó tay' khi dừng lại ở đó, và tới phiên bạn quyết định. Để ý là, một số công cụ đồ họa như kdiff3, emerge, p4merge, ... sẽ giúp việc giải quyết xung đột dễ dàng hơn.
$ vim README # here I'm fixing the conflict $ git diff diff --cc README index 9103e27,69cad1a..0000000 --- a/README +++ b/README @@@ -1,4 -1,4 +1,4 @@@ - Many Hello World Examples -Hello World Lang Examples ++Many Hello World Lang Examples This project has examples of hello world in
Một mẹo rất hay khi tích hợp các xung đột trong Git là nếu khi dùng
git diff
,
chương trình sẽ cho bạn thấy cả hai phần (cũ, mới) gây nên xung đột,
và cả cách giải quyết xung đột. Sau đó, bạn cần đánh dấu cho tập tin
rằng xung đột trên nó đã giải quyết xong: bạn sử dụng lệnh
git add
và cả git commit
để xác nhận.
$ git status -s UU README $ git add README $ git status -s M README $ git commit [master 8d585ea] Merge branch 'fix_readme'
Bây giờ thì xung đột được giải quyết trọn vẹn.
Tóm tắt: bạn dùng git merge
để tích hợp một nhánh với
nhánh bạn đang rẽ vào. Git sẽ tự động xác định phép tích hợp tốt nhất để
có được sản phẩm chung lai giữa hai nhánh.
Ta đã biết cách xác nhận các thay đổi, cách chuyển qua lại giữa
các nhánh. Nhưng chuyện gì xảy ra một khi ta quên mất ta đang ở nhánh
nào, ở vị trí nào trong một nhánh, hoặc lý do ta rẽ nhánh? Hoặc ta quên
mất sự khác biệt giữa các nhánh? Lường trước cách trả lời cho câu hỏi này,
Git cung cấp lệnh git log
để giúp bạn xem lại mô tả hoặc
lý do của tất cả các lần xác nhận.
Truớc khi hiểu về lệnh log
, bạn cần biết Git lưu những
gì mỗi khi bạn xác nhận sự thay đổi bằng lệnh git commit
.
Bên cạnh thông tin về các tập tin, người thay đổi, thông điệp, Git còn
lưu thông tin về xác nhận có liên quan trực tiếp tới xác nhận đang thực
hiện. Để hình dung, ta lấy ví dụ: khi bạn nhân bản một dự án, bạn cần
biết là thay đổi của bạn cho nhân bản đó dựa trên trạng thái nào của dự
án đã có. Thông tin về trạng thái cũ rất có ích, giúp bạn xác nhận ngữ
cảnh (nhánh) của bạn và giúp hình dung quá trình phát triển của toàn bộ
dự án. Bằng cách đặt tên 'cha' (parent) có ngữ cảnh gốc, Git tự động
biết được bạn làm gì cho ngữ cảnh của bạn (con).
Để xem danh sách các xác nhận 'cha' theo thời gian, bạn dùng lệnh
git log
khi đang ở một nhánh bất kỳ. Ví dụ, kết quả của lệnh
git log
trong dự án HelloWorld ở trên như sau đây.
$ git log commit 8d585ea6faf99facd39b55d6f6a3b3f481ad0d3d Merge: 3cbb6aa 3ac015d Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:59:47 2010 +0200 Merge branch 'fix_readme' Conflicts: README commit 3cbb6aae5c0cbd711c098e113ae436801371c95e Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:53 2010 +0200 fixed readme title differently commit 3ac015da8ade34d4c7ebeffa2053fcac33fb495b Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:36 2010 +0200 fixed readme title commit 558151a95567ba4181bab5746bc8f34bd87143d6 Merge: b7ae93b 3467b0a Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:37:05 2010 +0200 Merge branch 'change_class' ...
Để xem lược sử ngắn gọn trong một dòng, ta dùng tùy chọn
--oneline
.
$ git log --oneline 8d585ea Merge branch 'fix_readme' 3cbb6aa fixed readme title differently 3ac015d fixed readme title 558151a Merge branch 'change_class' b7ae93b added from ruby 3467b0a changed the class name 17f4acf first commit
Qua thông tin thu được, ta hình dung được toàn bộ quá trình phát triển của nhánh hiện tại (và của dự án). Nếu trong mỗi lần xác nhận, thông điệp ta muốn Git lưu lại càng chi tiết thì ta dễ dàng nhớ lại, hình dùng tốt về các thay đổi đã thực hiện cũng như ảnh hưởng của nó lên nhánh hiện tại..
Ngoài ra, tùy chọn --graph
rất thú vị, khi nó vẽ ra
một cây phản ánh cấu trúc nhánh của dự án: Kết quả của tùy chọn này
giải thích tại sao ta dùng từ 'nhánh' để mô tả dự án
$ git log --oneline --graph * 8d585ea Merge branch 'fix_readme' |\ | * 3ac015d fixed readme title * | 3cbb6aa fixed readme title differently |/ * 558151a Merge branch 'change_class' |\ | * 3467b0a changed the class name * | b7ae93b added from ruby |/ * 17f4acf first commit
Minh họa có vẻ đơn sơ trên thực tế giúp bạn hình dung rõ hơn về
dự án, lý do tạo nhánh và tích hợp các nhánh, và cũng giúp ích cho việc
quản lý các nhánh. Sau đây, ta sẽ thử tạo nhánh mới, rẽ vào đó để thực
hiện vài thay đổi rồi qua lại nhánh chính. Ta sẽ dùng lệnh log
để hình dung chung về tất cả các nhánh.
Trước hết, ta tạo và rẽ vào nhánh 'erlang' để có phiên bản của chương trình HelloWorld trong ngôn ngữ Erlang. Rõ ràng, ta không nên tạo ngay một phiên bản Erlang trong nhánh chính, nếu không thì sau một hồi mọi thứ sẽ trở nên lộn xộn, thậm chí là không làm việc được.
$ git checkout -b erlang Switched to a new branch 'erlang' $ vim erlang_hw.erl $ git add erlang_hw.erl $ git commit -m 'added erlang' [erlang ab5ab4c] added erlang 1 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 erlang_hw.erl
Trong nhánh 'erlang', ta cũng sẽ tạo ra một bản HelloWorl cũng bằng ngôn ngữ hàm Haskell.
$ vim haskell.hs $ git add haskell.hs $ git commit -m 'added haskell' [erlang 1834130] added haskell 1 files changed, 4 insertions(+), 0 deletions(-) create mode 100644 haskell.hs
Các thay đổi được xác nhận. Bây giờ, ta quyết định quay ngược lại phiên bản Ruby để thay đổi tên lớp. Phiên bản Ruby nằm trong nhánh chính, vì thế ta cần rẽ vào nhánh đó trước.
$ git checkout master Switched to branch 'master' $ ls README ruby.rb $ vim ruby.rb $ git commit -am 'reverted to old class name' [master 594f90b] reverted to old class name 1 files changed, 2 insertions(+), 2 deletions(-)
Gỉa sử rằng mọi việc đã xong, bạn quay lại với các công việc khác
hơn là dự án HelloWorld buồn tẻ. Rồi một ngày khác, quay lại dự án này
ở nhánh 'master', bạn muốn biết những gì đã diễn ra trên nhánh 'erlang'
và vị trí trên nhánh chính 'master' mà bạn bắt đầu rẽ vào 'erlang'.
Nếu chỉ rẽ vào nhánh rồi xem nội dung các tập tin trong nhánh
đó, hiển nhiên là bạn sẽ không hình dung tại sao lại có phiên bản Haskell
trong nhánh 'erlang'. Bạn cần phải dùng git log
để tìm
hiểu các lý do; ngoài ra, việc chỉ định tên nhánh sẽ cho biết thông tin
về điểm rẽ nhánh.
$ git log --oneline erlang
1834130 added haskell
ab5ab4c added erlang
8d585ea Merge branch 'fix_readme'
3cbb6aa fixed readme title differently
3ac015d fixed readme title
558151a Merge branch 'change_class'
b7ae93b added from ruby
3467b0a changed the class name
17f4acf first commit
Từ kết quả trên, dễ thấy là phiên bản Haskell chỉ bắt đầu xuất hiện trong nhánh
'erlang' (được tô màu ở trên). Nhưng git log
còn ghi ra
nhiều thông tin ở trước thời điểm nhánh 'erlang' được tạo ra.
Quá nhiều! Điều tuyệt vời là Git đã tính tới phàn nàn này: bạn có thể
chỉ định Git đưa các thông tin chỉ có ở nhánh này mà không xuất hiện
ở nhánh khác. Điều này rất có ích, chẳng hạn khi bạn muốn tích hợp nhánh
'erlang' vào nhánh 'master', nhưng trước khi tích hợp, bạn cần biết
những gì sẽ diễn ra khi tích hợp.
Để không liệt kê các thông tin không cần thiết của một nhánh, bạn
dùng dấu mũ ^
trước tên nhánh. Ví dụ, nếu chỉ muốn xem
các thay đổi trong nhánh 'erlang', bỏ đi các thay đổi trong nhánh
'master', chỉ việc dùng erlang ^master
:
$ git log --oneline erlang ^master 1834130 added haskell ab5ab4c added erlang $ git log --oneline master ^erlang 594f90b reverted to old class name
Sử dụng dấu mũ là một cách đơn giản, hiểu quả khi quản lý các nhánh, cho phép bạn thấy được những cái chỉ có ở nhánh này mà không có ở nhánh khác. Nhờ đó, bạn kiểm soát các thiếu sót hoặc hình dung được những thay đổi khi tích hợp các nhánh.
Tóm tắt: bạn dùng git log
để liệt kê thông tin về
các lần xác nhận sự thay đổi mã nguồn hoặc các lý do của việc rẽ nhánh,
nhờ đó giúp bạn hình dung quá trình phát triển và trạng thái hiện tại
của dự án.
Có một số thời điểm quan trọng trong quá trình phát triển dự án mà
bạn muốn ghi nhớ một cách dễ dàng và dài lâu. Chúng được đánh dấu
bằng lệnh git tag
: lệnh này đánh dấu ở một điểm trên nhánh
để bạn có thể dễ dàng so sánh với các xác nhận khác trong tương lai.
Thường thì bạn sẽ dùng 'tag' (thẻ) khi đưa ra một phiên bản của dự án.
Ví dụ, ta muốn công bố phiên bản "1.0" của chương trình HelloWorld.
Ta có đánh dấu sự thay đổi cuối cùng (HEAD
) là "v1.0" bằng
lệnh git tag -a v1.0
. Tùy chọn -a
cho phép
bạn thêm chú thích cho thẻ "v1.0". Mặc dù việc không dùng tùy chọn đó
cũng có tác dụng đánh dấu, nhưng kết quả là thông tin về thời điểm đánh
dấu, tác giả, các ghi chú bổ sung sẽ không được lưu lại. Vì vậy,
bạn luôn nên dùng tùy chọn -a
.
$ git tag -a v1.0
Sau lệnh git tag -a
như trên, Git sẽ mở ra trình sọan thảo
để bạn bắt đầu ghi thông tin chi tiết về thẻ.
Sau đó, sử dụng lệnh git log --decorate
, ngoài các thông
tin về những lần xác nhận, bạn còn thấy thêm thông tin về các thẻ.
$ git log --oneline --decorate --graph
* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
* 8d585ea Merge branch 'fix_readme'
|\
| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
* 558151a Merge branch 'change_class'
|\
| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit
Nếu sau khi đánh dấu, bạn xác nhận thêm các thay đổi, thẻ vẫn giữ nguyên vị trí ở lần nó tạo ra lần đầu tiên. Như vậy, thẻ được đánh dấu sẽ cố định và có thể dùng để so sánh với các trạng thái mới của dự án trong tương lai.
Việc đánh dấu cho các thay đổi hiển nhiên không phải là điều bắt buộc.
Có thể xảy ra việc bạn đã công bố một phiên bản của dự án mà không (quên)
đánh dấu. Khi đó, nếu muốn bổ sung đánh dấu, bạn cũng dùng lệnh git tag
,
nhưng đi kèm với chỉ số của xác nhận. Ví dụ, ta sẽ đánh dấu cho xác
nhận mang chỉ số 558151a
như sau đây:
$ git tag -a v0.9 558151a
$ git log --oneline --decorate --graph
* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
* 8d585ea Merge branch 'fix_readme'
|\
| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
* 558151a (tag: v0.9) Merge branch 'change_class'
|\
| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit
Các nhãn trỏ đến các đối tượng đã được theo dõi bởi phân nhánh sẽ được
tự động tải về khi bạn fetch
từ kho lưu trữ đàu xa. Tuy nhiên,
các nhãn không truy cập được bởi phân nhánh sẽ được bỏ qua. Bạn dùng tùy
chọn --tags
để chắc rằng tất cả các nhãn được kèm
theo khi tải về.
$ git fetch origin --tags remote: Counting objects: 1832, done. remote: Compressing objects: 100% (726/726), done. remote: Total 1519 (delta 1000), reused 1202 (delta 764) Receiving objects: 100% (1519/1519), 1.30 MiB | 1.21 MiB/s, done. Resolving deltas: 100% (1000/1000), completed with 182 local objects. From git://github.com:example-user/example-repo * [new tag] v1.0 -> v1.0 * [new tag] v1.1 -> v1.1
Nếu bạn muốn dùng một nhãn đơn, dùng lệnh git fetch <remote>
tag <tag-name>
.
Mặc định, các nhãn không được kèm theo khi push
đến kho lưu
trữ đầu xa. Nếu bạn muốn kèm theo, bạn phải thêm vào tùy chọn
--tags
vào lệnh git push
.
Tóm tắt: bạn dùng lệnh git tag
để đánh dấu một xác nhận
hoặc điểm quan trọng trong kho lưu trữ. Việc đánh nhãn giúp bạn dễ nhớ các
xác nhận hơn mã SHA.