Làm cách nào để hoàn nguyên kho lưu trữ Git về lần xác nhận trước?


7630

Làm cách nào để tôi hoàn nguyên từ trạng thái hiện tại của mình sang ảnh chụp nhanh được thực hiện trên một cam kết nhất định?

Nếu tôi làm git log, sau đó tôi nhận được đầu ra sau đây:

$ git log
commit a867b4af366350be2e7c21b8de9cc6504678a61b`
Author: Me <me@me.com>
Date:   Thu Nov 4 18:59:41 2010 -0400

blah blah blah...

commit 25eee4caef46ae64aa08e8ab3f988bc917ee1ce4
Author: Me <me@me.com>
Date:   Thu Nov 4 05:13:39 2010 -0400

more blah blah blah...

commit 0766c053c0ea2035e90f504928f8df3c9363b8bd
Author: Me <me@me.com>
Date:   Thu Nov 4 00:55:06 2010 -0400

And yet more blah blah...

commit 0d1d7fc32e5a947fbd92ee598033d85bfc445a50
Author: Me <me@me.com>
Date:   Wed Nov 3 23:56:08 2010 -0400

Yep, more blah blah.

Làm thế nào để tôi trở lại cam kết từ ngày 3 tháng 11, tức là cam kết 0d1d7fc?



116
Đây là một bài viết rất rõ ràng và kỹ lưỡng về những thứ hoàn tác trong git, trực tiếp từ Github.
Nobita

3
Liên quan: Phục hồi cho một cam kết Git cũ trong một repo công khai . Lưu ý rằng câu hỏi đó thêm một ràng buộc rằng repo là công khai.

58
Tôi yêu git, nhưng thực tế là có 35 câu trả lời cho một thứ cực kỳ đơn giản đã phơi bày một vấn đề rất lớn với git. Hay là tài liệu?
Người đàn ông Muffin

2
Làm thế nào là "cái bẫy" trong việc sử dụng từ này để trở lại như ý nghĩa thông tục để thiết lập lại thậm chí không được quảng cáo ở đây ??? 6594 upvote cho đến nay và không phải là một chỉnh sửa theo cách này, để nhấn mạnh sự khác biệt? Sẽ không khó hiểu hơn khi đề cập đến "lưu tệp" ở đây với biểu thức "cam kết" ...
RomainValeri

Câu trả lời:


9730

Điều này phụ thuộc rất nhiều vào ý nghĩa của bạn khi "hoàn nguyên".

Tạm thời chuyển sang một cam kết khác

Nếu bạn muốn tạm thời quay lại với nó, đánh lừa xung quanh, sau đó quay lại nơi bạn đang ở, tất cả những gì bạn phải làm là kiểm tra cam kết mong muốn:

# This will detach your HEAD, that is, leave you with no branch checked out:
git checkout 0d1d7fc32

Hoặc nếu bạn muốn thực hiện các cam kết trong khi bạn ở đó, hãy tiếp tục và tạo một chi nhánh mới trong khi bạn ở đó:

git checkout -b old-state 0d1d7fc32

Để quay trở lại nơi bạn đang ở, chỉ cần kiểm tra chi nhánh bạn đã ở lại. (Nếu bạn đã thực hiện thay đổi, như mọi khi khi chuyển đổi chi nhánh, bạn sẽ phải xử lý chúng khi thích hợp. Bạn có thể đặt lại để vứt chúng đi; họ đến một chi nhánh ở đó nếu bạn muốn một chi nhánh ở đó.)

Xóa cứng các cam kết chưa được công bố

Mặt khác, nếu bạn muốn thực sự loại bỏ mọi thứ bạn đã làm kể từ đó, có hai khả năng. Một, nếu bạn chưa công bố bất kỳ cam kết nào trong số này, chỉ cần đặt lại:

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.

Nếu bạn làm phiền, bạn đã vứt bỏ các thay đổi cục bộ của mình, nhưng ít nhất bạn có thể quay lại nơi bạn đã ở trước đó bằng cách đặt lại lần nữa.

Hoàn tác các cam kết được công bố với các cam kết mới

Mặt khác, nếu bạn đã xuất bản tác phẩm, có lẽ bạn không muốn đặt lại chi nhánh, vì đó là cách viết lại lịch sử một cách hiệu quả. Trong trường hợp đó, bạn thực sự có thể hoàn nguyên các cam kết. Với Git, Revert có một ý nghĩa rất cụ thể: tạo một cam kết với bản vá ngược để hủy bỏ nó. Bằng cách này, bạn không viết lại bất kỳ lịch sử.

# This will create three separate revert commits:
git revert a867b4af 25eee4ca 0766c053

# It also takes ranges. This will revert the last two commits:
git revert HEAD~2..HEAD

#Similarly, you can revert a range of commits using commit hashes:
git revert a867b4af..0766c053 

# Reverting a merge commit
git revert -m 1 <merge_commit_sha>

# To get just one, you could use `rebase -i` to squash them afterwards
# Or, you could do it manually (be sure to do this at top level of the repo)
# get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .

# Then commit. Be sure and write a good message describing what you just did
git commit

Các git-reverttrang web thực sự bao gồm rất nhiều điều này trong mô tả của nó. Một liên kết hữu ích khác là phần git-scm.com này thảo luận về git-Revert .

Nếu bạn quyết định cuối cùng bạn không muốn hoàn nguyên, bạn có thể hoàn nguyên hoàn nguyên (như được mô tả ở đây) hoặc đặt lại về trước khi hoàn nguyên (xem phần trước).

Bạn cũng có thể thấy câu trả lời này hữu ích trong trường hợp này:
Làm thế nào để di chuyển CHÍNH trở lại vị trí trước đó? (Đầu tách ra)


118
Nhận xét của @ Rod về git revert HEAD~3việc wat tốt nhất để hoàn nguyên các 3cam kết là một quy ước quan trọng.
New Alexandria

19
Bạn có thể viết toàn bộ số? như:git reset --hard 0d1d7fc32e5a947fbd92ee598033d85bfc445a50
Spoeken

16
@MathiasMadsenStav Có, tất nhiên bạn có thể chỉ định các cam kết bằng SHA1 đầy đủ. Tôi đã sử dụng băm viết tắt để làm cho câu trả lời dễ đọc hơn và bạn cũng có xu hướng sử dụng chúng nếu bạn đang gõ. Nếu bạn đang sao chép và dán, bằng mọi cách, hãy sử dụng hàm băm đầy đủ. Xem Chỉ định Sửa đổi trong man git rev-parse để biết mô tả đầy đủ về cách bạn có thể đặt tên cho các cam kết.
Cascabel

59
Bạn có thể sử dụng git revert --no-commit hash1 hash2 ...và sau đó chỉ cần cam kết mỗi lần hoàn nguyên trong một lần cam kếtgit commit -m "Message"
Mirko Akov

6
"Xuất bản" nghĩa là gì trong bối cảnh này?
Howiecamp

1851

Có rất nhiều câu trả lời phức tạp và nguy hiểm ở đây, nhưng thực sự rất dễ:

git revert --no-commit 0766c053..HEAD
git commit

Điều này sẽ hoàn nguyên mọi thứ từ ĐẦU trở lại hàm băm xác nhận, nghĩa là nó sẽ tạo lại trạng thái cam kết đó trong cây làm việc như thể mọi cam kết đã được đưa trở lại. Sau đó, bạn có thể cam kết cây hiện tại và nó sẽ tạo ra một cam kết hoàn toàn mới về cơ bản tương đương với cam kết mà bạn "hoàn nguyên".

( --no-commitCờ cho phép git hoàn nguyên tất cả các cam kết cùng một lúc - nếu không, bạn sẽ được nhắc nhở về một thông báo cho từng cam kết trong phạm vi, xả rác lịch sử của bạn với các cam kết mới không cần thiết.)

Đây là một cách an toàn và dễ dàng để quay trở lại trạng thái trước đó . Không có lịch sử nào bị phá hủy, vì vậy nó có thể được sử dụng cho các cam kết đã được công khai.


23
Nếu bạn thực sự muốn có các cam kết riêng lẻ (thay vì hoàn nguyên mọi thứ bằng một cam kết lớn), thì bạn có thể vượt qua --no-editthay vì đó --no-commit, để bạn không phải chỉnh sửa thông báo cam kết cho mỗi lần đảo ngược.

88
Nếu một trong các cam kết trong khoảng từ 0766c053..IAD là hợp nhất thì sẽ có một lỗi xuất hiện (phải thực hiện với không có chỉ định -m). Điều này có thể giúp những người gặp phải rằng: stackoverflow.com/questions/5970889/ cấp
timhc22

7
Để xem khác biệt trước khi bạn cam kết sử dụng git diff --cached.
John Erck 6/11/2015

21
$ git revert --no-commit 53742ae..HEADtrở vềfatal: empty commit set passed
Alex G

10
@AlexG đó là vì bạn cần nhập hàm băm trước cái bạn muốn quay lại. Trong trường hợp của tôi, băm giống như: 81bcc9e HEAD{0}; e475924 HEAD{1}, ...(từ git reflog) và tôi muốn hoàn tác những gì tôi đã làm 81bcc9e, sau đó tôi phải làmgit revert e475924..HEAD
EpicPandaForce

1611

Mã hóa Rogue?

Làm việc một mình và chỉ muốn nó làm việc? Thực hiện theo các hướng dẫn dưới đây, họ đã làm việc đáng tin cậy cho tôi và nhiều người khác trong nhiều năm.

Làm việc với những người khác? Git là phức tạp. Đọc các bình luận bên dưới câu trả lời này trước khi bạn làm một cái gì đó phát ban.

Hoàn nguyên bản sao làm việc để cam kết gần đây nhất

Để trở lại cam kết trước đó, bỏ qua mọi thay đổi:

git reset --hard HEAD

Trong đó CHÍNH là cam kết cuối cùng trong chi nhánh hiện tại của bạn

Hoàn nguyên Bản sao làm việc cho một Cam kết cũ hơn

Để trở lại một cam kết cũ hơn so với cam kết gần đây nhất:

# Resets index to former commit; replace '56e05fced' with your commit code
git reset 56e05fced 

# Moves pointer back to previous HEAD
git reset --soft HEAD@{1}

git commit -m "Revert to 56e05fced"

# Updates working copy to reflect the new commit
git reset --hard

Tín dụng đi đến một câu hỏi Stack Overflow tương tự, Hoàn nguyên về một cam kết bằng hàm băm SHA trong Git? .


33
Tôi đã làm điều đó, nhưng sau đó tôi không thể cam kết và đẩy đến kho lưu trữ từ xa. Tôi muốn một cam kết cụ thể cũ hơn để trở thành CHÍNH ...
Lennon

7
Điều đó có nghĩa là bạn đã thực hiện các cam kết mà bạn muốn hoàn nguyên. Nó có thể tạo ra nhiều vấn đề cho những người đã kiểm tra mã của bạn và làm việc với nó. Vì họ không thể áp dụng cam kết của bạn trơn tru hơn họ. Trong trường hợp như vậy tốt hơn làm một git hoàn nguyên. Nếu bạn là người duy nhất sử dụng repo. Thực hiện một cú đẩy git -f (Nhưng hãy suy nghĩ kỹ trước khi làm điều đó)
vinothkr

6
Tôi cũng chỉ muốn chỉ ra rằng, thay vào đó, đối với giải pháp thiết lập lại mềm, thay vì thực hiện thiết lập lại hỗn hợp trước và thiết lập lại cứng trước, bạn thực sự có thể thực hiện thiết lập lại cứng trước, như sau : git reset --hard 56e05fc; git reset --soft HEAD@{1}; git commit.

5
@nuton linus paending mình, người tạo ra git, đã chỉ trích nó là quá phức tạp. Anh ta trong hồ sơ nói rằng anh ta đã "sốc" git trở nên nổi tiếng vì sự phức tạp của nó
boulder_ruby

5
@boulder_ruby Tôi nghĩ bạn có nghĩa là Linus Torvalds là người tạo ra git. Nhưng tôi nghĩ Linus Pauling có thể đồng ý rằng git rất phức tạp.
Suncat2000

215

Tùy chọn tốt nhất cho tôi và có lẽ những người khác là tùy chọn thiết lập lại Git:

git reset --hard <commidId> && git clean -f

Đây là lựa chọn tốt nhất cho tôi! Thật đơn giản, nhanh chóng và hiệu quả!


** Lưu ý: ** Như đã đề cập trong các nhận xét, đừng làm điều này nếu bạn chia sẻ chi nhánh của mình với những người khác có bản sao của các cam kết cũ

Cũng từ các bình luận, nếu bạn muốn một phương pháp 'bóng bẩy' hơn, bạn có thể sử dụng

git clean -i

37
Cảnh báo bắt buộc: không làm điều này nếu bạn chia sẻ chi nhánh của mình với những người khác có bản sao của các cam kết cũ, bởi vì sử dụng thiết lập lại cứng như thế này sẽ buộc họ phải đồng bộ hóa lại công việc của họ với chi nhánh mới được đặt lại. Để biết giải pháp giải thích chi tiết cách hoàn nguyên cam kết một cách an toàn mà không mất công với thiết lập lại cứng, hãy xem câu trả lời này .

7
Tôi cảnh báo thứ hai @ Cupcake ... rất ý thức về hậu quả. Tuy nhiên, lưu ý rằng nếu nhu cầu của bạn thực sự là làm cho những cam kết đó biến mất khỏi lịch sử mãi mãi, phương thức thiết lập lại + sạch này sẽ làm điều đó và bạn sẽ cần buộc các chi nhánh sửa đổi của bạn quay trở lại bất kỳ và tất cả các điều khiển từ xa.
ashnazg

5
git sạch -f NGUY HIỂM NGUY HIỂM
Tisch

2
Điều này đặt đầu của bản sao cục bộ của tôi vào cam kết mong muốn. Nhưng sau đó tôi không thể đẩy bất kỳ thay đổi nào vì nó nằm phía sau điều khiển. Và nếu tôi kéo từ điều khiển từ xa, nó sẽ quay trở lại vị trí của nó ở lần xác nhận mới nhất trên nhánh từ xa. Làm thế nào để tôi hoàn toàn xóa sạch (từ mọi nơi) một số cam kết cả trên bản sao địa phương của tôi đã bị đẩy?
Ade

2
@Ade .. Bạn có thể sử dụng git push -fcờ .. Nhưng hãy cẩn thận, nó sẽ ghi đè lên điều khiển từ xa .. Hãy chắc chắn bạn biết những gì bạn muốn làm ...
Pogrindis

176

Trước khi trả lời, hãy thêm một số nền tảng, giải thích đây HEADlà gì .

First of all what is HEAD?

HEADchỉ đơn giản là một tham chiếu đến cam kết hiện tại (mới nhất) trên nhánh hiện tại. Chỉ có thể có một lần duy nhất HEADtại bất kỳ thời điểm nào (không bao gồm git worktree).

Nội dung của HEADđược lưu trữ bên trong .git/HEADvà nó chứa 40 byte SHA-1 của cam kết hiện tại.


detached HEAD

Nếu bạn không có cam kết mới nhất - có nghĩa là HEAD là đang chỉ đến một cam kết trước đó trong lịch sử, nó được gọi làdetached HEAD .

Nhập mô tả hình ảnh ở đây

Trên dòng lệnh, nó sẽ trông như thế này - SHA-1 thay vì tên chi nhánh kể từ HEAD nó không trỏ đến đầu của nhánh hiện tại:

Nhập mô tả hình ảnh ở đây


Một vài lựa chọn về cách phục hồi từ một ĐẦU tách ra:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Điều này sẽ kiểm tra chi nhánh mới chỉ vào cam kết mong muốn. Lệnh này sẽ kiểm tra một cam kết nhất định.

Tại thời điểm này, bạn có thể tạo một nhánh và bắt đầu làm việc từ thời điểm này:

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Bạn luôn có thể sử dụng reflog là tốt. git reflogsẽ hiển thị bất kỳ thay đổi nào đã cập nhật HEADvà kiểm tra mục nhập reflog mong muốn sẽ đặt HEADlại cho cam kết này.

Mỗi khi sửa đổi ĐẦU sẽ có một mục mới trong reflog

git reflog
git checkout HEAD@{...}

Điều này sẽ đưa bạn trở lại cam kết mong muốn của bạn

Nhập mô tả hình ảnh ở đây


git reset HEAD --hard <commit_id>

"Di chuyển" đầu của bạn trở lại cam kết mong muốn.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • Lưu ý: ( Kể từ Git 2.7 ), bạn cũng có thể sử dụng git rebase --no-autostash.

Lược đồ này minh họa lệnh nào làm gì. Như bạn có thể thấy có reset && checkoutsửa đổi HEAD.

Nhập mô tả hình ảnh ở đây


6
Tuyệt vời gợi ý để git reflog, đó chính xác là những gì tôi cần
smac89

4
Ôi! Tất cả điều này có vẻ cực kỳ phức tạp ... không có một lệnh đơn giản nào chỉ đưa bạn lùi một bước trong quy trình? Giống như đi từ phiên bản 1.1 trong dự án của bạn trở lại phiên bản 1.0? Tôi mong đợi một cái gì đó như: git stepback_one_commit hoặc một cái gì đó ....
Kokodoko 20/03/2016

có: git reset HEAD^--hard`
CodeWizard

