Làm thế nào để hoàn nguyên một cam kết hợp nhất đã được đẩy đến chi nhánh từ xa?


962

git revert <commit_hash>Một mình sẽ không làm việc. -mphải được chỉ định, và tôi khá bối rối về nó.

Bất cứ ai có kinh nghiệm này trước đây?


3
Hãy xem câu trả lời cho câu hỏi này: stackoverflow.com/questions/2318777/
Kẻ


Liên kết ở đây là ví dụ tốt nhất minh họa cho việc hoàn nguyên cam kết đã hợp nhất: christianengvall.se/undo-pushed-merge-git
SK Venkat

Đây là một ví dụ về nơi thiết kế gitkhông phù hợp với git-flowquy trình công việc mà mọi người sử dụng. Nếu bạn đã developkiểm tra, tất nhiên bạn muốn hoàn nguyên nhánh tính năng 2 cam kết đã giới thiệu một lỗi và không phải là nhánh dev được chia sẻ lâu năm. Cảm thấy vô lý cần phải chọn nó với -m 1.
pkamb

2
Chỉ là một đề nghị khác chưa từng xảy ra với tôi trước đây - nếu một trong những danh sách cam kết của chi nhánh nhỏ, bạn có thể cảm thấy thoải mái hơn khi hoàn trả các cam kết riêng lẻ thay vì toàn bộ chi nhánh.
Sridhar Sarnobat

Câu trả lời:


1153

Các -mtùy chọn xác định số cha mẹ . Điều này là do một cam kết hợp nhất có nhiều hơn một cha mẹ và Git không tự động biết cha mẹ nào là dòng chính và cha mẹ nào là nhánh bạn muốn hủy hợp nhất.

Khi bạn xem một cam kết hợp nhất trong đầu ra của git log, bạn sẽ thấy cha mẹ của nó được liệt kê trên dòng bắt đầu bằng Merge:

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: 8989ee0 7c6b236
Author: Ben James <ben@example.com>
Date:   Wed Aug 17 22:49:41 2011 +0100

Merge branch 'gh-pages'

Conflicts:
    README

Trong tình huống này, git revert 8f937c6 -m 1bạn sẽ nhận được cây như lúc ban đầu 8989ee0git revert -m 2sẽ khôi phục lại cây như lúc ban đầu 7c6b236.

Để hiểu rõ hơn về ID gốc, bạn có thể chạy:

git log 8989ee0 

git log 7c6b236

127
từ hai con số 8989ee0, 7c6b236, cái nào để đi. Làm sao tôi hiểu được?
Arup Rakshit

12
Sau khi hoàn nguyên, tôi không nghĩ người ta sẽ có thể dễ dàng sửa mã trong nhánh nguồn và hợp nhất lại? kernel.org/pub/software/scm/git/docs/howto/
Kẻ

10
Trong khi tìm kiếm một lời giải thích tốt hơn, tôi đã tìm thấy bài viết này mà tôi nghĩ rằng đã làm rất tốt khi xem qua các chi tiết. Tôi phát hiện ra sau khi đọc rằng thứ tôi thực sự tìm kiếm là lệnh RESET, sau đó là một lực đẩy. Có lẽ nó sẽ giúp người khác. atlassian.com/git/tutorials/ từ
Funktr0n

46
@ArupRakshit nếu bạn chạy git log 8989ee0git log 7c6b236, bạn nên biết câu trả lời.
BMW

4
git log --merges để xem tất cả các sự hợp nhất và git log --no-sáp nhập để xem lịch sử mà không cần sáp nhập. Hợp nhất một chi nhánh mang lại lịch sử chi nhánh được sáp nhập vào mục tiêu và làm cho nó khó thực hiện bằng cách sử dụng nhật ký git đơn giản
Alex Punnen

370

Đây là một ví dụ hoàn chỉnh với hy vọng nó sẽ giúp được ai đó:

git revert -m 1 <commit-hash> 
git push -u origin master

