Làm cách nào để hoàn tác các cam kết cục bộ gần đây nhất trong Git?


21062

Tôi đã vô tình cam kết các tệp sai cho Git , nhưng tôi chưa đẩy cam kết đến máy chủ.

Làm thế nào tôi có thể hoàn tác những cam kết đó từ kho lưu trữ cục bộ?


138
Trước khi bạn đăng câu trả lời mới, hãy xem xét đã có hơn 65 câu trả lời cho câu hỏi này. Hãy chắc chắn rằng câu trả lời của bạn đóng góp những gì không nằm trong số các câu trả lời hiện có.
Sazzad Hissain Khan

89
Bạn biết những gì git cần? git undo, đó là nó. Sau đó, git danh tiếng đã xử lý những sai lầm do chúng ta làm cho những người phàm trần biến mất. Thực hiện bằng cách đẩy trạng thái hiện tại trên ngăn xếp git trước khi thực hiện bất kỳ gitlệnh nào . Nó sẽ ảnh hưởng đến hiệu suất, vì vậy tốt nhất là thêm cờ cấu hình xem có bật hay không.
Yimin Rong

11
@YiminRong Điều đó có thể được thực hiện với aliastính năng của Git : git-scm.com/book/en/v2/Git-Basics-Git-Aliases
Edric

3
@RomainValeri - Cách tương tự hoàn tác hoạt động ở mọi nơi khác.
Yimin Rong

1
@YiminRong Không mua nó. Mọi người vẫn sẽ dò dẫm và hoàn tác những thứ không thể hoàn tác. Nhưng quan trọng hơn, git reflogđã gần với những gì bạn mô tả, nhưng cung cấp cho người dùng quyền kiểm soát nhiều hơn về những gì sẽ được thực hiện (chưa). Nhưng xin vui lòng, không, "hoàn tác" không hoạt động giống nhau ở mọi nơi và mọi người sẽ mong đợi nhiều điều khác nhau để tính năng này đạt được. Hoàn tác cam kết cuối cùng? Hoàn tác hành động cuối cùng? Nếu hành động cuối cùng là đẩy, hoàn tác chính xác như thế nào, (đặt lại và đẩy) hoặc (hoàn nguyên và đẩy)?
RomainValeri

Câu trả lời:


22860

Hoàn tác một cam kết và làm lại

$ git commit -m "Something terribly misguided"             # (1)
$ git reset HEAD~                                          # (2)
<< edit files as necessary >>                              # (3)
$ git add ...                                              # (4)
$ git commit -c ORIG_HEAD                                  # (5)
  1. Đây là những gì bạn muốn hoàn tác.
  2. Điều này không ảnh hưởng gì đến cây làm việc của bạn (trạng thái tệp của bạn trên đĩa), nhưng hoàn tác cam kết và để lại những thay đổi bạn đã cam kết (vì vậy chúng sẽ xuất hiện dưới dạng "Thay đổi không được tổ chức cho cam kết" git status, vì vậy bạn sẽ cần phải thêm chúng một lần nữa trước khi cam kết). Nếu bạn chỉ muốn thêm nhiều thay đổi cho cam kết trước đó hoặc thay đổi thông báo cam kết 1 , bạn có thể sử dụng git reset --soft HEAD~thay thế, giống như git reset HEAD~2 nhưng để lại các thay đổi hiện tại của bạn.
  3. Thực hiện chỉnh sửa các tập tin cây làm việc.
  4. git add bất cứ điều gì bạn muốn bao gồm trong cam kết mới của bạn.
  5. Cam kết thay đổi, sử dụng lại thông điệp cam kết cũ. resetsao chép cái đầu cũ sang .git/ORIG_HEAD; commitvới -c ORIG_HEADsẽ mở một trình soạn thảo, ban đầu chứa thông điệp tường trình từ cam kết cũ và cho phép bạn chỉnh sửa nó. Nếu bạn không cần chỉnh sửa tin nhắn, bạn có thể sử dụng -Ctùy chọn.

Tuy nhiên, hãy cẩn thận rằng nếu bạn đã thêm bất kỳ thay đổi mới nào vào chỉ mục, sử dụng commit --amendsẽ thêm chúng vào cam kết trước đó của bạn.

Nếu mã đã được đẩy đến máy chủ của bạn và bạn có quyền ghi đè lên lịch sử (rebase) thì:

git push origin master --force

Bạn cũng có thể nhìn vào câu trả lời này:

Làm thế nào tôi có thể di chuyển trở lại vị trí trước đó? (Đầu tách rời) & Hoàn tác cam kết

Câu trả lời ở trên sẽ cho bạn thấy git reflog,cái nào được sử dụng để tìm hiểu SHA-1 là gì, mà bạn muốn hoàn nguyên. Khi bạn tìm thấy điểm mà bạn muốn hoàn tác để sử dụng chuỗi lệnh như được giải thích ở trên.


Tuy nhiên, 1 Lưu ý rằng bạn không cần thiết lập lại cam kết trước đó nếu bạn vừa mắc lỗi trong thông điệp cam kết của mình . Tùy chọn dễ dàng hơn là git reset(để bỏ qua bất kỳ thay đổi nào bạn đã thực hiện) và sau đó git commit --amend, sẽ mở trình soạn thảo thông báo cam kết mặc định của bạn được điền trước bằng thông báo cam kết cuối cùng.

