Làm thế nào để tôi không hoàn nguyên lại một cam kết Git được hoàn nguyên?


486

Đưa ra một thay đổi đã được cam kết sử dụng commit, và sau đó được hoàn nguyên bằng cách sử dụng revert, cách tốt nhất để hoàn tác lại đó là gì?

Lý tưởng nhất, điều này nên được thực hiện với một cam kết mới, để không viết lại lịch sử.



@phuclv một bình luận cho có thể trùng lặp ở đó, điểm ở đây. Một phải được đánh dấu ban đầu và một phải được đánh dấu song công
John Demetriou

@JohnDemetriou không có câu hỏi nào có bộ câu trả lời tốt hơn nên được giữ mở. Thời gian không liên quan ở đây Làm thế nào các câu hỏi trùng lặp nên được xử lý?
phuclv

Tôi đã không nói về thời gian. Tôi chỉ tình cờ bình luận về một trong hai về vấn đề này
John Demetriou

Câu trả lời:


385

Nếu bạn chưa thúc đẩy sự thay đổi đó, git reset --hard HEAD^

Nếu không, hoàn nguyên hoàn nguyên là hoàn toàn tốt.

Một cách khác là git checkout HEAD^^ -- .và sau đó git add -A && git commit.


8
Lưu ý rằng nếu bạn muốn hủy hoàn nguyên mà không áp dụng ngay các thay đổi ban đầu cho nhánh chính, bạn có thể (1) khôi phục nhánh ban đầu nếu bị xóa, (2) nhấp vào "hoàn nguyên" trên nhánh hoàn nguyên như Adam đã lưu ý, sau đó ( 3) nhấp vào "chỉnh sửa" trong tiêu đề của PR kết quả và thay đổi nhánh đích thành nhánh ban đầu thay vì chính. Bây giờ chi nhánh ban đầu của bạn có thể được hợp nhất lại để thực hiện các thay đổi được hoàn nguyên trước đó.
pauljm

1
Xin lưu ý điều này sẽ loại bỏ tất cả các thay đổi trong cây và chỉ mục làm việc. Sử dụng git stash để lưu bất kỳ thay đổi nào bạn không muốn mất.
zpon

3
Lợi thế của phương thức thanh toán && cam kết là gì? Có vẻ như trong trường hợp chung git cherry-pickhoặc cách khác git revertlà những cách đơn giản nhất để hoàn nguyên một hoàn nguyên.
Robert Jack Will

5
Tôi nghĩ sẽ hữu ích khi chỉ ra cách: Otherwise, reverting the revert is perfectly fine.và sau đó cũng để giải thích những gì git checkout HEAD^^ -- .đang làm.
Todd

3
Chúa ơi, đừng sử dụng git add -A... trừ khi bạn muốn thêm mọi tập tin vào kiểm soát phiên bản, rất có thể không phải là điều bạn muốn.
Kaitain

474

git cherry-pick <original commit sha>
Sẽ tạo một bản sao của cam kết ban đầu, về cơ bản áp dụng lại cam kết

Hoàn nguyên hoàn nguyên sẽ làm điều tương tự, với một thông điệp cam kết lộn xộn hơn:
git revert <commit sha of the revert>

Một trong những cách này sẽ cho phép bạn git pushmà không ghi đè lên lịch sử, bởi vì nó tạo ra một cam kết mới sau khi hoàn nguyên.
Khi nhập sha xác nhận, thông thường bạn chỉ cần 5 hoặc 6 ký tự đầu tiên:
git cherry-pick 6bfabc


61
Đây dễ dàng là giải pháp thanh lịch và đầy đủ nhất cho câu hỏi của OP. Tốt hơn nhiều so với câu trả lời được chấp nhận với các giả định của nó về cam kết hoàn nguyên nằm ở đầu của cây cam kết. Các op cũng đặc biệt yêu cầu một giải pháp không viết lại lịch sử nên giải pháp cứng được đưa ra trong câu trả lời được chấp nhận đơn giản là sai.
Timo