3
@Kokodoko Vâng, nó phức tạp khủng khiếp ... và một ví dụ hoàn hảo về việc các chuyên gia cân nhắc ít có những người mới bắt đầu. Vui lòng tham khảo câu trả lời của tôi ở đây, và cả cuốn sách tôi giới thiệu trong đó. Git KHÔNG phải là thứ bạn có thể nhận bằng trực giác. Và tôi có thể hoàn toàn chắc chắn CodeWizard đã không làm như vậy.
mike gặm nhấm

145

Nếu bạn muốn "không phổ biến", xóa thông báo cam kết cuối cùng và đưa các tệp đã sửa đổi trở lại theo giai đoạn, bạn sẽ sử dụng lệnh:

git reset --soft HEAD~1
  • --softchỉ ra rằng các tệp không được cam kết nên được giữ lại làm các tệp đang hoạt động trái ngược với các tệp --hardsẽ loại bỏ chúng.
  • HEAD~1là cam kết cuối cùng. Nếu bạn muốn rollback 3 lần, bạn có thể sử dụng HEAD~3. Nếu bạn muốn quay trở lại một số sửa đổi cụ thể, bạn cũng có thể làm điều đó bằng cách sử dụng hàm băm SHA của nó.

Đây là một lệnh cực kỳ hữu ích trong các tình huống bạn đã phạm phải sai lầm và bạn muốn hoàn tác cam kết cuối cùng đó.

