Tham Khảo Git

 

book Phân Nhánh và Tích Hợp

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.

docs   book git branch liệt kê, tạo, quản lý các nhánh


docs   book git checkout rẽ vào một nhánh

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ỳ.

git branch liệt kê các nhánh

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.

git branch (tên_nhánh) tạo một nhánh mới

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

git branch -v xem xác nhận cuối của mỗi nhánh

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

git checkout -b (tên_nhánh) tạo một nhánh và rẽ ngay vào nhánh đó

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.

git branch -d (tên_nhánh) xóa một nhánh

Để 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

git push (tên-kho-đầu-xa) :(tên-nhánh) xóa một nhánh kho đầu xa

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.

docs   book git merge tích hợp một nhánh vào nhánh đang làm việc

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

các phương thức tích hợp phức tạp hơn

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

tích hợp các xung đột

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.

docs   book git log xem ghi chú về các lần xác nhận

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.

docs   book git tag đánh dấu thời điểm quan trọng trong quá trình phát triể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.

Chia Sẻ và Cập Nhật Dự Án »