Git Pro: Những thao tác cơ bản khi làm việc với Git (P2) – Commit History & Undo
Chúng ta đã tìm hiểu những lệnh cơ bản khi sử dụng Git ở bài trước . Trong bài này chúng ta sẽ tiếp tục tìm hiểu kỹ hơn với các thao tác: xem lại lịch sử commit và hoàn tác những gì bạn đã làm.
2.3 Xem Lịch sử Commit
Xem lịch sử commit
Sau khi bạn đã tạo một số commit hoặc nếu bạn đã clone một kho lưu trữ với lịch sử commit hiện có, nếu bạn sẽ muốn nhìn lại xem những việc gì đã diễn ra. Công cụ cơ bản và mạnh mẽ nhất để làm điều này là git log
.
Ví dụ này sử dụng một dự án rất đơn giản có tên là “simplegit”. Để có được dự án, thực hiện lệnh:
$ git clone https://github.com/schacon/simplegit-progit
Khi bạn chạy git log
bạn sẽ nhận được output như sau:
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 10:31:28 2008 -0700
Initial commit
Theo mặc định, git log
liệt kê các commit được thực hiện trong repo đó theo thứ tự thời gian đảo ngược; nghĩa là, các commit gần đây nhất xuất hiện đầu tiên. Lệnh này liệt kê từng commit với SHA-1, tên và email của tác giả, ngày commit và thông điệp commit.
Có nhiều tùy chọn có sẵn với git log
để hiển thị những gì bạn đang tìm kiếm. Chúng ta cùng đi đến một số tuỳ chọn phổ biến nhất:
Đầu tiên là -p
hoặc --patch
, cho thấy sự khác biệt (output patch ) được đặt trong trong mỗi commit. Bạn có thể giới hạn số lượng mục được hiển thị, chẳng hạn như sử dụng -2
để chỉ hiển thị hai mục nhập cuối cùng.
$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = "schacon@gee-mail.com"
s.summary = "A simple gem for using Git in Ruby code."
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
end
end
-
-if $0 == __FILE__
- git = SimpleGit.new
- puts git.show
-end
Tùy chọn này hiển thị sự khác biết sau mỗi mục.
Bạn cũng có thể sử dụng một loạt các tùy chọn tóm tắt với git log
. Ví dụ: nếu muốn xem một số thông tin rút gọn cho mỗi commit, bạn có thể sử dụng tùy chọn --stat
:
$ git log --stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
Rakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
lib/simplegit.rb | 5 -----
1 file changed, 5 deletions(-)
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 10:31:28 2008 -0700
Initial commit
README | 6 ++++++
Rakefile | 23 +++++++++++++++++++++++
lib/simplegit.rb | 25 +++++++++++++++++++++++++
3 files changed, 54 insertions(+)
Như bạn thấy, tùy chọn --stat
in bên dưới mỗi mục commit một danh sách các tệp đã sửa đổi, có bao nhiêu tệp đã được thay đổi và có bao nhiêu dòng trong các tệp đó đã được thêm và xóa. Cuối cùng là tóm tắt thông tin ở cuối.
Tuỳ chọn --pretty
. Tùy chọn này thay đổi đầu ra log thành các định dạng khác. Tùy chọn oneline in mỗi commit trên một dòng, nó rất hữu ích khi bạn xem nhiều commit. Ngoài ra, các tùy chọn short
, full
và fuller
output hiển thị cùng định dạng nhưng có ít hoặc nhiều thông tin hơn:
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 Change version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Remove unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 Initial commit
Một tuỳ chọn hay ho hơn mà bạn có thể chọn là format
, cho phép bạn chỉ định định dạng đầu ra theo ý của bạn. Như sau:
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : Change version number
085bb3b - Scott Chacon, 6 years ago : Remove unnecessary test
a11bef0 - Scott Chacon, 6 years ago : Initial commit
Các tùy chọn hữu ích cho git log --pretty=format
Option | Description of Output |
---|---|
| Commit hash |
| Abbreviated commit hash |
| Tree hash |
| Abbreviated tree hash |
| Parent hashes |
| Abbreviated parent hashes |
| Author name |
| Author email |
| Author date (format respects the –date=option) |
| Author date, relative |
| Committer name |
| Committer email |
| Committer date |
| Committer date, relative |
| Subject |
oneline và format rất có ích với tùy chọn log
khác như --graph
. Tùy chọn này thêm một biểu đồ ASCII nhỏ hiển thị lịch sử nhánh của bạn:
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 Ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Add method for getting the current branch
* | 30e367c Timeout code and tests
* | 5a09431 Add timeout protection to grit
* | e1193f8 Support for heads with slashes in them
|/
* d6016bc Require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
Đó chỉ là một số tùy chọn định dạng đầu ra đơn giản cho git log
. Các tùy chọn phổ biến cho git log
được liệt kê dưới đây:
Option | Description |
---|---|
| Show the patch introduced with each commit. |
| Show statistics for files modified in each commit. |
| Display only the changed/insertions/deletions line from the –stat command. |
| Show the list of files modified after the commit information. |
| Show the list of files affected with added/modified/deleted information as well. |
| Show only the first few characters of the SHA-1 checksum instead of all 40. |
| Display the date in a relative format (for example, “2 weeks ago”) instead of using the full date format. |
| Display an ASCII graph of the branch and merge history beside the log output. |
| Show commits in an alternate format. Options include oneline, short, full, fuller, and format (where you specify your own format). |
| Shorthand for |
Giới hạn của output log
Ngoài các tùy chọn format output, git log
có một số tùy chọn khác cho phép bạn chỉ hiển thị số lượng nhỏ commit. Ví dụ như -2
, chỉ hiển thị hai lần commit cuối cùng. Bạn có thể áp dụng tương tự với -<n>
, n
là bất kỳ số nguyên nào để hiển thị n
lần commit cuối cùng. Bạn không nên sử dụng lệnh này thường xuyên, vì Git mặc định tất cả các output đều thông qua một pager để bạn chỉ nhìn thấy một trang log output.
Tuy nhiên, vẫn có các tuỳ chọn khác như --since
và --until
. Ví dụ: lệnh này đưa ra danh sách các commit được thực hiện trong hai tuần qua:
$ git log --since=2.weeks
Lệnh trên hoạt động với nhiều định dạng – bạn có thể chỉ định một ngày cụ thể như "2008-01-15"
hoặc tương đối như "2 years 1 day 3 minutes ago"
.
Bạn cũng có thể lọc để commit phù hợp với một số tiêu chí tìm kiếm, như –author cho phép bạn lọc theo một author cụ thể và tùy chọn --grep
cho phép bạn tìm kiếm từ khóa trong thông điệp commit.
Tùy chọn -S
(được gọi chung là tùy chọn Pickitaxe của Git). Ví dụ: nếu bạn muốn tìm commit cuối cùng đã thêm hoặc xóa tham chiếu đến một chức năng cụ thể, bạn có thể gọi:
$ git log -S function_name
Tùy chọn cuối cùng là một đường dẫn. Nếu bạn chỉ định một thư mục hoặc tên tệp, bạn có thể giới hạn đầu ra log commit. Đây luôn là tùy chọn cuối cùng và thường được bắt đầu bằng dấu gạch ngang kép ( --
) để tách các đường dẫn khỏi các tùy chọn.
Một vài tùy chọn phổ biến khác để bạn tham khảo.
Option | Description |
---|---|
| Show only the last n commits |
| Limit the commits to those made after the specified date. |
| Limit the commits to those made before the specified date. |
| Only show commits in which the author entry matches the specified string. |
| Only show commits in which the committer entry matches the specified string. |
| Only show commits with a commit message containing the string |
| Only show commits adding or removing code matching the string |
Ví dụ: nếu bạn muốn xem trong lịch sử commit Git, commit nào được tạo bởi “Junio C Hamano
” trong thời gian từ “2008-10-01
” đến “2008-11-01
” sửa đổi các tệp test. Bạn có thể chạy một lệnh như:
$ git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attributes are in use
acd3b9e - Enhance hold_lock_file_for_{update,append}() API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
51a94af - Fix "checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch
Trong số gần 40.000 commit trong lịch sử, lệnh này hiển thị 6 kết quả phù hợp với các tiêu chí đó.
2.4 Hoàn tác
Undoing
Ở bất kỳ stage nào, bạn có thể muốn hoàn tác( undo) một cái gì đó. Trong mục này, chúng tôi sẽ giới thiệu một vài công cụ cơ bản để hoàn tác các thay đổi mà bạn đã thực hiện. Nhưng hãy chú ý nhé, bởi vì không phải tất cả mọi thứ đều có thể hoàn tác.
Một trong những lần undo phổ biến diễn ra khi bạn commit quá sớm và khi đó bạn quên add một số tệp hoặc thông điệp commit của bạn bị sai. Nếu bạn muốn làm lại commit đó, hãy thực hiện các thay đổi mà bạn đã quên, thực hiện chúng và commit lại bằng tùy chọn --amend
:
$ git commit --amend
Lệnh này lấy stage và dùng cho commit. Nếu bạn không thực hiện thay đổi nào kể từ lần commit cuối cùng (ví dụ: bạn chạy lệnh này ngay sau lần commit trước đó), thì snapshot của bạn sẽ giống hệt nhau, và tất cả những gì bạn thay đổi chỉ là thông điệp của commit.
Với cùng một commit trình chỉnh sửa thông điệp được kích hoạt, nó vẫn chứa thông điệp của commit trước đó của bạn. Bạn có thể chỉnh sửa thông điệp giống như mọi khi, nhưng nó sẽ ghi đè lên commit trước đó của bạn.
Ví dụ: nếu bạn commit và sau đó nhớ ra rằng bạn đã quên thực hiện các thay đổi trong một vài tệp bạn muốn thêm vào commit này, bạn có thể thực hiện như sau:
$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend
Nó thể hiện rằng, bạn kết thúc với một commit – commit thứ hai thay thế kết quả của lần commit đầu tiên.
Unstaging một tập tin Staged
Có một điều thú vị là khi bạn sử dụng lệnh lệnh git status
output của lệnh cũng nhắc bạn cách hoàn tác các thay đổi. Ví dụ: giả sử bạn đã thay đổi hai tệp và muốn commit chúng là hai thay đổi riêng biệt, nhưng bạn lại vô tình nhập git add *
. Làm thế nào bạn có thể bỏ qua một trong hai thay đổi trên? Lệnh git status
có nhắc bạn:
$ git add *
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
modified: CONTRIBUTING.md
Ngay bên dưới dòng “Changes to be committed” nói rằng hãy sử dụng git reset HEAD <file>...
để unstage. Vì vậy, hãy sử dụng lời khuyên đó để bỏ qua tệp CONTRIBUTING.md
:
$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
Tệp CONTRIBUTING.md
được sửa đổi nhưng unstaged.
Unmodifying một tập tin Modified
Nếu bạn không muốn giữ lại các thay đổi đối với tệp CONTRIBUTING.md
? Vậy bạn phải làm thế nào? – làm sao để đưa file của bạn chở lại như trước khi bạn commit (hoặc như khi bạn vừa thực hiện clone)? Rất may mắn git status
cũng cho biết ta cần làm gì trong trường hợp này. Trong output ở ví dụ trên, unstaged được liệt kê như sau:
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
Output trên cho bạn biết khá rõ cách để loại bỏ những thay đổi mà bạn đã thực hiện. Chúng ta làm theo đề nghị:
$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
Bạn có thể thấy rằng những thay đổi đã được hoàn nguyên.
Các bạn hãy nhớ rằng, bất kỳ thứ gì được commit trong Git hầu như luôn có thể được phục hồi. Cả các commit trên các nhánh đã bị xóa hoặc các commit được ghi đè bằng một commit --amend
cũng vậy. Tuy nhiên, bất cứ thứ gì bạn không commit thì sẽ không bao giờ tìm lại được.