Nguồn: http://nakkaya.com/2009/09/24/git-delete-last-commit/


3
Điều này thật nhẹ nhàng và nhẹ nhàng: không có rủi ro nếu bạn không thúc đẩy công việc của mình
nilsM

124

Bạn có thể làm điều này bằng hai lệnh sau:

git reset --hard [previous Commit SHA id here]
git push origin [branch Name] -f

Nó sẽ loại bỏ cam kết Git trước đó của bạn.

Nếu bạn muốn giữ các thay đổi của mình, bạn cũng có thể sử dụng:

git reset --soft [previous Commit SHA id here]

Sau đó, nó sẽ lưu các thay đổi của bạn.


2
Tôi đã thử 1/2 tá câu trả lời trong bài đăng này cho đến khi tôi nhận được câu trả lời này .. tất cả những câu hỏi khác, cấu hình git của tôi liên tục gây ra lỗi cho tôi khi cố gắng đẩy. Câu trả lời này đã làm việc. Cảm ơn!
Gene Bo

Một chi tiết đối với tôi là tôi đã mất các khác biệt .. mà tôi muốn giữ lại để xem những gì tôi đã làm trong cam kết không hoạt động. Vì vậy, lần tới tôi sẽ chỉ lưu dữ liệu đó trước khi ban hành lệnh đặt lại này
Gene Bo