2 HEAD~ giống như HEAD~1. Ngoài ra, xem các tiêu đề trong git là gì? . Nó hữu ích nếu bạn muốn không phổ biến nhiều cam kết.


472
Và nếu cam kết sai chi nhánh, bạn có thể git checkout theRightBranchvới tất cả các giai đoạn thay đổi. Như tôi vừa phải làm.
Frank Shearar

490
Nếu bạn đang làm việc trong DOS, thay vì git reset --soft HEAD^bạn sẽ cần sử dụng git reset --soft HEAD~1. ^ Là một ký tự tiếp tục trong DOS nên nó sẽ không hoạt động chính xác. Ngoài ra, --softlà mặc định, vì vậy bạn có thể bỏ qua nó nếu bạn muốn và chỉ cần nói git reset HEAD~1.
Ryan Lundy

119
Người dùng zsh có thể nhận được: zsh: no matches found: HEAD^- bạn cần phải thoát ^ tức làgit reset --soft HEAD\^
tnajdek

7
Câu trả lời là không chính xác nếu, nói một cách tình cờ, git commit -ađã được ban hành khi -acần phải bỏ đi. Trong trường hợp đó, tốt hơn hết là không nên bỏ qua --soft(điều này sẽ dẫn đến --mixedmặc định) và sau đó bạn có thể khôi phục các thay đổi bạn muốn cam kết.
dmansfield

6
@IcyBrk git add là một lệnh. git add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--chmod=(+|-)x] [--] [<pathspec>…​]
Ashraf.Shk786

10734

Hoàn tác một cam kết là một chút đáng sợ nếu bạn không biết làm thế nào nó hoạt động. Nhưng nó thực sự dễ dàng đáng kinh ngạc nếu bạn hiểu.

Giả sử bạn có cái này, trong đó C là CHÍNH của bạn và (F) là trạng thái của các tệp của bạn.

   (F)
A-B-C
    ↑
  master

Bạn muốn nuke cam kết C và không bao giờ nhìn thấy nó nữa và mất tất cả các thay đổi trong các tệp sửa đổi cục bộ . Bạn làm điều này:

git reset --hard HEAD~1

Kết quả là:

 (F)
A-B
  ↑
master

Bây giờ B là ĐẦU. Vì bạn đã sử dụng --hard, các tệp của bạn được đặt lại về trạng thái của chúng tại cam kết B.

À, nhưng giả sử cam kết C không phải là một thảm họa, nhưng chỉ là một chút thôi. Bạn muốn hoàn tác cam kết nhưng giữ các thay đổi của bạn để chỉnh sửa một chút trước khi bạn thực hiện cam kết tốt hơn. Bắt đầu lại từ đây, với C là ĐẦU của bạn:

   (F)
A-B-C
    ↑
  master

Bạn có thể làm điều này, bỏ qua --hard:

git reset HEAD~1

Trong trường hợp này, kết quả là:

   (F)
A-B-C
  ↑
master

Trong cả hai trường hợp, HEAD chỉ là một con trỏ đến cam kết mới nhất. Khi bạn thực hiện một git reset HEAD~1, bạn nói với Git để di chuyển con trỏ CHÍNH trở lại một lần xác nhận. Nhưng (trừ khi bạn sử dụng --hard) bạn để các tập tin của bạn như cũ. Vì vậy, bây giờ git statuscho thấy những thay đổi bạn đã kiểm tra vào C. Bạn đã không mất một thứ!

Đối với các liên lạc nhẹ nhất, bạn thậm chí có thể hoàn tác cam kết của mình nhưng để lại các tệp và chỉ mục của bạn :

git reset --soft HEAD~1

Điều này không chỉ để các tập tin của bạn một mình, nó thậm chí còn để lại chỉ mục của bạn . Khi bạn làm như vậy git status, bạn sẽ thấy các tệp tương tự nằm trong chỉ mục như trước đây. Thực tế, ngay sau lệnh này, bạn có thể làm git commitvà bạn sẽ làm lại cùng một cam kết mà bạn vừa có.

Một điều nữa: Giả sử bạn phá hủy một cam kết như trong ví dụ đầu tiên, nhưng sau đó bạn phát hiện ra bạn cần nó ? May mắn, phải không?

Không, vẫn còn một cách để lấy lại. Gõ git reflogvà bạn sẽ thấy một danh sách các (một phần) cam kết Shas (có nghĩa là, băm) mà bạn đã di chuyển xung quanh trong Tìm cam kết bạn bị phá hủy, và làm điều này.:

git checkout -b someNewBranchName shaYouDestroyed

Bây giờ bạn đã hồi sinh cam kết đó. Các cam kết không thực sự bị phá hủy trong Git trong khoảng 90 ngày, vì vậy bạn thường có thể quay lại và giải cứu một người mà bạn không có ý định loại bỏ.


15
HÃY THỬ! Điều này có thể không làm những gì bạn mong đợi nếu cam kết sai lầm của bạn là hợp nhất (chuyển tiếp nhanh)! Nếu đầu của bạn nằm trên một cam kết hợp nhất (ví dụ: tính năng nhánh được hợp nhất thành chủ), git reset --hard~1sẽ trỏ nhánh chính đến cam kết cuối cùng bên trong nhánh tính năng. Trong trường hợp này, ID cam kết cụ thể nên được sử dụng thay cho lệnh tương đối.
Chris Kerekes

