a

Câu hỏi Làm thế nào để hoàn tác các cam kết gần đây nhất trong Git?


Tôi vô tình phạm sai các tập tin Git, nhưng tôi chưa đẩy cam kết vào máy chủ.

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


17823
2017-07-28 22:22


gốc


Cảnh báo: bạn chỉ nên làm điều này nếu bạn chưa đẩy cam kết vào một điều khiển từ xa, nếu không bạn sẽ làm hỏng lịch sử của những người khác đã kéo cam kết từ xa! - thSoft
Đây là một bài viết rất rõ ràng và kỹ lưỡng về việc hoàn tác mọi thứ trong git, trực tiếp từ Github. - Nobita
Xem hướng dẫn này cho Git cam kết hoàn tác trên Chi nhánh địa phương, Công cộng và Git Cách hoàn tác Cam kết Git như chuyên nghiệp - Luzan Baral
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
Bạn biết git cần gì không? git undo, đó là nó. Sau đó, git danh tiếng có để xử lý những sai lầm được thực hiện bởi chúng tôi chỉ những con người biến mất. Thực hiện bằng cách đẩy trạng thái hiện tại trên một ngăn xếp git trước khi thực hiện bất kỳ gitchỉ huy. Nó sẽ ảnh hưởng đến hiệu suất, vì vậy nó sẽ là tốt nhất để thêm một lá cờ cấu hình để cho dù để kích hoạt nó. - Yimin Rong


Các câu trả lời:


Hoàn tác 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 lá cây làm việc của bạn (trạng thái của các tập tin của bạn trên đĩa) không thay đổi nhưng undoes cam kết và để lại những thay đổi bạn cam kết unstaged (do đó, họ sẽ xuất hiện như là "Thay đổi không dàn dựng cho cam kết" trong git statusvà bạn sẽ cần phải thêm lại chúng trước khi cam kết). nếu bạn chỉ có muốn thêm vào nhiều thay đổi hơn cho cam kết trước đó hoặc thay đổi thông báo cam kết1, bạn đã có thể sử dụng git reset --soft HEAD~ thay vào đó, giống như git reset HEAD~ (Ở đâu HEAD~ giống như HEAD~1) nhưng để lại các thay đổi hiện có của bạn.
  3. Chỉnh sửa các tệp cây đang hoạt động.
  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 các thay đổi, sử dụng lại thông điệp cam kết cũ. reset sao chép đầu cũ để .git/ORIG_HEAD; commit với -c ORIG_HEAD sẽ mở một trình soạn thảo, ban đầu có 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 -C Tùy chọn.

Tuy nhiên, hãy cẩn thận nếu bạn đã thêm bất kỳ thay đổi mới nào vào chỉ mục, sử dụng commit --amend sẽ 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ể xem câu trả lời này:

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

Câu trả lời ở trên sẽ cho bạn thấy git reflog được sử dụng để tìm hiểu SHA-1 mà bạn muốn hoàn nguyên là gì. 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.


1 Tuy nhiên, lưu ý rằng bạn không cần phải đặt lại về cam kết trước đó nếu bạn vừa mắc lỗi trong cam kết tin nhắn. Tùy chọn dễ dàng hơn là git reset (để upstage bất kỳ thay đổi bạn đã thực hiện từ) và sau đó git commit --amend, sẽ mở trình chỉnh sửa tin nhắn 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.


19285
2018-06-16 17:27



Và nếu cam kết bị sai chi nhánh, bạn có thể git checkout theRightBranch với tất cả các giai đoạn thay đổi. Như tôi đã phải làm. - Frank Shearar
Nếu bạn đang làm việc trong DOS, thay vì git reset --soft HEAD^ bạn sẽ cần phải 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 đúng cách. Cũng thế, --soft là mặc định, vì vậy bạn có thể bỏ qua nếu bạn thích và chỉ nói git reset HEAD~1. - Ryan Lundy
Ngoài ra, trong zsh bạn phải báo giá ^, vì vậy git reset --soft 'HEAD^'... ít nhất tôi đã làm - jberryman
(Chỉnh sửa những gì tôi đã viết ở trên; --mixed là mặc định. --mixed có nghĩa là giữ các tệp đã thay đổi, nhưng không giữ chúng trong chỉ mục. --soft sẽ giữ các tập tin đã thay đổi và giữ chúng trong chỉ mục như trước khi commit đã thay đổi. Xin lỗi vì sự nhầm lẫn.) - Ryan Lundy
người dùng zsh có thể nhận được: zsh: no matches found: HEAD^ - bạn cần phải thoát khỏi ^ tức là git reset --soft HEAD\^ - tnajdek


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