1
Đây là cách duy nhất để tôi hoàn tác một sự hợp nhất tồi tệ, hoàn nguyên đã không hoạt động trong trường hợp đó. Cảm ơn!
Dave Cole

Câu trả lời tốt nhất. Cảm ơn
Imran Pollob

Câu trả lời tốt nhất, cảm ơn bạn.
dùng1394

114

Tôi đã thử rất nhiều cách để hoàn nguyên các thay đổi cục bộ trong Git và có vẻ như điều này hoạt động tốt nhất nếu bạn chỉ muốn trở lại trạng thái cam kết mới nhất.

git add . && git checkout master -f

Mô tả ngắn:

  • Nó sẽ KHÔNG tạo ra bất kỳ cam kết git revertnào.
  • Nó sẽ KHÔNG tách ra ĐẦU của bạn như thế git checkout <commithashcode>.
  • Nó S over ghi đè tất cả các thay đổi cục bộ của bạn và XÓA tất cả các tệp đã thêm kể từ lần xác nhận cuối cùng trong nhánh.
  • Nó chỉ hoạt động với tên nhánh, vì vậy bạn chỉ có thể hoàn nguyên các cam kết mới nhất trong nhánh theo cách này.

Tôi tìm thấy một cách thuận tiện và đơn giản hơn nhiều để đạt được kết quả ở trên:

git add . && git reset --hard HEAD

trong đó CHÍNH chỉ vào cam kết mới nhất tại chi nhánh hiện tại của bạn.

Đó là mã mã giống như boulder_ruby đã đề xuất, nhưng tôi đã thêm git add .trước đó git reset --hard HEADđể xóa tất cả các tệp mới được tạo kể từ lần xác nhận cuối cùng vì đây là điều mà hầu hết mọi người mong đợi khi tôi quay lại cam kết mới nhất.


83

OK, quay trở lại một cam kết trước đó trong Git khá dễ dàng ...

Hoàn nguyên mà không giữ các thay đổi:

git reset --hard <commit>

Hoàn nguyên lại với việc giữ các thay đổi:

git reset --soft <commit>

Giải thích: sử dụnggit reset , bạn có thể đặt lại về trạng thái cụ thể. Nó phổ biến bằng cách sử dụng nó với một hàm băm cam kết như bạn thấy ở trên.

Nhưng như bạn thấy sự khác biệt là sử dụng hai cờ --soft--hardtheo mặc định git resetsử dụng --softcờ, nhưng đó là một cách thực hành tốt luôn sử dụng cờ, tôi giải thích từng cờ:


--mềm mại

Cờ mặc định như được giải thích, không cần cung cấp nó, không thay đổi cây làm việc, nhưng nó thêm tất cả các tệp đã thay đổi sẵn sàng để cam kết, vì vậy bạn quay lại trạng thái cam kết thay đổi tệp không được thực hiện.


--cứng

Hãy cẩn thận với lá cờ này. Nó đặt lại cây làm việc và tất cả các thay đổi đối với các tệp được theo dõi và tất cả sẽ biến mất!


Tôi cũng đã tạo ra hình ảnh bên dưới có thể xảy ra trong cuộc sống thực khi làm việc với Git:

Git đặt lại thành một cam kết



70

Giả sử bạn đang nói về chủ và trên chi nhánh tương ứng (có nghĩa là, đây có thể là bất kỳ chi nhánh làm việc nào bạn quan tâm):

# Reset local master branch to November 3rd commit ID
git reset --hard 0d1d7fc32e5a947fbd92ee598033d85bfc445a50

# Reset remote master branch to November 3rd commit ID
git push -f origin 0d1d7fc32e5a947fbd92ee598033d85bfc445a50:master

Tôi tìm thấy câu trả lời từ trong một bài đăng trên blog (bây giờ không còn tồn tại)

Lưu ý rằng đây là Đặt lại và Buộc thay đổi đối với điều khiển từ xa, để nếu những người khác trong nhóm của bạn đã git kéo, bạn sẽ gây ra sự cố cho họ. Bạn đang phá hủy lịch sử thay đổi, đó là một lý do quan trọng tại sao mọi người sử dụng git ngay từ đầu.