Trường hợp <commit-hash>băm cam kết của hợp nhất mà bạn muốn hoàn nguyên và như đã nêu trong phần giải thích của câu trả lời này , -m 1cho biết rằng bạn muốn hoàn nguyên về cây của cha mẹ đầu tiên trước khi hợp nhất.

Các git revert ...dòng cơ bản cam kết thay đổi của bạn trong khi dòng thứ hai làm cho thay đổi của bạn nào bằng cách đẩy họ đến chi nhánh từ xa.


21
Tôi tin rằng git revertlệnh đã cam kết đối tượng cam kết được tạo. Để điều đó không xảy ra, bạn sẽ phải nhập --no-commitcờ
Delfic

2
Như @Delfic đã đề cập, cam kết đã được quản lý bởi dòng đầu tiên (tôi cần một: wq để xác thực nó) vì vậy dòng thứ hai là không cần thiết.
eka808

1
Điều này thật khó hiểu. chỉ có 2 dòng và không có cam kết git .. ai đó có thể vui lòng chỉnh sửa.
Jay ngẫu nhiên

176

Ben đã nói với bạn cách hoàn nguyên một cam kết hợp nhất, nhưng điều rất quan trọng là bạn nhận ra rằng khi làm như vậy

" ... tuyên bố rằng bạn sẽ không bao giờ muốn cây những thay đổi mang lại bởi việc hợp nhất. Kết quả là, hòa trộn sau này sẽ chỉ mang lại những thay đổi cây được giới thiệu bởi các cam kết mà không phải là tổ tiên của việc hợp nhất quay trở trước đó. Điều này có thể hoặc không thể những gì bạn muốn. " (trang git-merge man) .

Một tin nhắn danh sách bài viết / thư được liên kết từ trang man chi tiết các cơ chế và cân nhắc có liên quan. Chỉ cần đảm bảo rằng bạn hiểu rằng nếu bạn hoàn nguyên cam kết hợp nhất, bạn không thể hợp nhất lại chi nhánh sau đó và mong đợi những thay đổi tương tự sẽ quay trở lại.


81
Nhưng bạn có thể hoàn nguyên hoàn nguyên để lấy lại chúng nếu thực sự cần thiết.
dinois

5
Cảm ơn. Rất hữu ích để biết như trường hợp sử dụng để hoàn tác hợp nhất - do lỗi, giả sử - và sau đó hợp nhất lại toàn bộ chi nhánh một khi lỗi được sửa, là một lỗi phổ biến.
Hợp phần 10

3
Nếu bạn giống như tôi và sau này muốn hợp nhất, bạn có thể hoàn nguyên lại hoặc anh đào chọn thay đổi mà bạn đã hoàn nguyên.
UnitasBrooks

Trong tình huống của tôi, tôi nhấn vào việc phải 'hoàn nguyên hoàn nguyên' để có được sự thay đổi của tôi. Chọn anh đào có thể là một cách gọn gàng hơn? Tôi sẽ thử điều đó vào lần tới ...
Steven Anderson

79

Bạn có thể làm theo các bước sau để hoàn nguyên (các) cam kết không chính xác hoặc đặt lại nhánh từ xa của bạn trở lại chính xác / trạng thái.

  1. kiểm tra các chi nhánh từ xa để repo địa phương.
    git checkout development
  2. sao chép hàm băm xác nhận (tức là id của xác nhận ngay trước khi xác nhận sai) từ nhật ký git git log -n5

    đầu ra:

    cam kết 7cd42475d6f95f5896b6f02e902efab0b70e8038 "chi nhánh Merge 'sai phạm' thành 'phát triển'"
    cam kết f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "đây là một sai phạm"
    cam kết 3779ab50e72908da92d2cfcd72256d7a09f446ba "đây là đúng cam kết"

  3. đặt lại nhánh thành hàm băm cam kết được sao chép ở bước trước
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. chạy git statusđể hiển thị tất cả các thay đổi là một phần của cam kết sai.
  5. chỉ cần chạy git reset --hardđể hoàn nguyên tất cả những thay đổi.
  6. buộc đẩy chi nhánh địa phương của bạn đến điều khiển từ xa và lưu ý rằng lịch sử cam kết của bạn sạch sẽ như trước khi nó bị ô nhiễm.
    git push -f origin development