Giả sử bạn có điều này, trong đó C là HEAD của bạn và (F) là trạng thái 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ó một lần nữa. 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à HEAD. 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.

Ah, nhưng giả sử cam kết C không phải là một thảm họa, nhưng chỉ một chút đi. Bạn muốn hoàn tác cam kết nhưng giữ các thay đổi của bạn cho một chút chỉnh sửa 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àm HEAD của bạn:

   (F)
A-B-C
    ↑
  master

Bạn có thể làm điều này, để lại --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ỏ tới commit mới nhất. Khi bạn làm một git reset HEAD~1, bạn yêu cầu Git di chuyển con trỏ HEAD trở lại một lần commit. Nhưng (trừ khi bạn sử dụng --hard) bạn để lại các tập tin của bạn như chúng được. Vậy bây giờ git status cho thấy những thay đổi bạn đã kiểm tra vào C. Bạn chưa mất một thứ gì!

Để chạm nhẹ nhất, bạn thậm chí có thể undo cam kết của bạn nhưng để lại các tập tin của bạn và của bạn mục lục:

git reset --soft HEAD~1

Điều này không chỉ để lại các tệp của bạn một mình, nó thậm chí còn để lại mục lục một mình. Khi bạn làm git status, bạn sẽ thấy rằng các tệp giống nhau 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 commit và 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 đó khám phá ra bạn cần nó sau khi tất cả? Tough may mắn, phải không?

Không, có vẫn một cách để lấy lại. Kiểu git reflog và bạn sẽ thấy một danh sách (một phần) cam kết shas mà bạn đã di chuyển xung quanh. Tìm các 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 đã khôi phục lại cam kết đó. Cam kết không thực sự bị tiêu diệt 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 trong những điều bạn không có ý định loại bỏ.


9734
2018-05-29 18:16



@dma_k, vâng. Hoặc bạn có thể làm git reset --hard HEAD^^ Một lần. Tôi sử dụng ký hiệu dấu ngã (~) vì ký hiệu dấu (^) không hoạt động trong DOS. - Ryan Lundy
Một mẹo hay khác: Bạn có thể đính kèm lại chi nhánh vào cam kết mà bạn đã xóa nó khỏi git branch -f <branch> <commit-id>. Tiết kiệm phải tạo lại cam kết! - naught101
Đối với một người mới bắt đầu git, nó không phải là rõ ràng những gì là sự khác biệt giữa hai lựa chọn cuối cùng (--soft và một ở trên nó). Đề cập đến các chỉ số không giúp đỡ, chúng tôi không thực sự biết điều đó có nghĩa là chưa. @ Nessur của kết nối giữa mềm và Ctrl-Z thực sự đã giúp! Nhưng tôi vẫn không hoàn toàn hiểu được sự khác biệt giữa hai lựa chọn. - Stomp
Tốt hơn là nên nói 'tại sao' một cái gì đó hiệu quả, hơn là chỉ để được kể câu trả lời. Kudos cho mô tả này - nó đã giúp 'get' git. - Chris Nash
Thiếu một điểm quan trọng: Nếu lời cam kết trước đó bị 'đẩy' tới điều khiển từ xa, bất kỳ thao tác 'hoàn tác' nào, dù đơn giản như thế nào, sẽ gây ra nỗi đau to lớn và đau khổ cho những người dùng còn lại trong bản sao cục bộ của họ, khi họ thực hiện 'git pull' trong tương lai. Vì vậy, nếu cam kết đã được 'đẩy', hãy làm điều này thay vào đó: git hoàn nguyên <bad-commit-sha1-id> git push origin: - FractalSpace