Tốt hơn để sử dụng hoàn nguyên (xem câu trả lời khác) hơn là thiết lập lại. Nếu bạn là một đội một người thì có lẽ điều đó không thành vấn đề.


6
Làm thế nào là câu trả lời này khác với vô số người khác?
Matsmath

Thật không may. Tôi đã gửi email cho blogger - hy vọng anh ấy vẫn có nó!
markreyes


2
Cú pháp đẩy bị thiếu trong hầu hết các đề xuất khác về cách khắc phục điều này. Làm việc tuyệt vời.
jpa57

61

Giả sử bạn có các cam kết sau trong tệp văn bản có tên ~/commits-to-revert.txt(Tôi đã sử dụng git log --pretty=onelineđể nhận chúng)

fe60adeba6436ed8f4cc5f5c0b20df7ac9d93219
0c27ecfdab3cbb08a448659aa61764ad80533a1b
f85007f35a23a7f29fa14b3b47c8b2ef3803d542
e9ec660ba9c06317888f901e3a5ad833d4963283
6a80768d44ccc2107ce410c4e28c7147b382cd8f
9cf6c21f5adfac3732c76c1194bbe6a330fb83e3
fff2336bf8690fbfb2b4890a96549dc58bf548a5
1f7082f3f52880cb49bc37c40531fc478823b4f5
e9b317d36a9d1db88bd34831a32de327244df36a
f6ea0e7208cf22fba17952fb162a01afb26de806
137a681351037a2204f088a8d8f0db6e1f9179ca

Tạo một tập lệnh shell Bash để hoàn nguyên từng tệp trong số chúng:

#!/bin/bash
cd /path/to/working/copy
for i in `cat ~/commits-to-revert.txt`
do
    git revert $i --no-commit
done

Điều này hoàn nguyên mọi thứ trở lại trạng thái trước đó, bao gồm cả việc tạo tệp và thư mục và xóa, cam kết nó với chi nhánh của bạn và bạn giữ lại lịch sử, nhưng bạn đã hoàn nguyên về cấu trúc tệp tương tự. Tại sao Git không có một git revert --to <hash>là ngoài tôi.


41
Bạn có thể thực hiện git revert HEAD~3để xóa 3 lần xác nhận gần nhất
Rod

25
@Rod - Không, điều đó không đúng. Lệnh đó sẽ hoàn nguyên cam kết là ông bà thứ ba của HEAD (không phải là ba lần xác nhận cuối cùng).
kflorence

1
@kflorence Ok cảm ơn vì thông tin. Sẽ git revert -n master~3..master~1làm việc? (Như được thấy từ kernel.org/pub/software/scm/git/docs/git-revert.html )
Rod

3
@Rod - Nghe có vẻ đúng, chắc chắn đó là một cú pháp xấu xí phải không? Tôi luôn thấy việc kiểm tra cam kết mà tôi muốn "hoàn nguyên" và sau đó cam kết trực quan hơn.
kflorence

7
Có một dễ dàng hơn nhiều cách để làm điều này ngay bây giờ so với một kịch bản như thế này, chỉ cần sử dụng git revert --no-commit <start>..<end>, bởi vì git revertchấp nhận một loạt in new cam kết (hoặc tất cả?) Phiên bản của Git. Lưu ý rằng bắt đầu của phạm vi không được bao gồm trong hoàn nguyên.

58

Các lựa chọn thay thế khác cho các giải pháp của Jefromi

Các giải pháp của Jefromi chắc chắn là những giải pháp tốt nhất và bạn chắc chắn nên sử dụng chúng. Tuy nhiên, để hoàn thiện, tôi cũng muốn chỉ ra các giải pháp thay thế khác cũng có thể được sử dụng để hoàn nguyên một cam kết (theo nghĩa là bạn tạo ra một cam kết mới hoàn tác các thay đổi trong cam kết trước đó , giống như những gì đã git revertlàm).

Để rõ ràng, những lựa chọn thay thế này không phải là cách tốt nhất để hoàn nguyên các cam kết , các giải pháp của Jefromi , nhưng tôi chỉ muốn chỉ ra rằng bạn cũng có thể sử dụng các phương pháp khác này để đạt được điều tương tự git revert.

Phương án 1: Đặt lại cứng và mềm

Đây là phiên bản sửa đổi rất ít của giải pháp Charles Bailey để Hoàn nguyên cam kết bằng hàm băm SHA trong Git? :

# Reset the index to the desired commit
git reset --hard <commit>

# Move the branch pointer back to the previous HEAD
git reset --soft HEAD@{1}

# Commit the changes
git commit -m "Revert to <commit>"

Điều này về cơ bản hoạt động bằng cách sử dụng thực tế là các thiết lập lại mềm sẽ rời khỏi trạng thái của cam kết trước đó được đặt trong khu vực chỉ mục / khu vực mà bạn có thể cam kết.

Phương án 2: Xóa cây hiện tại và thay thế bằng cây mới

Giải pháp này xuất phát từ giải pháp của Svick để thanh toán cam kết cũ và biến nó thành cam kết mới :

git rm -r .
git checkout <commit> .
git commit

Tương tự như thay thế # 1, điều này tái tạo trạng thái <commit>trong bản sao làm việc hiện tại. Điều cần thiết là phải làm git rmtrước vì git checkoutsẽ không xóa các tệp đã được thêm vào kể từ đó <commit>.


Về Giải pháp thay thế 1, một câu hỏi nhanh: Bằng cách làm như vậy, chúng ta không bị lạc giữa các cam kết phải không?
Bogac

2
@Bogac - các dấu chấm chỉ đường dẫn tệp, trong trường hợp này là thư mục hiện tại, do đó, giả sử bạn đang chạy nó từ thư mục gốc của bản sao làm việc của bạn.
Tom

Cảnh báo được lặp đi lặp lại nhiều lần trong câu trả lời, nhưng ai đó có thể thêm lý do tại sao đây không phải là cách tốt nhất - được so sánh với một cái gì đó giống như git revert HEAD~2..HEADtừ giải pháp được liên kết của @ Cascabel (@ Jefromi's). Tôi không thấy vấn đề.
Joshua Goldberg

55

Đây là một cách đơn giản hơn nhiều để quay lại cam kết trước đó (và để nó ở trạng thái không được cam kết, thực hiện với bất cứ điều gì bạn thích):

git reset HEAD~1

Vì vậy, không cần id id và như vậy :)