90
Thiếu một điểm quan trọng: Nếu cam kết đã nói trước đó bị 'đẩy' ra điều khiển từ xa, bất kỳ thao tác 'hoàn tác' nào, dù đơn giản đến đâu, sẽ gây ra nỗi đau và đau khổ lớn cho những người dùng còn lại có cam kết này trong bản sao địa phương của họ, khi họ thực hiện một 'git pull' trong tương lai. Vì vậy, nếu cam kết đã được 'đẩy', thay vào đó, hãy thực hiện điều này: git hoàn nguyên <bad-commit-sha1-id> git đẩy gốc:
FractalSpace

12
@FractalSpace, nó sẽ không gây ra "nỗi đau và đau khổ to lớn." Tôi đã thực hiện một vài cú đẩy mạnh khi sử dụng Git với một đội. Tất cả cần có là giao tiếp.
Ryan Lundy

14
@Kyralessa Ở nơi làm việc của tôi, làm rối tung toàn bộ quy trình làm việc của nhóm và sau đó nói với họ cách khắc phục sh * t không được gọi là 'giao tiếp'. lịch sử git viết lại là một hoạt động phá hủy dẫn đến việc bỏ đi các phần của repo. Nhấn mạnh vào việc sử dụng nó, trong khi các lựa chọn thay thế rõ ràng và an toàn có sẵn chỉ đơn giản là vô trách nhiệm.
FractalSpace

14
Tôi muốn nuke một cam kết và không bao giờ nhìn thấy nó một lần nữa. Tôi đã sử dụng ví dụ của bạn với --hardnhưng điều tôi không nhận ra là tất cả những thay đổi không được đề cập trong cây làm việc của tôi cũng bị thu hẹp! Tôi sẽ cam kết những tập tin này như là một phần của một cam kết sau này. Bây giờ dường như không thể lấy lại các tệp này - tôi thậm chí đã thử giải pháp mà bạn đã đăng reflognhưng điều này không khôi phục các thay đổi chưa được thực hiện trước đó.
Adam Burley

2129

Có hai cách để "hoàn tác" cam kết cuối cùng của bạn, tùy thuộc vào việc bạn có thực hiện cam kết của mình hay không (được đẩy vào kho lưu trữ từ xa):

Làm thế nào để hoàn tác một cam kết cục bộ

Giả sử tôi đã cam kết tại địa phương, nhưng bây giờ tôi muốn xóa cam kết đó.

git log
    commit 101: bad commit    # Latest commit. This would be called 'HEAD'.
    commit 100: good commit   # Second to last commit. This is the one we want.

Để khôi phục mọi thứ trở lại như trước khi cam kết cuối cùng, chúng ta cần phải resetcam kết trước HEAD:

git reset --soft HEAD^     # Use --soft if you want to keep your changes
git reset --hard HEAD^     # Use --hard if you don't care about keeping the changes you made

Bây giờ git logsẽ cho thấy cam kết cuối cùng của chúng tôi đã được gỡ bỏ.

Làm thế nào để hoàn tác một cam kết công khai

Nếu bạn đã thực hiện các cam kết của mình ở chế độ công khai, bạn sẽ muốn tạo một cam kết mới sẽ "hoàn nguyên" các thay đổi bạn đã thực hiện trong cam kết trước đó (CHÍNH hiện tại).

git revert HEAD

Thay đổi của bạn bây giờ sẽ được hoàn nguyên và sẵn sàng để bạn cam kết:

git commit -m 'restoring the file I removed by accident'
git log
    commit 102: restoring the file I removed by accident
    commit 101: removing a file we don't need
    commit 100: adding a file that we need

Để biết thêm thông tin, hãy xem Thông tin cơ bản về Git - Hoàn tác mọi thứ .


101
Tôi tìm thấy câu trả lời này rõ ràng nhất. git revert HEAD^không phải là trước đó, là trước đó của trước đó. Tôi đã làm: git revert HEADvà sau đó đẩy một lần nữa và nó đã hoạt động :)
nacho4d

Nếu Git hỏi bạn "Thêm?" Khi bạn thử các lệnh này, hãy sử dụng cú pháp thay thế cho câu trả lời này: stackoverflow.com/a/14204318/823470
tar

1745

Thêm / xóa tệp để có được mọi thứ theo cách bạn muốn:

git rm classdir
git add sourcedir

Sau đó sửa đổi cam kết:

git commit --amend

Cam kết sai lầm trước đây sẽ được chỉnh sửa để phản ánh trạng thái chỉ mục mới - nói cách khác, nó sẽ giống như bạn không bao giờ mắc lỗi ở vị trí đầu tiên.

Lưu ý rằng bạn chỉ nên làm điều này nếu bạn chưa đẩy. Nếu bạn đã đẩy, thì bạn sẽ phải cam kết sửa chữa bình thường.


2
FYI: Điều này loại bỏ tất cả các tập tin của tôi và tôi đã mất các thay đổi.
egorlitvinenko

CẬP NHẬT: Tuy nhiên, tôi đã khôi phục nó bằng cách sử dụng reflog. Nhưng biên lai đã không làm việc cho các cam kết ban đầu.
egorlitvinenko

1
Sử dụng git rm --cachedđể giữ các tệp trong hệ thống tệp và chỉ xóa chúng khỏi chỉ mục git!
xuiqzy