4
Điều gì xảy ra nếu trong lúc đó, 20 nhà phát triển đã hợp nhất dev mới nhất?
Ewoks

2
Tôi sẽ không ép buộc một nhánh phát triển khi có 20 nhà phát triển trong nhóm sử dụng nhánh đó. :) Trong trường hợp đó, thật khôn ngoan khi chỉ thực hiện một cam kết hoàn nguyên.
ssasi

4
Đây là một giải pháp rất tốt khi bạn đang làm việc một mình hoặc bạn chắc chắn không có nhà phát triển nào khác thực hiện các cam kết mà bạn đã làm hỏng
Kyle B


30

Để giữ nhật ký sạch như không có gì xảy ra (với một số nhược điểm với phương pháp này (do đẩy -f)):

git checkout <branch>
git reset --hard <commit-hash-before-merge>
git push -f origin HEAD:<remote-branch>

'commit-hash-before-merge' xuất phát từ nhật ký (git log) sau khi hợp nhất.


Gợi ý: Nếu bạn đang làm điều này tại công ty của bạn, bạn có thể không được phép.
eneski

3
không bao giờ thực hiện một push -frepo được chia sẻ
Baptiste Mille-Mathias

17

Đôi khi cách hiệu quả nhất để quay trở lại là lùi lại và thay thế.

git log

Sử dụng hàm băm cam kết thứ 2 (hàm băm đầy đủ, hàm băm bạn muốn hoàn nguyên trở lại, trước khi lỗi được liệt kê) và sau đó chuyển lại từ đó.

git checkout -b newbranch <HASH>

Sau đó xóa chi nhánh cũ, sao chép newbranch ở vị trí của nó và khởi động lại từ đó.

git branch -D oldbranch
git checkout -b oldbranch newbranch

Nếu nó được phát sóng, sau đó xóa nhánh cũ khỏi tất cả các kho lưu trữ, đẩy nhánh làm lại đến trung tâm nhất và kéo nó xuống tất cả.


4
Cảnh báo về việc phát sóng nên thực sự rõ ràng hơn về ý tưởng này kinh khủng đến mức nào. Điều này sẽ làm hỏng phiên bản của nhánh đó của mọi người và chỉ thực sự hữu ích nếu bạn làm việc với kho lưu trữ từ xa (github / bitbucket) mà chỉ bạn mới có quyền truy cập.
RobbyD

Không tệ như đẩy các tập tin cấu hình sửa đổi xuống dòng để sản xuất. Nó sẽ không bị hỏng, nó chỉ là một cuộc tấn công từ một cam kết trước đó, vì vậy đây là cách để chuyển con trỏ nhánh sang phiên bản cũ hơn. Hy vọng rằng nó chỉ tác động đến kho lưu trữ cục bộ
ppostma1

5

Nếu bạn muốn hoàn nguyên một mergecam kết, đây là những gì bạn phải làm.

  1. Trước tiên, hãy kiểm tra git logđể tìm id xác nhận hợp nhất của bạn. Bạn cũng sẽ tìm thấy nhiều id cha liên kết với hợp nhất (xem hình ảnh bên dưới).

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

Lưu ý id xác nhận hợp nhất hiển thị màu vàng. ID cha là những cái được viết trong dòng tiếp theo là Merge: parent1 parent2. Hiện nay...

Truyện ngắn:

  1. Chuyển sang chi nhánh mà việc hợp nhất đã được thực hiện. Sau đó, chỉ cần làm điều git revert <merge commit id> -m 1đó sẽ mở một vigiao diện điều khiển để nhập thông điệp cam kết. Viết, lưu, thoát, thực hiện!