không hoạt động, một git kéo sau khi điều này mang lại: lỗi: Thay đổi cục bộ của bạn đối với các tệp sau đây sẽ bị ghi đè bằng cách hợp nhất:
malhal

1
@malhal Đó là vì bạn đã có những thay đổi không được cam kết. Stash / thiết lập lại chúng và sau đó nó sẽ hoạt động mà không có lỗi đó.
Paul Walczewski

39

Có một lệnh (không phải là một phần của lõi Git, nhưng nó nằm trong gói git-bổ sung ) đặc biệt để hoàn nguyên và dàn dựng các cam kết cũ:

git back

Trên trang man , nó cũng có thể được sử dụng như sau:

# Remove the latest three commits
git back 3

38

Cách tốt nhất là:

git reset --hard <commidId> && git push --force

Điều này sẽ đặt lại nhánh thành cam kết cụ thể và sau đó sẽ tải lên máy chủ từ xa với cùng các cam kết như bạn có tại địa phương (điều này sẽ loại bỏ hoàn toàn các lệnh sau cam kết cụ thể đó)

Hãy cẩn thận với --forcecờ sẽ xóa tất cả các lần xác nhận tiếp theo sau khi xác nhận đã chọn mà không có tùy chọn khôi phục chúng.


3
Làm việc như quyến rũ!
Gaurav Gupta

Tôi đã bỏ qua vì tôi không biết câu trả lời của bạn cung cấp bất kỳ thông tin mới nào chưa được cung cấp trong ví dụ stackoverflow.com/a/37145089/1723886 , stackoverflow.com/a/27438379/1723886 hoặc stackoverflow.com/a/48756719/1723886 . Trong thực tế, hầu hết các cam kết đã đề cập đến git reset - rất nhiều, và nhiều đề cập khác sử dụng --force hoặc -f để đẩy.
Alex Telon

Nó thực hiện công việc chỉ với một lệnh, rõ ràng và đơn giản. Bạn có thể tự do hạ thấp câu trả lời của tôi nếu bạn không thích nó.
david.t_92

1
Một tùy chọn khác để xem xét cho tương lai là đề xuất chỉnh sửa cho câu trả lời trước đó hoặc thêm nhận xét nói rằng 'điều này cũng có thể được thực hiện trong một dòng bằng cách sử dụng &&' chẳng hạn. Bằng cách đó mọi người có thể thấy câu trả lời được cải thiện ở một nơi.
Alex Telon

36

Sau tất cả các thay đổi, khi bạn đẩy tất cả các lệnh này, bạn có thể phải sử dụng:

git push -f ...

Và không chỉ git push.


15
Cảnh báo bắt buộc: không làm điều này nếu bạn chia sẻ chi nhánh của mình với những người khác có bản sao của các cam kết cũ, bởi vì sử dụng lực đẩy như thế này sẽ buộc họ phải đồng bộ hóa lại công việc của họ. Để biết giải pháp giải thích chi tiết cách hoàn nguyên cam kết một cách an toàn mà không mất công với lực đẩy, hãy xem câu trả lời này .

3
Đôi khi đây là những gì bạn muốn. Ví dụ: đã cam kết và đẩy một số cam kết vào nhánh sai (nhánh A). Sau khi chọn cherry cho nhánh B, tôi muốn xóa các cam kết này khỏi nhánh A. Tôi không muốn hoàn nguyên, vì sau đó hoàn nguyên sẽ được áp dụng khi nhánh A và B được hợp nhất với nhau. Thực hiện thiết lập lại - cho phép <commitId> trong nhánh A, sau đó là một lực đẩy sẽ loại bỏ các cam kết này khỏi nhánh trong khi bảo quản chúng ở nhánh B. Tôi có thể thoát khỏi điều này vì tôi biết không ai khác đang phát triển trên nhánh A.
Doug R

Cảm ơn! Tôi không thể tìm ra làm thế nào để có được chi nhánh từ xa phù hợp với chi nhánh địa phương của tôi, Chỉ cần thực hiện một cú đẩy mạnh.
Mido

32

Bạn có thể tự hoàn thành tất cả các bước ban đầu này và đẩy trở lại kho Git.

  1. Kéo phiên bản mới nhất của kho lưu trữ của bạn từ Bitbucket bằng git pull --alllệnh.

  2. Chạy lệnh đăng nhập Git với -n 4từ thiết bị đầu cuối của bạn. Số sau khi -nxác định số lần xác nhận trong nhật ký bắt đầu từ lần xác nhận gần đây nhất trong lịch sử địa phương của bạn.

    $ git log -n 4
    
  3. Đặt lại phần đầu lịch sử của kho lưu trữ của bạn bằng cách sử dụng git reset --hard HEAD~NN là số lần xác nhận bạn muốn lấy lại phần đầu. Trong ví dụ sau, phần đầu sẽ được đặt lại một lần xác nhận, đến lần xác nhận cuối cùng trong lịch sử kho lưu trữ:

  4. Đẩy thay đổi vào kho Git bằng cách sử dụng git push --forceđể đẩy thay đổi.

Nếu bạn muốn kho Git đến một cam kết trước đó:

git pull --all
git reset --hard HEAD~1
git push --force


28

Chọn cam kết yêu cầu của bạn và kiểm tra nó bằng cách

git show HEAD
git show HEAD~1
git show HEAD~2 

cho đến khi bạn nhận được cam kết yêu cầu. Để làm cho điểm chính đến đó, làm

git reset --hard HEAD~1

hoặc git reset --hard HEAD~2bất cứ điều gì.


7
Cảnh báo bắt buộc: không làm điều này nếu bạn chia sẻ chi nhánh của mình với những người khác có bản sao của các cam kết cũ, bởi vì sử dụng thiết lập lại cứng như thế này sẽ buộc họ phải đồng bộ hóa lại công việc của họ với chi nhánh mới được đặt lại. Để biết giải pháp giải thích chi tiết cách hoàn nguyên cam kết một cách an toàn mà không mất công với thiết lập lại cứng, hãy xem câu trả lời này .