Điều này đã cho tôi một thời gian để tìm ra, vì vậy có lẽ điều này sẽ giúp một ai đó ...

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 đã thực hiện cam kết của mình công khai hay chưa (được đẩy tới kho lưu trữ từ xa của bạn):

Cách hoàn tác cam kết cục bộ

Giả sử tôi cam kết cục bộ, nhưng bây giờ 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ứ về cách trước khi commit trước, chúng ta cần reset đến cam 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

Hiện nay git log sẽ cho thấy cam kết cuối cùng của chúng tôi đã bị xóa.

Cách hoàn tác cam kết công khai

Nếu bạn đã thực hiện các cam kết của bạn công cộng, 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 đó của bạn (HEAD 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 thực hiện:

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 Git Khái niệm cơ bản - Hoàn tác mọi thứ


1751
2018-05-29 18:13



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 HEAD và sau đó đẩy lại và nó hoạt động :) - nacho4d
@riezebosch: cảnh báo của bạn đang ở sai chỗ. Câu trả lời này không làm hỏng mọi thứ, gây ra nó một cách chính xác tạo ra một cam kết mới 102: "khôi phục các tập tin tôi gỡ bỏ một cách tình cờ" - rubo77
Đây là câu trả lời hay nhất vì nó không gây rối với lịch sử git của bạn - Lukas


Thêm / xóa tệp để có được những 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 đó 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 chưa bao giờ phạm sai lầm ngay từ đầu.

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, sau đó bạn sẽ chỉ phải thực hiện một sửa chữa bình thường.


1625
2017-07-31 09:39



Điều này có hiệu quả khi tôi làm git commit --amend và điều tôi thực sự muốn làm là git commit? - dbm
@dbm, nếu bạn vô tình sửa đổi, sử dụng git reset --soft <oldref>, trong đó oldref là ID cam kết trước khi sửa đổi. Bạn có thể dùng git reflog để xác định ID cam kết cũ. Điều này sẽ hoàn tác các tác động của sửa đổi, nhưng để lại các thay đổi được tổ chức. Sau đó, chỉ cần làm git commit cam kết như một cam kết thường xuyên. - bdonlan
@ Dennis, git commit --amend biến cây hiện tại (tức là các thay đổi theo giai đoạn) thành một cam kết, ghi đè HEAD hiện tại. Sau thời điểm đó, chúng không được coi là dàn dựng nữa bởi vì chúng là một phần của cam kết (ví dụ, git diff --cached là trống), nhưng chúng không bị "loại bỏ" hoặc "bị mất". - bdonlan


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 .java tệp (và bất kỳ tệp nào khác) mà bạn muốn cam kết.

Các hard reset đến HEAD-1 sẽ đặt bản sao làm việc của bạn về trạng thái của cam kết trước khi cam kết sai của bạn.


878
2018-05-25 16:04



"--hard" sẽ loại bỏ các tệp .java đã sửa đổi trong thư mục làm việc mà anh ta muốn cam kết. - Esko Luontola
Bạn có thể "git stash save" thay đổi bản sao làm việc, thực hiện khôi phục cứng và sau đó "git stash pop" để lấy lại chúng, mặc dù tôi cho rằng việc đặt lại mềm sẽ đơn giản hơn. - Asad R.
git commit -a -m "" hoặc là git commit -am "" một cách tự nhiên! :] - trejder
Một 'shortcut' khác sử dụng stash; nếu bạn muốn unstage tất cả mọi thứ (undo git add), chỉ git stash, sau đó git stash pop - seanriordan08


Để 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 nhánh tư nhân, sửa đổi cam kết:

git commit --amend

Hoặc, nếu đó là một nhánh 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 cam kết trước đó, sử dụng tuyệt vời tương tác rebase)


ProTip: Thêm *.class đến một gitignore để ngăn chặn điều này xảy ra lần nữa.


Để hoàn nguyên cam kết

Sửa đổi 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 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

