1. Lý thuyết.
Hủy commit cuối bằng lệnh git reset.
Lệnh git reset
được sử dụng để đặt lại trạng thái của repository git về một commit cụ thể. Có hai tham số chính thường được sử dụng với lệnh này là --soft
và --hard
.
Git reset với tham số –soft.
git reset --soft HEAD~1
: Lệnh này sẽ đặt lại con trỏ HEAD về commit cha của commit hiện tại (commit cuối cùng). Tuy nhiên, nó không thay đổi vùng staging hoặc thư mục làm việc. Điều này có nghĩa là tất cả các thay đổi bạn đã commit sẽ được chuyển về vùng staging, cho phép bạn chỉnh sửa hoặc commit lại chúng.
Git reset với tham số –hard.
git reset --hard HEAD~1
: Lệnh này cũng đặt lại con trỏ HEAD về commit cha, nhưng nó cũng sẽ thay đổi vùng staging và thư mục làm việc để phù hợp với commit đó. Điều này có nghĩa là tất cả các thay đổi bạn đã commit sẽ bị xóa hoàn toàn. Bạn nên cẩn thận khi sử dụng lệnh này, vì nó sẽ mất tất cả các thay đổi chưa được commit.
Lưu ý rằng cả hai lệnh trên chỉ nên được sử dụng trên commit chưa được push lên remote repository. Nếu bạn đã push commit và sau đó sử dụng git reset
để hủy bỏ nó, bạn sẽ gặp rắc rối khi cố gắng push lại.
Một vài trường hợp dùng git reset khác.
Hủy git add.
git reset
(không có tham số): Khi bạn thực hiện lệnh git add
, các thay đổi của bạn sẽ được chuyển từ thư mục làm việc sang vùng staging, chuẩn bị cho việc commit. Nếu bạn muốn hủy việc thêm các thay đổi này vào vùng staging, bạn có thể sử dụng lệnh git reset
mà không cần tham số. Lệnh này sẽ đặt lại vùng staging về trạng thái của commit cuối cùng, loại bỏ tất cả các thay đổi đã được thêm vào từ lệnh git add
.
Hủy đưa một file vào staging.
git reset -- filename
: Nếu bạn chỉ muốn hủy việc thêm một file cụ thể vào vùng staging, thay vì tất cả các thay đổi, bạn có thể sử dụng lệnh git reset
kèm theo tên file. Lệnh này sẽ chỉ loại bỏ file cụ thể đó khỏi vùng staging, giữ lại tất cả các thay đổi khác.
Ví dụ:
git reset -- myfile.txt
Lệnh trên sẽ hủy việc thêm myfile.txt
vào vùng staging.
2. Thực hành.
Xóa commit cuối bằng git reset với tham số --soft
.
Quay lại ví dụ trước khi dùng lệnh git status
và git log --oneline
chúng ta thấy các dữ liệu đã được commit thành công và hiện tại đang có 3 commit như dưới.
shell> git status
On branch main
nothing to commit, working tree clean
shell> git log --oneline
ccbb0ef (HEAD -> main) Sua doi index
a914557 C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao
Để xóa đi commit cuối cùng chúng ta sử dụng git reset
với tham số --soft
để chuyển dữ liệu về vùng staging.
git reset --soft HEAD~1
Bây giờ nếu sử dụng lệnh git log --oneline
bạn chỉ thấy còn lại 3 commit và con trỏ HEAD bây giờ trỏ về commit C3 có mã hash là a914557.
shell> git log --oneline
a914557 (HEAD -> main) C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao
Nếu sử dụng lệnh git status
bạn sẽ thấy có file index.html
đang ở trong vùng staged
.
shell> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
Nếu chúng ta xem nội dung file index.html
thì chúng ta vẫn thấy nội dung của commit cuối cùng.
shell> cat index.html
Trang chu
Chao mung ban den voi Website của toi
Cap nhat index
Sau khi xóa commit cuối thì chúng ta vẫn có thể tạo một commit khác như bình thường.
Ví dụ mình tạo commit “C4 – Sua file index” và mình đã có 4 commit như dưới.
shell> git commit -m"C4 - Sua file index"
[main 3ea66b4] C4 - Sua file index
1 file changed, 2 insertions(+)
shell> git status
On branch main
nothing to commit, working tree clean
shell> git log --oneline
3ea66b4 (HEAD -> main) C4 - Sua file index
a914557 C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao
Xóa commit cuối bằng git reset với tham số --hard
.
Lệnh git reset --hard HEAD~1
được sử dụng để đặt lại trạng thái của repository git về commit trước đó (commit cha của commit hiện tại). Tham số --hard
nghĩa là nó sẽ thay đổi vùng staging và thư mục làm việc để phù hợp với commit đó, tức là tất cả các thay đổi bạn đã commit sau commit đó sẽ bị xóa hoàn toàn.
Khi bạn thực hiện lệnh này, git sẽ thông báo về trạng thái mới của HEAD. Trong trường hợp của mình ở dưới, “HEAD is now at a914557 C3 – Update, .gitignore” nghĩa là con trỏ HEAD hiện tại đang trỏ đến commit có mã hash là a914557
, với mô tả commit là “C3 – Update, .gitignore”. Điều này có nghĩa là commit cuối cùng của bạn đã bị hủy và repository đã quay trở lại trạng thái tại thời điểm commit a914557
.
shell> git reset --hard HEAD~1
HEAD is now at a914557 C3 - Update, .gitignore
Nếu bạn xem file index.html thì nội dung của nó đã quay về nội dung của commit thứ 3.
shell> cat index.html
Trang chu
Chao mung ban den voi Website của toi
Do sử dụng tùy chọn --hard
, commit sẽ bị xóa hoàn toàn và nếu bạn sử dụng git status
bạn sẽ không thấy dữ liệu nào nằm trong vùng statged.
shell> git status
On branch main
nothing to commit, working tree clean
Sử dụng lệnh git log --oneline
bây giờ bạn chỉ thấy 3 commit.
shell> git log --oneline
a914557 (HEAD -> main) C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao
Xóa một file ở trong vùng staged.
Giả sử tôi thêm nội dung file index.html với nội dung như dưới.
cat > index.html << 'OEF'
Trang chu
Chao mung ban den voi Website của toi
Chao mung ban den voi Website của toi
Chao mung ban den voi Website của toi
OEF
Khi bạn sử dụng lệnh git status bạn sẽ thấy file index.html này đang chưa nằm vào vùng staged.
shell> git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Sử dụng lệnh git add để thêm file này vào vùng staged và kiểm tra lại bằng lệnh git status bạn sẽ thấy file index.html đã nằm trong vùng staged và đã sẵn sàng để commit.
shell> git add .
shell> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
Tuy nhiên ở ví dụ này chúng ta sẽ dùng lệnh git reset để xóa file index.html trong vùng staged bằng ví dụ như dưới.
shell> git reset -- index.html
Unstaged changes after reset:
M index.html
Và nếu bây giờ bạn sử dụng lệnh git status bạn sẽ thấy file index.html đã bị xóa khỏi staged.
shell> git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Phục hồi nội dung từ vùng staged khi thêm một file vào vùng staging (với git add
), sau đó tiếp tục chỉnh sửa file đó mà không thêm các thay đổi mới vào vùng staging.
Giả sử tôi sẽ thêm 1 dòng Chao mung ban den voi Website của toi
vào file index.html
và tôi sẽ có 4 dòng với nội dung này.
shell> echo -e '\nChao mung ban den voi Website của toi' >> index.html
shell> cat index.html
Trang chu
Chao mung ban den voi Website của toi
Chao mung ban den voi Website của toi
Chao mung ban den voi Website của toi
Chao mung ban den voi Website của toi
Khi bạn chạy lệnh git status
, git sẽ cung cấp cho bạn thông tin về trạng thái hiện tại của repository. Trong trường hợp của này, có hai phần chính trong output:
Changes to be committed:
: Phần này liệt kê các thay đổi đã được thêm vào vùng staging và sẽ được commit khi bạn chạy lệnhgit commit
. Trong trường hợp của bạn, fileindex.html
đã được sửa đổi và thay đổi đó đã được thêm vào vùng staging.Changes not staged for commit:
: Phần này liệt kê các thay đổi trong thư mục làm việc mà chưa được thêm vào vùng staging. Điều này có nghĩa là, nếu bạn chạy lệnhgit commit
ngay bây giờ, những thay đổi này sẽ không được commit. Trong trường hợp của bạn, fileindex.html
cũng đã được sửa đổi nhưng thay đổi đó chưa được thêm vào vùng staging.
Điều này có thể xảy ra nếu bạn đã thêm một file vào vùng staging (với git add
), sau đó tiếp tục chỉnh sửa file đó mà không thêm các thay đổi mới vào vùng staging.
shell> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
Mình sử dụng lệnh git restore với tham số — và chỉ định tên file cần phục hồi nội dung hoặc dấu chấm (.) nếu phục hồi tất cả. Sau khi restore xong bạn sẽ thấy file index.html đã nằm trong vùng staged và sẵn sàng để commit.
shell> git restore -- .
shell> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
Trước khi commit mình sẽ kiểm tra số lượng commit và sau đó tiến hành commit.
shell> git log --oneline
a914557 (HEAD -> main) C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao
shell> git commit -m"C4 Update index"
[main 0237c94] C4 Update index
1 file changed, 2 insertions(+), 1 deletion(-)
Commit xong mình sử dụng git status
và nhận thông báo working tree clean
cho biết commit đã thành công.
shell> git status
On branch main
nothing to commit, working tree clean
Sử dụng git log --oneline
bạn nhận được 4 commit dưới dưới.
shell> git log --oneline
0237c94 (HEAD -> main) C4 Update index
a914557 C3 - Update, .gitignore
17a15de C2 - Sua doi index
248eedf C1 - Khoi tao