Câu chuyện dài:

  1. Chuyển sang chi nhánh mà việc hợp nhất đã được thực hiện. Trong trường hợp của tôi, đó là testchi nhánh và tôi đang cố gắng loại bỏ feature/analytics-v3chi nhánh khỏi nó.

  2. git revertlà lệnh hoàn nguyên bất kỳ cam kết nào. Nhưng có một mẹo khó chịu khi hoàn nguyên một mergecam kết. Bạn cần nhập -mcờ nếu không nó sẽ thất bại. Từ đây trở đi, bạn cần quyết định xem bạn muốn hoàn nguyên chi nhánh của mình và làm cho nó trông giống như chính xác nó đã được bật parent1hoặc parent2thông qua:

git revert <merge commit id> -m 1(trở lại parent2)

git revert <merge commit id> -m 2(trở lại parent1)

Bạn có thể git log các phụ huynh này để tìm ra con đường bạn muốn đi và đó là gốc rễ của tất cả sự nhầm lẫn.


5

Tất cả các câu trả lời đã bao gồm hầu hết mọi thứ nhưng tôi sẽ thêm 5 xu của mình. Nói ngắn gọn, một cam kết hợp nhất khá đơn giản:

git revert -m 1 <commit-hash>

Nếu bạn có quyền, bạn có thể đẩy nó trực tiếp đến nhánh "chính" nếu không chỉ cần đẩy nó sang nhánh "hoàn nguyên" của bạn và tạo yêu cầu kéo.

Bạn có thể tìm thấy nhiều thông tin hữu ích hơn về chủ đề này tại đây: https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html


1

Tôi thấy việc tạo ra một bản vá ngược giữa hai điểm cuối biết và áp dụng bản vá đó sẽ hoạt động. Điều này giả định rằng bạn đã tạo ảnh chụp nhanh (thẻ) từ nhánh chính của bạn hoặc thậm chí là sao lưu của nhánh chính của bạn nói master_bk_01012017.

Nói rằng nhánh mã bạn sáp nhập vào chủ là mycodebranch.

  1. Thanh toán chính chủ.
  2. Tạo một bản vá ngược nhị phân đầy đủ giữa chủ và bản sao lưu của bạn. git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. Kiểm tra bản vá của bạn git apply --check myrevert.patch
  4. Áp dụng bản vá với đăng xuất git am --signoff < myrevert.patch
  5. Nếu bạn cần phải mang lại mã này một lần nữa khi nó được sửa, bạn sẽ cần tách nhánh hoàn nguyên và kiểm tra nhánh sửa lỗi git branch mycodebranch_fix git checkout mycodebranch_fix
  6. Ở đây bạn cần tìm khóa SHA để hoàn nguyên và hoàn nguyên hoàn nguyên git revert [SHA]
  7. Bây giờ bạn có thể sử dụng mycodebranch_fix của mình để khắc phục các sự cố, cam kết và hợp nhất lại thành chủ sau khi hoàn tất.

1

Câu trả lời được đánh dấu chính xác có tác dụng với tôi nhưng tôi đã phải dành thời gian để xác định điều gì đang xảy ra .. Vì vậy, tôi quyết định thêm câu trả lời bằng các bước đơn giản cho các trường hợp như của tôi ..

Hãy nói rằng chúng ta đã có nhánh A và B .. Bạn đã hợp nhất nhánh A thành nhánh B và đẩy nhánh B thành chính nó để bây giờ hợp nhất là một phần của nó .. Nhưng bạn muốn quay lại cam kết cuối cùng trước khi hợp nhất .. Làm gì bạn làm?

  1. Chuyển đến thư mục gốc git của bạn (thư mục dự án thường) và sử dụng git log
  2. Bạn sẽ thấy lịch sử của các cam kết gần đây - các xác nhận có thuộc tính cam kết / tác giả / ngày trong khi các kết hợp cũng có một thuộc tính hợp nhất - vì vậy bạn thấy chúng như thế này:

    commit: <commitHash> Merge: <parentHashA> <parentHashB> Author: <author> Date: <date>

  3. Sử dụng git log <parentHashA>git log <parentHashB>- bạn sẽ thấy lịch sử cam kết của các nhánh cha mẹ đó - các cam kết đầu tiên trong danh sách là các cam kết mới nhất

  4. Lấy <commitHash>cam kết bạn muốn, đi đến thư mục gốc git của bạn và sử dụng git checkout -b <newBranchName> <commitHash>- điều đó sẽ tạo ra một nhánh mới bắt đầu từ cam kết cuối cùng mà bạn đã chọn trước khi hợp nhất .. Voila, sẵn sàng!