6
@Timo Để làm rõ, câu trả lời được chấp nhận chứa giải pháp tôi đã sử dụng ("hoàn nguyên hoàn nguyên") và đã được đăng trong vòng vài giờ câu hỏi của tôi - sớm hơn 3 năm so với câu trả lời này. Do đó, dấu kiểm.
JimmidyJoo

6
@JimmidyJoo Tôi biết tôi đã trễ 3 năm, tôi chỉ muốn mọi người đến đây từ một tìm kiếm google để xem câu trả lời tốt hơn
Stephan

2
@Stephan Yep, nhưng một lần nữa, để làm rõ - đó là một câu trả lời tốt hơn không chỉ là câu tôi đã sử dụng để giải quyết vấn đề của mình. Tôi chỉ đơn giản là nhận xét về lý do tại sao dấu kiểm là ở đâu. Câu hỏi cho tất cả: liệu quy ước SO có ra lệnh rằng tôi nên "duy trì" các câu hỏi trước đây của mình và gán lại dấu kiểm khi có câu trả lời mới không?
JimmidyJoo

3
@JimmidyJoo Tôi không có ý tưởng về hội nghị SO. Nhưng là một người tìm kiếm google, tôi thực sự thích xem câu trả lời tốt hơn được kiểm tra. Và sẽ đánh giá cao những nỗ lực của bất cứ ai để duy trì các câu hỏi trong quá khứ của họ.
Paiman Roointan

29

Một cam kết hoàn nguyên cũng giống như bất kỳ cam kết nào khác trong git. Có nghĩa là, bạn có thể hoàn nguyên nó, như trong:

git revert 648d7d808bc1bca6dbf72d93bf3da7c65a9bd746

Điều đó rõ ràng chỉ có ý nghĩa một khi các thay đổi được đẩy, và đặc biệt là khi bạn không thể buộc đẩy vào nhánh đích (đó là một ý tưởng tốt cho nhánh chính của bạn ). Nếu thay đổi chưa được đẩy, chỉ cần thực hiện chọn cherry, hoàn nguyên hoặc đơn giản là xóa cam kết hoàn nguyên theo các bài đăng khác.

Trong nhóm của chúng tôi, chúng tôi có một quy tắc sử dụng hoàn nguyên trên các cam kết Revert đã được cam kết trong nhánh chính, chủ yếu là để giữ cho lịch sử sạch sẽ, để bạn có thể thấy cam kết nào hoàn nguyên những gì:

      7963f4b2a9d   Revert "Revert "OD-9033 parallel reporting configuration"
      "This reverts commit a0e5e86d3b66cf206ae98a9c989f649eeba7965f.
                    ...
     a0e5e86d3b6    Revert "OD-9055 paralel reporting configuration"
     This reverts commit 648d7d808bc1bca6dbf72d93bf3da7c65a9bd746.
                ...
     Merge pull request parallel_reporting_dbs to master* commit 
    '648d7d808bc1bca6dbf72d93bf3da7c65a9bd746'

Bằng cách này, bạn có thể theo dõi lịch sử và tìm ra toàn bộ câu chuyện, và ngay cả những người không có kiến ​​thức về di sản cũng có thể tự mình giải quyết. Trong khi đó, nếu bạn chọn thứ anh đào hoặc rebase , thông tin có giá trị này sẽ bị mất (trừ khi bạn đưa nó vào bình luận).

Rõ ràng, nếu một cam kết được hoàn nguyên và hoàn nguyên lại nhiều lần, điều đó trở nên khá lộn xộn.


14

Hoàn nguyên hoàn nguyên sẽ thực hiện các mẹo

Ví dụ,

Nếu abcdeflà cam kết của bạn và ghijkllà cam kết bạn có khi hoàn nguyên cam kết abcdef, thì hãy chạy:

git revert ghijkl

Điều này sẽ hoàn nguyên hoàn nguyên


4

Nó có vẻ ngu ngốc đối với tôi. Nhưng tôi đã ở trong tình huống tương tự và tôi đã hoàn nguyên các cam kết được hoàn nguyên. Tôi đã hoàn nguyên số vì vậy tôi phải hoàn nguyên cho mỗi 'cam kết hoàn nguyên'.