2
Ngoài ra, để rõ ràng, git show HEADtương đương với chỉ sử dụng git log HEAD -1.

25

Nếu tình huống là một vấn đề khẩn cấp và bạn chỉ muốn làm những gì người hỏi đã hỏi một cách nhanh chóng và bẩn thỉu , giả sử dự án của bạn nằm trong một thư mục có tên, ví dụ: "dự án của tôi":


NHANH CHÓNG VÀ TRỰC TIẾP : tùy thuộc vào hoàn cảnh, nhanh và bẩn trên thực tế có thể rất TỐT. Giải pháp của tôi ở đây là KHÔNG thay thế các tệp bạn có trong thư mục làm việc của bạn bằng các tệp được kéo lên / trích xuất từ ​​độ sâu của kho git ẩn bên dưới thư mục .git / của bạn bằng cách sử dụng các lệnh git thông minh và mạnh mẽ của bệnh nhân, trong đó có các lệnh git thông minh và mạnh mẽ. nhiều BẠN KHÔNG PHẢI LÀM B DING CHÂN DUNG BIỂN ĐỂ PHỤC HỒI những gì có vẻ là một tình huống tai hại, và cố gắng làm điều đó mà không có đủ chuyên môn có thể gây tử vong .


  1. Sao chép toàn bộ thư mục và gọi nó là một cái gì đó khác, như "dự án của tôi - sao chép". Giả sử các tệp kho git ("repo") của bạn nằm trong thư mục "dự án của tôi" (vị trí mặc định cho chúng, trong thư mục có tên ".git"), giờ đây bạn sẽ sao chép cả tệp công việc và tệp repo của mình.

  2. Làm điều này trong thư mục "dự án của tôi":

    .../my project $ git reset --hard [first-4-letters&numbers-of-commit's-SHA]
    

Điều này sẽ trả lại trạng thái của repo trong "dự án của tôi" về trạng thái của nó khi bạn thực hiện cam kết đó ("cam kết" có nghĩa là ảnh chụp nhanh các tệp làm việc của bạn). Tất cả các cam kết kể từ đó sẽ bị mất vĩnh viễn trong "dự án của tôi", NHƯNG ... chúng sẽ vẫn có mặt trong repo dưới "dự án của tôi - sao chép" vì bạn đã sao chép tất cả các tệp đó - kể cả các tệp trong ... /. Git /.

Sau đó, bạn có hai phiên bản trên hệ thống của mình ... bạn có thể kiểm tra hoặc sao chép hoặc sửa đổi các tệp quan tâm hoặc bất cứ điều gì từ cam kết trước đó. Bạn hoàn toàn có thể loại bỏ các tệp trong "dự án của tôi - sao chép", nếu bạn đã quyết định công việc mới kể từ khi cam kết được khôi phục không đi đến đâu ...

Điều rõ ràng nếu bạn muốn tiếp tục với trạng thái của dự án mà không thực sự loại bỏ công việc vì cam kết đã lấy này là đổi tên thư mục của bạn một lần nữa: Xóa dự án có chứa cam kết đã lấy (hoặc đặt tên tạm thời) và đổi tên của bạn " dự án của tôi - sao chép "thư mục trở lại" dự án của tôi ". Sau đó, có thể cố gắng để hiểu một số câu trả lời khác ở đây, và có thể thực hiện một cam kết khác khá sớm.

Git là một sáng tạo tuyệt vời nhưng hoàn toàn không ai có thể chỉ "nhặt nó lên một cách nhanh chóng": những người cố gắng giải thích nó quá thường xuyên thừa nhận kiến ​​thức trước đây về [Hệ thống kiểm soát phiên bản] khác và đi sâu quá xa quá sớm và thực hiện các tội ác khác, như sử dụng các thuật ngữ có thể hoán đổi cho nhau để "kiểm tra" - theo những cách đôi khi xuất hiện gần như được tính toán để gây nhầm lẫn cho người mới bắt đầu.

Để giảm bớt căng thẳng cho bản thân, hãy học hỏi từ những vết sẹo của tôi. Bạn phải đọc khá nhiều sách trên Git - Tôi khuyên bạn nên "Kiểm soát phiên bản với Git" . Làm điều đó sớm hơn là sau này. Nếu bạn làm như vậy, hãy nhớ rằng phần lớn sự phức tạp của Git đến từ việc phân nhánh và sau đó làm lại: bạn có thể bỏ qua những phần đó trong bất kỳ cuốn sách nào. Từ câu hỏi của bạn, không có lý do tại sao mọi người nên làm mờ mắt bạn với khoa học .

Đặc biệt, nếu, ví dụ, đây là một tình huống tuyệt vọng và bạn là người mới với Git!

PS: Một suy nghĩ khác: Hiện tại thực sự khá đơn giản để giữ repo Git trong một thư mục khác với thư mục có các tệp đang hoạt động. Điều này có nghĩa là bạn sẽ không phải sao chép toàn bộ kho Git bằng giải pháp nhanh & bẩn ở trên. Xem câu trả lời của Fasher bằng cách sử dụng --separate-git-dir ở đây . Mặc dù được cảnh báo : Nếu bạn có kho lưu trữ "thư mục riêng" mà bạn không sao chép và bạn thực hiện thiết lập lại cứng, tất cả các phiên bản sau cam kết đặt lại sẽ bị mất vĩnh viễn, trừ khi bạn hoàn toàn nên, như bạn hoàn toàn nên, thường xuyên sao lưu kho lưu trữ của bạn, tốt nhất là vào Đám mây (ví dụ: Google Drive ) trong số những nơi khác.

Với chủ đề "sao lưu lên đám mây" này, bước tiếp theo là mở một tài khoản (tất nhiên là miễn phí) với GitHub hoặc (tốt hơn theo quan điểm của tôi) GitLab . Sau đó, bạn có thể thường xuyên thực hiện một git pushlệnh để làm cho Cloud repo cập nhật "đúng cách". Nhưng một lần nữa, nói về điều này có thể là quá nhiều quá sớm.


23

Đây là một cách nữa để đặt lại trực tiếp cho một cam kết gần đây

git stash
git stash clear

Nó trực tiếp xóa tất cả các thay đổi mà bạn đã thực hiện kể từ lần cam kết cuối cùng.

PS: Nó có một vấn đề nhỏ; nó cũng xóa tất cả những thay đổi của bạn được lưu trữ gần đây. Mà tôi đoán trong hầu hết các trường hợp không nên quan trọng.


LƯU Ý: Các tệp mới không được thêm vào trong chỉ mục sẽ không được lưu. Bạn đã thêm chúng hoặc xóa chúng bằng tay.
andreyro

Tại sao oh tại sao xóa stash? Ngoài việc là một giải pháp không, điều này thực sự có hại. Đọc câu đầu tiên của câu hỏi ngay lập tức làm mất hiệu lực giải pháp stash (CHỈ có thể hữu ích để đặt lại về cam kết LAST).
RomainValeri

22

Để làm sạch hoàn toàn thư mục của coder khỏi một số thay đổi ngẫu nhiên, chúng tôi đã sử dụng:

git add -A .
git reset --hard HEAD

Chỉ cần git reset --hard HEADloại bỏ các sửa đổi, nhưng nó sẽ không thoát khỏi các tệp "mới". Trong trường hợp của họ, họ đã vô tình kéo một thư mục quan trọng ở đâu đó một cách ngẫu nhiên và tất cả các tệp đó đang được Git coi là mới, vì vậy reset --hardđã không sửa nó. Bằng cách chạy git add -A .trước, nó rõ ràng đã theo dõi tất cả chúng bằng git, để xóa sạch bằng cách thiết lập lại.


21

Để giữ các thay đổi từ cam kết trước đó thành CHÍNH và chuyển sang cam kết trước đó, hãy làm:

git reset <SHA>

Nếu các thay đổi không bắt buộc từ cam kết trước đó với CHÍNH và chỉ cần loại bỏ tất cả các thay đổi, hãy làm:

git reset --hard <SHA>

20

Tôi tin rằng một số người có thể đi đến câu hỏi này muốn biết làm thế nào để phục hồi những thay đổi đã cam kết mà họ đã thực hiện trong chủ của mình - tức là vứt bỏ mọi thứ và quay trở lại nguồn gốc / chủ, trong trường hợp này, hãy làm điều này:

git reset --hard origin/master

/superuser/273172/how-to-reset-master-to-origin-master


18

Hoàn nguyên là lệnh để khôi phục các cam kết.

git revert <commit1> <commit2> 

Mẫu vật:

git revert 2h3h23233

Nó có khả năng lấy phạm vi từ ĐẦU như bên dưới. Ở đây 1 nói "hoàn nguyên cam kết cuối cùng."

git revert HEAD~1..HEAD

và sau đó làm git push


14

Hãy thử đặt lại cam kết mong muốn -

git reset <COMMIT_ID>

(để kiểm tra việc sử dụng COMMIT_ID git log)

Điều này sẽ thiết lập lại tất cả các tập tin thay đổi về trạng thái chưa thêm.

Bây giờ bạn có thể checkouttất cả các tệp chưa được thêm bằng cách

git checkout .

Kiểm tra git logđể xác minh thay đổi của bạn.

CẬP NHẬT

Nếu bạn có một và chỉ cam kết trong repo của mình, hãy thử

git update-ref -d HEAD


13

Khi các cam kết của bạn được đẩy từ xa, bạn cần xóa chúng. Hãy để tôi giả sử chi nhánh của bạn đang phát triển và nó được đẩy qua nguồn gốc .

Trước tiên bạn cần xóa phát triển từ nguồn gốc :

git push origin :develop (note the colon)

Sau đó, bạn cần phát triển đến trạng thái bạn muốn, hãy để tôi giả sử hàm băm cam kết là EFGHIJK:

git reset --hard EFGHIJK

Cuối cùng, đẩy phát triển trở lại:

git push origin develop

13

Chú ý! Lệnh này có thể gây mất lịch sử cam kết, nếu người dùng đặt nhầm cam kết sai. Luôn luôn có thêm bản sao lưu của git của bạn một số nơi khác chỉ trong trường hợp nếu bạn làm sai, hơn là bạn an toàn hơn một chút. :)