0

Tôi cũng phải đối mặt với vấn đề này trên một PR đã được sáp nhập vào nhánh chính của repo GitHub.

Vì tôi chỉ muốn sửa đổi một số tệp đã sửa đổi nhưng không phải toàn bộ thay đổi mà PR mang lại, tôi phải thực amendhiện merge commitvới git commit --am.

Các bước:

  1. Chuyển đến nhánh mà bạn muốn thay đổi / hoàn nguyên một số tệp đã sửa đổi
  2. Thực hiện các thay đổi bạn muốn theo các tệp sửa đổi
  3. chạy git add *hoặcgit add <file>
  4. chạy git commit --amvà xác nhận
  5. chạy git push -f

Tại sao nó thú vị:

  • Nó giữ cho tác giả của PR cam kết không thay đổi
  • Nó không phá vỡ cây git
  • Bạn sẽ được đánh dấu là committer (tác giả cam kết hợp nhất sẽ không thay đổi)
  • Git hoạt động như thể bạn đã giải quyết xung đột, nó sẽ xóa / thay đổi mã trong các tệp đã sửa đổi như thể bạn nói với GitHub theo cách thủ công để không hợp nhất nó như hiện trạng

0

Tôi đã tìm thấy lời giải thích tốt cho Cách hoàn nguyên Hợp nhất từ liên kết này và tôi sao chép dán phần giải thích bên dưới và nó sẽ hữu ích chỉ trong trường hợp nếu liên kết bên dưới không hoạt động.

Làm thế nào để hoàn nguyên một sự hợp nhất bị lỗi Alan (alan@clueserver.org) cho biết:

Tôi có một chi nhánh chính. Chúng tôi có một chi nhánh mà một số nhà phát triển đang thực hiện. Họ tuyên bố nó đã sẵn sàng. Chúng tôi hợp nhất nó vào nhánh chủ. Nó phá vỡ một cái gì đó để chúng tôi hoàn nguyên hợp nhất. Họ thay đổi mã. họ đưa nó đến một điểm mà họ nói nó ổn và chúng tôi hợp nhất lại. Khi kiểm tra, chúng tôi thấy rằng các thay đổi mã được thực hiện trước khi hoàn nguyên không nằm trong nhánh chính, nhưng mã thay đổi sau nằm trong nhánh chính. và yêu cầu giúp đỡ phục hồi từ tình huống này.

Lịch sử ngay sau khi "hoàn nguyên hợp nhất" sẽ như thế này:

---o---o---o---M---x---x---W
              /
      ---A---B

Trong đó A và B ở bên phát triển không tốt lắm, M là sự hợp nhất mang lại những thay đổi sớm này vào tuyến chính, x là những thay đổi không liên quan đến những gì nhánh bên đã làm và đã thực hiện trên tuyến chính và W là " hoàn nguyên hợp nhất M "(không W nhìn M lộn ngược?). IOW, "diff W ^ .. W" tương tự như "diff -RM ^ .. M".

Một "hoàn nguyên" của hợp nhất có thể được thực hiện với:

$ git Revert -m 1 M Sau khi các nhà phát triển của nhánh bên sửa lỗi, lịch sử có thể như sau:

---o---o---o---M---x---x---W---x
              /
      ---A---B-------------------C---D