Bây giờ lịch sử cam kết của tôi trông hơi kỳ lạ một chút.

lịch sử kỳ lạ

Đây là một dự án thú cưng nên không sao. Nhưng đối với dự án thực tế, tôi sẽ ưu tiên thực hiện cam kết cuối cùng trước khi hoàn nguyên khôi phục tất cả mã được hoàn nguyên cùng nhau trong một cam kết và nhận xét hợp lý hơn.


4
Bạn có thể quan tâm đến câu trả lời cho câu hỏi khác của tôi: stackoverflow.com/questions/10415565/iêu
JimmidyJoo

2
Bạn có thể tránh cam kết tự động và chọn bạn cam kết tin nhắn bằng cách sử dụng cờ --no-commit khi hoàn nguyên và sau đó cam kết với thông báo có liên quan
David Barda

Ngoài ra, bạn có thể chỉ định nhiều id id khi sử dụng git Revert (chúng phải theo đúng thứ tự tôi nghĩ).
Aalex Gabi

Tôi có thể làm điều đó từ máy tính để bàn github?
Guillaume F.

4

Đây là cách tôi đã làm:
Nếu chi nhánh my_branchnameđược bao gồm trong một hợp nhất đã được hoàn nguyên. Và tôi muốn hủy bỏ my_branchname:

Trước tiên tôi làm một git checkout -b my_new_branchnametừ my_branchname.
Sau đó, tôi làm một git reset --soft $COMMIT_HASHnơi $COMMIT_HASHlà cam kết hash của cam kết ngay trước khi cam kết đầu tiên của my_branchname(xem git log)
sau đó tôi thực hiện một cam kết mới git commit -m "Add back reverted changes"
sau đó tôi đẩy mạnh các chi nhánh mới git push origin new_branchname
sau đó tôi đã thực hiện một yêu cầu kéo cho chi nhánh mới.


Con đường để đi khi có quá nhiều "cherry-pick" để làm. Tôi không hiểu tại sao câu trả lời này có quá ít sự
ủng hộ

2

Hoặc bạn có thể git checkout -b <new-branch>git cherry-pick <commit>trước đó để và git rebasebỏ revertcam kết. gửi yêu cầu kéo như trước đây.


1

Nếu bạn không thích ý tưởng "hoàn nguyên hoàn nguyên" (đặc biệt là khi điều đó có nghĩa là mất thông tin lịch sử cho nhiều lần xác nhận), bạn luôn có thể hướng đến tài liệu git về "Hoàn nguyên hợp nhất bị lỗi" .

Cho tình huống bắt đầu sau đây

 P---o---o---M---x---x---W---x
  \         /
   A---B---C----------------D---E   <-- fixed-up topic branch

(W là hoàn nguyên ban đầu của bạn về hợp nhất M; D và E là các bản sửa lỗi cho nhánh / cam kết tính năng bị hỏng ban đầu của bạn)

Bây giờ bạn có thể chỉ cần phát lại cam kết từ A đến E, để không ai trong số họ "thuộc về" hợp nhất hoàn nguyên:

$ git checkout E
$ git rebase --no-ff P

Bản sao mới của chi nhánh của bạn bây giờ có thể được hợp nhất masterlại:

   A'---B'---C'------------D'---E'  <-- recreated topic branch
  /
 P---o---o---M---x---x---W---x
  \         /
   A---B---C----------------D---E

Ý tưởng tốt và cảm ơn cho các liên kết doc. Chúng tôi thường nổi loạn với chủ trước khi hợp nhất trở lại, nhưng git sau đó dường như nhận ra rằng A ', B', C 'giống như trước đây và bây giờ tôi có D, E sau W ( pastebin ). Gợi ý về cách giải quyết điều này?
Kristoffer Bakkejord

0

Để lấy lại những thay đổi chưa được dàn dựng và dàn dựng được hoàn nguyên sau một cam kết:

git reset HEAD@{1}

Để khôi phục tất cả các thao tác xóa chưa được chỉnh sửa:

git ls-files -d | xargs git checkout --
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.