Tôi đã có một vấn đề tương tự và muốn trở lại một cam kết trước đó. Trong trường hợp của tôi, tôi không muốn giữ cam kết mới hơn, do đó tôi đã sử dụng Hard.

Đây là cách tôi đã làm nó:

git reset --hard CommitId && git clean -f

Điều này sẽ trở lại trên kho lưu trữ cục bộ và ở đây sau khi sử dụng git push -fsẽ cập nhật kho lưu trữ từ xa.

git push -f

13

Trong GitKraken bạn có thể làm điều này:

  1. Nhấp chuột phải vào cam kết mà bạn muốn đặt lại, chọn: Đặt lại cho cam kết này / Cứng :

    Nhập mô tả hình ảnh ở đây

  2. Nhấp chuột phải vào cam kết một lần nữa, chọn: Tên chi nhánh hiện tại / Đẩy :

    Nhập mô tả hình ảnh ở đây

  3. Nhấp vào Lực đẩy :

    Nhập mô tả hình ảnh ở đây

Quan sát. : Bạn cần cẩn thận, vì tất cả lịch sử cam kết sau khi thiết lập lại cứng bị mất và hành động này là không thể đảo ngược. Bạn cần chắc chắn những gì bạn làm.


11

Nếu bạn muốn sửa một số lỗi trong lần xác nhận cuối cùng, một sự thay thế tốt sẽ là sử dụng lệnh git commit --amend . Nếu cam kết cuối cùng không được chỉ ra bởi bất kỳ tham chiếu nào, thì điều này sẽ thực hiện thủ thuật, vì nó tạo ra một cam kết có cùng cha mẹ như cam kết cuối cùng. Nếu không có tham chiếu đến lần xác nhận cuối cùng, đơn giản là nó sẽ bị loại bỏ và cam kết này sẽ là lần cam kết cuối cùng. Đây là một cách tốt để sửa lỗi cam kết mà không hoàn nguyên các cam kết. Tuy nhiên nó có những hạn chế riêng của nó.

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.