1016
git rm yourfiles/*.class
git commit -a -m "deleted all class files in folder 'yourfiles'"

hoặc là

git reset --hard HEAD~1

Cảnh báo: Lệnh trên sẽ xóa vĩnh viễn các sửa đổi đối với các .javatệp (và bất kỳ tệp nào khác) mà bạn muốn cam kết.

Các hard resetđể HEAD-1sẽ thiết lập bản sao làm việc của bạn với nhà nước của các cam kết trước khi sai của bạn cam kết.


19
git commit -a -m ""hoặc git commit -am ""tự nhiên! :]
trejder

Một cách sử dụng 'phím tắt' khác của stash; nếu bạn muốn hủy bỏ mọi thứ (hoàn tác thêm), chỉ git stash, sau đógit stash pop
seanriordan08

778

Để thay đổi cam kết cuối cùng

Thay thế các tệp trong chỉ mục:

git rm --cached *.class
git add *.java

Sau đó, nếu đó là một chi nhánh tư nhân, hãy sửa đổi cam kết:

git commit --amend

Hoặc, nếu đó là một chi nhánh được chia sẻ, hãy thực hiện một cam kết mới:

git commit -m 'Replace .class files with .java files'


( Để thay đổi một cam kết trước đó , hãy sử dụng rebase tương tác tuyệt vời .)


ProTip ™: Thêm *.classvào một gitignore để ngăn điều này xảy ra lần nữa.


Hoàn nguyên cam kết

Sửa đổi một cam kết là giải pháp lý tưởng nếu bạn cần thay đổi cam kết cuối cùng, nhưng một giải pháp tổng quát hơn là reset.

Bạn có thể đặt lại Git thành bất kỳ cam kết nào với:

git reset @~N

Trong trường hợp Nlà số cam kết trước HEAD@~resets đến trước cam kết.

Vì vậy, thay vì sửa đổi cam kết, bạn có thể sử dụng:

git reset @~
git add *.java
git commit -m "Add .java files"

Kiểm tra git help reset, cụ thể là các phần trên --soft --mixed--hard, để hiểu rõ hơn về những gì nó làm.

Từ chối

Nếu bạn làm phiền, bạn luôn có thể sử dụng reflog để tìm các xác nhận bị hủy:

$ git reset @~
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~
2c52489 HEAD@{1}: commit: added some .class files
$ git reset 2c52489
... and you're back where you started



2
Đối với những người đọc trong tương lai - xin lưu ý rằng đó git revertlà một lệnh riêng - về cơ bản là 'đặt lại' một dấu phẩy đơn.
BKSpurgeon

682

Sử dụng git revert <commit-id>.

Để có được ID cam kết, chỉ cần sử dụng git log.


15
Điều đó có nghĩa là gì, anh đào chọn cam kết? Trong trường hợp của tôi, tôi đã ở sai chi nhánh khi tôi chỉnh sửa một tập tin. Tôi cam kết nó sau đó nhận ra tôi đã ở sai chi nhánh. Sử dụng "git reset --soft HEAD ~ 1" đã đưa tôi trở lại ngay trước khi cam kết, nhưng bây giờ nếu tôi kiểm tra nhánh chính xác, làm cách nào để hoàn tác các thay đổi đối với tệp trong nhánh sai mà thay vào đó hãy thực hiện chúng (trong cùng tên tập tin) trong chi nhánh chính xác?
nhà thiên văn học

Tôi chỉ sử dụng git revert commit-idlàm việc như một nét duyên dáng. Tất nhiên sau đó bạn sẽ cần phải thúc đẩy những thay đổi của bạn.
Casey Robinson

8
Tôi tin rằng đó sẽ là git cherry-pick <<erroneous-commit-sha>>@astronomerdave. Từ, Ông gần 2 tuổi-muộn-đến-Đảng.
Tom Howard

@Kris: Thay vì cherry-pick sử dụng rebase. Bởi vì nó là hái anh đào tiên tiến
Eugen Konkov

Tôi chỉ sử dụng hoàn nguyên nếu tôi đã thực hiện cam kết của mình. Nếu không, thiết lập lại là một lựa chọn tốt hơn. Đừng quên rằng hoàn nguyên tạo ra một cam kết mới và thường thì đây không phải là mục tiêu.
Hola Soy Edu Feliz Navidad

532

Nếu bạn đang dự định hoàn tác một cam kết cục bộ, bất cứ điều gì bạn thay đổi bạn đã thực hiện trên cam kết đó và nếu bạn không lo lắng bất cứ điều gì về điều đó, chỉ cần thực hiện lệnh sau.

git reset --hard HEAD^1

(Lệnh này sẽ bỏ qua toàn bộ cam kết của bạn và các thay đổi của bạn sẽ bị mất hoàn toàn khỏi cây làm việc cục bộ của bạn). Nếu bạn muốn hoàn tác cam kết của mình, nhưng bạn muốn thay đổi của mình trong khu vực tổ chức (trước khi cam kết giống như sau git add), hãy thực hiện lệnh sau.

git reset --soft HEAD^1

Bây giờ các tập tin cam kết của bạn đi vào khu vực tổ chức. Giả sử nếu bạn muốn tăng cường các tập tin, bởi vì bạn cần chỉnh sửa một số nội dung sai, sau đó thực hiện lệnh sau

git reset HEAD

Bây giờ các tập tin cam kết đến từ khu vực được dàn dựng vào khu vực chưa được tổ chức. Bây giờ các tệp đã sẵn sàng để chỉnh sửa, vì vậy bất cứ điều gì bạn thay đổi, bạn muốn đi chỉnh sửa và thêm nó và thực hiện một cam kết mới / mới.

Hơn


13
@SMR, Trong ví dụ của bạn, tất cả chỉ trỏ vào ĐẦU hiện tại. ĐẦU ^ = ĐẦU ^ 1. Cũng như ĐẦU ^ 1 = ĐẦU ~ 1. Khi bạn sử dụng HEAD ~ 2, có một sự khác biệt giữa các ký hiệu ~ và ^. Nếu bạn sử dụng ~ 2 có nghĩa là cha mẹ đầu tiên của cha mẹ đầu tiên, thì hay là ông bố của ông bà.
Madhan Ayyasamy

501

Nếu bạn đã cài đặt Git Extras , bạn có thể chạy git undođể hoàn tác cam kết mới nhất. git undo 3sẽ hoàn tác ba cam kết cuối cùng.


470

Tôi muốn hoàn tác năm cam kết mới nhất trong kho lưu trữ được chia sẻ của chúng tôi. Tôi đã tra cứu id sửa đổi mà tôi muốn quay trở lại. Sau đó tôi gõ như sau.

prompt> git reset --hard 5a7404742c85
HEAD is now at 5a74047 Added one more page to catalogue
prompt> git push origin master --force
Total 0 (delta 0), reused 0 (delta 0)
remote: bb/acl: neoneye is allowed. accepted payload.
To git@bitbucket.org:thecompany/prometheus.git
 + 09a6480...5a74047 master -> master (forced update)
prompt>

25
Viết lại lịch sử trên một kho lưu trữ được chia sẻ nói chung là một ý tưởng rất tồi. Tôi giả sử bạn biết những gì bạn đang làm, tôi chỉ hy vọng những độc giả tương lai cũng làm như vậy.
Brad Koch

Có rollback là nguy hiểm. Hãy chắc chắn rằng bản sao làm việc của bạn ở trạng thái mong muốn trước khi bạn đẩy. Khi đẩy thì các cam kết không mong muốn sẽ bị xóa vĩnh viễn.
neoneye

6
"Giống như trong thế giới thực, nếu bạn muốn viết lại lịch sử, bạn cần một âm mưu: mọi người phải 'tham gia' vào âm mưu (ít nhất là mọi người biết về lịch sử, tức là mọi người đã từng rút khỏi chi nhánh) . " Nguồn: stackoverflow.com/a/2046748/334451
Mikko Rantalainen

440

Tôi thích sử dụng git rebase -icho công việc này, bởi vì một danh sách đẹp xuất hiện nơi tôi có thể chọn các cam kết để loại bỏ. Nó có thể không trực tiếp như một số câu trả lời khác ở đây, nhưng nó chỉ cảm thấy đúng .

Chọn bao nhiêu cam kết bạn muốn liệt kê, sau đó gọi như thế này (để tranh thủ ba lần cuối)

git rebase -i HEAD~3

Danh sách mẫu

pick aa28ba7 Sanity check for RtmpSrv port
pick c26c541 RtmpSrv version option
pick 58d6909 Better URL decoding support

Sau đó, Git sẽ xóa các xác nhận cho bất kỳ dòng nào mà bạn xóa.


422

Cách sửa lỗi cam kết cục bộ trước đó

Sử dụng git-gui (hoặc tương tự) để thực hiện a git commit --amend. Từ GUI, bạn có thể thêm hoặc xóa các tệp riêng lẻ khỏi cam kết. Bạn cũng có thể sửa đổi thông điệp cam kết.

Làm thế nào để hoàn tác các cam kết cục bộ trước đó

Chỉ cần đặt lại chi nhánh của bạn về vị trí trước đó (ví dụ: sử dụng gitkhoặc git rebase). Sau đó, áp dụng lại các thay đổi của bạn từ một bản sao đã lưu. Sau khi thu gom rác trong kho lưu trữ cục bộ của bạn, nó sẽ giống như cam kết không mong muốn không bao giờ xảy ra. Để làm tất cả điều đó trong một lệnh duy nhất, sử dụng git reset HEAD~1.

Lời cảnh báo : Sử dụng bất cẩn git resetlà một cách tốt để đưa bản sao làm việc của bạn vào trạng thái khó hiểu. Tôi khuyên người mới Git nên tránh điều này nếu họ có thể.

Làm thế nào để hoàn tác một cam kết công khai

Thực hiện chọn anh đào ngược ( git-Revert ) để hoàn tác các thay đổi.

Nếu bạn chưa kéo các thay đổi khác lên chi nhánh của mình, bạn chỉ cần thực hiện ...

git revert --no-edit HEAD

Sau đó đẩy chi nhánh cập nhật của bạn vào kho lưu trữ được chia sẻ.

Lịch sử cam kết sẽ hiển thị cả hai cam kết, riêng biệt .


Nâng cao: Sửa lỗi của nhánh riêng trong kho lưu trữ công cộng

Điều này có thể nguy hiểm - hãy chắc chắn rằng bạn có một bản sao của chi nhánh để sửa lại.

Cũng lưu ý: Bạn không muốn làm điều này nếu người khác có thể đang làm việc trong chi nhánh.

git push --delete (branch_name) ## remove public version of branch

Dọn dẹp chi nhánh của bạn tại địa phương sau đó sửa lại ...

git push origin (branch_name)

Trong trường hợp bình thường, có lẽ bạn không cần phải lo lắng về lịch sử cam kết của chi nhánh tư nhân của mình còn nguyên sơ. Chỉ cần đẩy một cam kết tiếp theo (xem 'Cách hoàn tác một cam kết công khai' ở trên) và sau đó, thực hiện kết hợp squash để ẩn lịch sử.


8
gitk --all $(git reflog | cut -c1-7)&có thể hữu ích cho việc tìm kiếm bản sửa đổi trước đó nếu bạn muốn hoàn tác cam kết '--amend'.
tộc

4
Cần lưu ý rằng nếu bạn đang cố xóa thông tin bí mật trước khi chuyển sang kho lưu trữ được chia sẻ, thì việc hoàn nguyên sẽ không giúp ích gì cho bạn, vì thông tin sẽ vẫn còn trong lịch sử trong lần xác nhận trước. Nếu bạn muốn đảm bảo thay đổi không bao giờ hiển thị cho những người khác mà bạn cần sử dụnggit reset
Jherico

Tôi nghĩ rằng 'riêng tư' / 'công khai' sẽ chính xác hơn là 'địa phương' / 'từ xa'.
tộc

Sửa một nhánh riêng trong kho lưu trữ từ xa cũng có thể được thực hiện bằng cách đơn giảngit push origin (branch_name) --force
nobar

336

Nếu bạn muốn hoàn tác vĩnh viễn và bạn đã nhân bản một số kho lưu trữ

Id cam kết có thể được nhìn thấy bởi

git log 

Sau đó, bạn có thể làm -

git reset --hard <commit_id>

git push origin <branch_name> -f

Điều gì xảy ra nếu bạn không sử dụng "<commit_id>" và chỉ cần sử dụng "git reset --hard"? Tôi thường chỉ muốn loại bỏ các bản cập nhật mới nhất mà tôi chưa cam kết và quay lại với bản cam kết mới nhất tôi đã thực hiện và tôi luôn sử dụng "git reset --hard".
Jaime Montoya

3
@JaimeMontoya Để hoàn tác các thay đổi mới nhất bạn có thể sử dụng git reset --hard, nhưng nếu bạn phải loại bỏ khó khăn "n" cuối cùng, bạn chỉ định SHA
poorva

334

Nếu bạn đã phạm tội nhưng không được đẩy,

git reset --soft HEAD~1

Đầu ~ 1 là một tốc ký cho cam kết trước đầu. Ngoài ra, bạn có thể tham khảo SHA-1 của hàm băm nếu bạn muốn đặt lại. Tùy chọn --soft sẽ xóa cam kết nhưng nó sẽ để lại tất cả các tệp đã thay đổi của bạn "Thay đổi được cam kết", vì trạng thái git sẽ đặt nó.

Nếu bạn muốn loại bỏ bất kỳ thay đổi nào đối với các tệp được theo dõi trong cây làm việc kể từ khi xác nhận trước khi sử dụng " - ", thay vào đó.

HOẶC LÀ

Nếu bạn đã đẩy và ai đó thường là trường hợp của tôi, bạn không thể sử dụng git reset . Tuy nhiên, bạn có thể thực hiện hoàn nguyên git ,

git revert HEAD

Điều này sẽ tạo ra một cam kết mới đảo ngược mọi thứ được giới thiệu bởi cam kết tình cờ.


Tôi đang ở trường hợp thứ 2, nhưng khi tôi thực hiện "git Revert HEAD" thì nó báo "error: Commit [ID] là một sự hợp nhất nhưng không có tùy chọn -m nào được đưa ra. Fatal: Revert fail". Bất kỳ đề xuất?
metaforge

2
Có lẽ đáng để đề cập rằng thay vì HEAD~1bạn có thể sử dụng hàm băm thực tế như được hiển thị bởi git log --stathoặc bởi git reflog- hữu ích khi bạn cần 'hoàn tác' nhiều hơn một cam kết.
ccpizza

284

Trên SourceTree (GUI cho GitHub), bạn có thể nhấp chuột phải vào cam kết và thực hiện 'Cam kết ngược'. Điều này sẽ hoàn tác những thay đổi của bạn.

Trên thiết bị đầu cuối:

Bạn có thể sử dụng thay thế:

git revert

Hoặc là:

git reset --soft HEAD^ # Use --soft if you want to keep your changes.
git reset --hard HEAD^ # Use --hard if you don't care about keeping your changes.

263

Một lệnh duy nhất:

git reset --soft 'HEAD^' 

Nó hoạt động tuyệt vời để hoàn tác cam kết địa phương cuối cùng!


11
Tôi cần phải viết git reset --soft "HEAD ^" với dấu ngoặc kép, vì tôi viết nó từ dấu nhắc lệnh của Windows.
Ena

253

Chỉ cần thiết lập lại nó bằng cách sử dụng lệnh dưới đây git:

git reset --soft HEAD~1

Giải thích: những gì git resetkhông, đó là cơ bản resetđối với bất kỳ cam kết mà bạn muốn quay trở lại, sau đó nếu bạn kết hợp nó với --softchìa khóa, nó sẽ quay trở lại, nhưng giữ những thay đổi trong tập tin của bạn (s), do đó bạn sẽ có được trở lại sân khấu mà tập tin vừa được thêm vào, HEADlà người đứng đầu chi nhánh và nếu bạn kết hợp với ~1(trong trường hợp này bạn cũng sử dụng HEAD^), nó sẽ quay trở lại chỉ một cam kết mà bạn muốn ...

Tôi tạo các bước trong hình ảnh bên dưới để biết thêm chi tiết cho bạn, bao gồm tất cả các bước có thể xảy ra trong các tình huống thực tế và cam kết mã:

Làm thế nào để hoàn tác các cam kết cuối cùng trong Git?


239

Làm thế nào để hoàn tác cam kết Git cuối cùng?

Để khôi phục mọi thứ trở lại như trước khi cam kết cuối cùng, chúng ta cần đặt lại về cam kết trước TRƯỚC.

  1. Nếu bạn không muốn giữ những thay đổi mà bạn đã thực hiện:

    git reset --hard HEAD^
    
  2. Nếu bạn muốn giữ những thay đổi của mình:

    git reset --soft HEAD^
    

Bây giờ hãy kiểm tra nhật ký git của bạn. Nó sẽ cho thấy cam kết cuối cùng của chúng tôi đã bị xóa.


193

"Đặt lại cây làm việc đến lần xác nhận cuối cùng"

git reset --hard HEAD^ 

"Làm sạch các tệp không xác định khỏi cây làm việc"

git clean    

xem - Tham khảo nhanh Git

LƯU Ý: Lệnh này sẽ xóa cam kết trước đó của bạn, vì vậy hãy thận trọng khi sử dụng! git reset --hardan toàn hơn


190

Sử dụng reflog để tìm trạng thái chính xác

git reflog

reflog trước REFLOG TRƯỚC KHI THIẾT LẬP

Chọn reflog chính xác (f3cb6e2 trong trường hợp của tôi) và gõ

git reset --hard f3cb6e2

Sau đó, phần đầu repo sẽ được đặt lại thành phần ĐẦU TIÊN SAU KHI THIẾT LẬP thiết lập lại hiệu ứng

Cuối cùng, reflog trông giống như hình dưới đây

từ chối sau CUỐI CÙNG REFLOG


164

Lần chạy đầu tiên:

git reflog

Nó sẽ cho bạn thấy tất cả các hành động có thể bạn đã thực hiện trên kho lưu trữ của mình, ví dụ: cam kết, hợp nhất, kéo, v.v.

Sau đó làm:

git reset --hard ActionIdFromRefLog

155

Hoàn tác cam kết cuối cùng:

git reset --soft HEAD^ hoặc là git reset --soft HEAD~

Điều này sẽ hoàn tác cam kết cuối cùng.

Ở đây --softcó nghĩa là thiết lập lại thành dàn.

HEAD~hoặc HEAD^có nghĩa là di chuyển để cam kết trước TRƯỚC.


Thay thế cam kết cuối cùng vào cam kết mới:

git commit --amend -m "message"

Nó sẽ thay thế cam kết cuối cùng với cam kết mới.


153

Cách khác:

Kiểm tra chi nhánh bạn muốn hoàn nguyên, sau đó đặt lại bản sao làm việc cục bộ của bạn trở lại cam kết mà bạn muốn là bản mới nhất trên máy chủ từ xa (mọi thứ sau đó sẽ tạm biệt). Để thực hiện việc này, trong SourceTree, tôi đã nhấp chuột phải vào và chọn "Đặt lại BRANCHNAME cho cam kết này".

Sau đó điều hướng đến thư mục cục bộ của kho lưu trữ của bạn và chạy lệnh này:

git -c diff.mnemonicprefix=false -c core.quotepath=false push -v -f --tags REPOSITORY_NAME BRANCHNAME:BRANCHNAME

Điều này sẽ xóa tất cả các xác nhận sau cái hiện tại trong kho lưu trữ cục bộ của bạn nhưng chỉ cho một nhánh đó.


144

Nhập git logvà tìm mã băm xác nhận cuối cùng và sau đó nhập:

git reset <the previous co>

139

Trong trường hợp của tôi, tôi đã vô tình phạm một số tệp mà tôi không muốn. Vì vậy, tôi đã làm như sau và nó đã làm việc:

git reset --soft HEAD^
git rm --cached [files you do not need]
git add [files you need]
git commit -c ORIG_HEAD

Xác minh kết quả bằng gitk hoặc git log --stat


133

Đơn giản, chạy cái này trong dòng lệnh của bạn:

git reset --soft HEAD~ 

126

Có rất nhiều cách để làm điều đó:

Lệnh Git để hoàn tác lần xác nhận cuối cùng / lần xác nhận trước:

Cảnh báo: Không sử dụng - cho dù bạn không biết bạn đang làm gì. - quá nguy hiểm và nó có thể xóa các tập tin của bạn.

Lệnh cơ bản để hoàn nguyên cam kết trong Git là:

$ git reset --hard <COMMIT -ID>

hoặc là

$ git reset --hard HEAD~<n>

CAM KẾT : ID cho cam kết

n: là số lần xác nhận cuối cùng bạn muốn hoàn nguyên

Bạn có thể lấy id xác nhận như hình dưới đây:

$ **git log --oneline**

d81d3f1 function to subtract two numbers

be20eb8 function to add two numbers

bedgfgg function to mulitply two numbers

trong đó d81d3f1be20eb8 là id xác nhận.

Bây giờ hãy xem một số trường hợp:

Giả sử bạn muốn hoàn nguyên cam kết cuối cùng 'd81d3f1'. Đây là hai lựa chọn:

$ git reset --hard d81d3f1

hoặc là

$ git reset --hard HEAD~1

Giả sử bạn muốn hoàn nguyên cam kết 'be20eb8':

$ git reset --hard be20eb8

Để biết thêm thông tin chi tiết, bạn có thể tham khảo và thử một số lệnh khác để đặt lại đầu vào trạng thái được chỉ định:

$ git reset --help

5
git reset --hard HEAD~1quá nguy hiểm ! Điều này sẽ không chỉ "hủy bỏ cam kết cuối cùng", mà sẽ hoàn nguyên repo hoàn toàn trở lại cam kết trước đó. Vì vậy, bạn sẽ LOẠI tất cả các thay đổi đã cam kết trong lần cam kết cuối cùng!
Arnis Juraga

Bạn đúng, để hoàn tác điều này bạn có thể sử dụnggit push -f <remote> HEAD@{1}:<branch>
Benny

Thật không may, tôi sử dụng --hard và các tệp của tôi bị xóa! Tôi đã không kiểm tra bình luận đầu tiên vì nó bị sụp đổ. Không sử dụng - cho dù bạn không biết bạn đang làm gì!
ẩn danh

125

Đối với một cam kết địa phương

git reset --soft HEAD~1

hoặc nếu bạn không nhớ chính xác cam kết đó là gì, bạn có thể sử dụng

git rm --cached <file>

Đối với một cam kết đẩy

Cách thích hợp để loại bỏ các tập tin từ lịch sử kho lưu trữ đang sử dụng git filter-branch. Đó là,

git filter-branch --index-filter 'git rm --cached <file>' HEAD

Nhưng tôi khuyên bạn nên sử dụng lệnh này một cách cẩn thận. Đọc thêm tại Trang hướng dẫn git-filter-Branch (1) .


125

Có hai kịch bản chính

Bạn chưa đẩy mạnh cam kết

Nếu sự cố là các tệp bổ sung mà bạn đã cam kết (và bạn không muốn những tệp đó trên kho lưu trữ), bạn có thể xóa chúng bằng cách sử dụng git rmvà sau đó cam kết với--amend

git rm <pathToFile>

Bạn cũng có thể xóa toàn bộ thư mục với -rhoặc thậm chí kết hợp với các lệnh Bash khác

git rm -r <pathToDirectory>
git rm $(find -name '*.class')

Sau khi xóa các tệp, bạn có thể cam kết, với tùy chọn --amend

git commit --amend -C HEAD # the -C option is to use the same commit message

Điều này sẽ viết lại cam kết cục bộ gần đây của bạn loại bỏ các tệp bổ sung, vì vậy, các tệp này sẽ không bao giờ được gửi khi đẩy và cũng sẽ bị xóa khỏi kho lưu trữ .git cục bộ của bạn bởi GC.

Bạn đã đẩy cam kết

Bạn có thể áp dụng cùng một giải pháp cho kịch bản khác và sau đó thực hiện git pushvới -ftùy chọn, nhưng không nên vì nó ghi đè lên lịch sử từ xa bằng một thay đổi khác nhau (nó có thể làm rối kho lưu trữ của bạn).

Thay vào đó, bạn phải thực hiện cam kết mà không cần --amend(hãy nhớ điều này về -amend`: Tùy chọn đó viết lại lịch sử trên lần xác nhận cuối cùng).


125

Để đặt lại về phiên bản trước, xóa vĩnh viễn tất cả các thay đổi không được cam kết:

git reset --hard HEAD~1

23
Có lẽ bạn có thể tại một ghi chú / cảnh báo rằng lệnh của anh ta sẽ vứt bỏ cam kết và những thay đổi trong thư mục làm việc mà không cần hỏi thêm.
cr7pt0gr4ph7

6
Nếu bạn tình cờ làm điều này một cách tình cờ, mặc dù không phải tất cả đã mất. Xem stackoverflow.com/questions/10099258/ ,, stackoverflow.com/questions/15479501/ và và stackoverflow.com/questions/7374069/undo-git-reset-hard/7376959 .
cr7pt0gr4ph7

13
Sử dụng --softđể giữ các thay đổi của bạn như uncommitted changes, --hardđể hoàn thành cam kết và hoàn nguyên lại một lần. Hãy nhớ chỉ thực hiện các thao tác như vậy đối với các thay đổi, chưa được đẩy.
Yunus Nedim Mehel

@Zaz: Bạn nói đúng; có lẽ tôi nên làm rõ điều đó Chỉ các tệp / thay đổi đã được thêm vào chỉ mục (/ dàn dựng) hoặc đã được cam kết có thể được phục hồi. Những thay đổi không được cam kết, không được thực hiện , như bạn đã nói, hoàn toàn bị ném đi git reset --hard.
cr7pt0gr4ph7

1
Là một sidenote: Mỗi khi một tập tin được dàn dựng, gitlưu trữ nội dung của nó trong cơ sở dữ liệu đối tượng của nó. Các nội dung được lưu trữ chỉ được loại bỏ khi bộ sưu tập rác được thực thi. Do đó, có thể khôi phục phiên bản được dàn dựng cuối cùng của một tệp hiện không được dàn dựng khi git reset --hardđược thực thi (xem các bài đăng được liên kết ở trên để biết thêm thông tin).
cr7pt0gr4ph7
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.