Ở đâu N là số lần commit trước HEAD@~ đặt lại về cam kết trước đó.

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 và --hard, để hiểu rõ hơn về điều này.

Reflog

Nếu bạn mess up, bạn luôn có thể sử dụng reflog để tìm các commit bị xóa:

$ 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



679
2018-01-31 07:06



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


Sử dụng git revert commit-id

Để nhận ID cam kết, chỉ cần sử dụng git log


549
2017-12-13 10:18



Nếu bạn đã cam kết sai chi nhánh: khi đã hoàn nguyên, hãy chuyển sang nhánh chính xác và chọn cherry. - Kris
Đ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 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 cam kết, nhưng bây giờ nếu tôi thanh toán chi 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 sai chi nhánh nhưng thay vào đó hãy tạo chúng tệp) trong nhánh chính xác? - astronomerdave
Tôi chỉ sử dụng git revert commit-id làm việc như người ở. Tất nhiên sau đó bạn sẽ cần phải đẩy những thay đổi của bạn. - Casey Robinson
Tôi tin rằng đó sẽ là git cherry-pick <<erroneous-commit-sha>> @astronomerdave. Từ, ông gần hai năm-muộn-to-the-đảng. - Tom Howard


Nếu bạn đang lập kế hoạch hoàn tác một cam kết cục bộ hoàn toàn, bất kể bạn thay đổi bạn đã làm gì 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 làm lệnh sau đây.

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 bạn, nhưng bạn muốn thay đổi của bạn trong khu vực dàn dựng (trước khi cam kết giống như sau git add) sau đó 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 dàn dựng. Giả sử nếu bạn muốn unstage các tập tin, bởi vì bạn cần phải chỉnh sửa một số conent sai, sau đó làm lệnh sau

git reset HEAD

Bây giờ các tệp đã cam kết đến từ khu vực được dàn dựng vào khu vực không được tổ chức. Bây giờ các tập tin đã 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 tươi / mới.

Hơn


441
2018-04-06 13:58



@SMR, Trong ví dụ của bạn, tất cả chỉ trỏ vào HEAD hiện tại. HEAD ^ = HEAD ^ 1. Cũng như HEAD ^ 1 = HEAD ~ 1. Khi bạn sử dụng HEAD ~ 2, có sự khác biệt giữa ~ và ^ ký hiệu. Nếu bạn sử dụng ~ 2 có nghĩa là "cha mẹ đầu tiên của cha mẹ đầu tiên" hoặc "ông bà". - Madhan Ayyasamy
rõ ràng và dễ hiểu, tiết kiệm đêm của tôi! - Vladimir Ch
Rõ ràng, tôi vô tình downvoted câu trả lời này, và nó quá muộn để tôi un-downvote nó. Xin lỗi. - Matt


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


431
2017-10-25 03:41





Tôi muốn hoàn tác 5 cam kết mới nhất trong kho lưu trữ được chia sẻ của chúng tôi. Tôi nhìn lên id sửa đổi mà tôi muốn quay trở lại. Sau đó, tôi gõ vào 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>

399



Viết lại lịch sử trên một kho lưu trữ chung thường là một ý tưởng rất tồi. Tôi cho rằng bạn biết bạn đang làm gì, tôi chỉ hy vọng những người đọc trong tương lai cũng vậy. - Brad Koch
Có rollback là nguy hiểm. Đảm bảo 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
"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" ở trong "âm mưu (ít nhất 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
tuyệt diệu. Điều này làm việc như một say mê. Nên có tùy chọn git cho nhà phát triển làm việc độc lập - nơi người dùng có thể sử dụng GitX hoặc nút xóa bên cạnh cam kết và xác nhận - vì vậy quá trình này không quá bí ẩn :) - zero_cool


Tôi thích sử dụng git rebase -i cho công việc này, bởi vì một danh sách tốt đẹp bật lê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 số lượng cam kết bạn muốn liệt kê, sau đó gọi như thế này (để nhập ngũ cuối cùng)

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 cam kết cho bất kỳ dòng nào bạn xóa.


381