trong đó C và D phải sửa những gì đã bị hỏng ở A và B, và bạn có thể đã có một số thay đổi khác trên tuyến chính sau W.

Nếu bạn hợp nhất nhánh bên được cập nhật (với D ở đầu), sẽ không có thay đổi nào được thực hiện trong A hoặc B, vì chúng được hoàn nguyên bởi W. Đó là những gì Alan thấy.

Linus giải thích tình huống:

Hoàn nguyên một cam kết thông thường chỉ có hiệu quả hoàn tác những gì cam kết đó đã làm, và khá đơn giản. Nhưng hoàn nguyên một cam kết hợp nhất cũng hoàn tác dữ liệu mà cam kết đã thay đổi, nhưng nó hoàn toàn không ảnh hưởng gì đến lịch sử mà hợp nhất có. Vì vậy, sự hợp nhất sẽ vẫn tồn tại và nó vẫn sẽ được coi là kết hợp hai nhánh lại với nhau và các sự hợp nhất trong tương lai sẽ thấy sự hợp nhất đó là trạng thái chia sẻ cuối cùng - và hoàn nguyên hoàn nguyên hợp nhất sẽ không ảnh hưởng đến điều đó. Vì vậy, một "hoàn nguyên" hoàn tác các thay đổi dữ liệu, nhưng nó không phải là rất nhiềumột "hoàn tác" theo nghĩa là nó không hoàn tác các tác động của một cam kết đối với lịch sử kho lưu trữ. Vì vậy, nếu bạn nghĩ "hoàn nguyên" là "hoàn tác", thì bạn sẽ luôn bỏ lỡ phần hoàn nguyên này. Vâng, nó hoàn tác dữ liệu, nhưng không, nó không hoàn tác lịch sử. Trong tình huống như vậy, trước tiên bạn muốn hoàn nguyên lại lần hoàn nguyên trước đó, điều này sẽ làm cho lịch sử trông như thế này:

---o---o---o---M---x---x---W---x---Y
              /
      ---A---B-------------------C---D

Trong đó Y là hoàn nguyên của W. Như vậy "hoàn nguyên hoàn nguyên" có thể được thực hiện với:

$ git Revert W Lịch sử này sẽ (bỏ qua những xung đột có thể có giữa những gì W và W..Y đã thay đổi) tương đương với việc không có W hoặc Y trong lịch sử:

---o---o---o---M---x---x-------x----
              /
      ---A---B-------------------C---D

và sáp nhập nhánh bên một lần nữa sẽ không có xung đột phát sinh từ một lần hoàn nguyên trước đó và hoàn nguyên của hoàn nguyên.

---o---o---o---M---x---x-------x-------*
              /                       /
      ---A---B-------------------C---D

Tất nhiên những thay đổi được thực hiện trong C và D vẫn có thể xung đột với những gì được thực hiện bởi bất kỳ x nào, nhưng đó chỉ là xung đột hợp nhất bình thường.


-2

Như Ryan đã đề cập, git revertcó thể làm cho việc hợp nhất trở nên khó khăn, vì vậy git revertcó thể không phải là điều bạn muốn. Tôi thấy rằng sử dụng git reset --hard <commit-hash-prior-to-merge>lệnh để hữu ích hơn ở đây.

Khi bạn đã thực hiện phần thiết lập lại cứng, sau đó bạn có thể buộc đẩy đến nhánh từ xa, tức là git push -f <remote-name> <remote-branch-name>, nơi <remote-name>thường được đặt tên origin. Từ thời điểm đó, bạn có thể hợp nhất lại nếu bạn muốn.


4
Bất cứ điều gì liên quan đến lực đẩy là một ý tưởng tồi trừ khi bạn là người duy nhất sử dụng repo và bạn biết chính xác những gì bạn đang làm. Hoàn nguyên với git Revert và sau đó có thể hoàn nguyên Revert với git Revert (nếu bạn cần mang lại mọi thứ một lần nữa) là một cách thay thế an toàn hơn nhiều.
Oyvind
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.