Hoàn tác hợp nhất theo yêu cầu kéo?


159

Ai đó đã chấp nhận một yêu cầu kéo mà họ không nên có. Bây giờ chúng tôi có một loạt các mã bị hỏng được hợp nhất. Làm thế nào để bạn hoàn tác một yêu cầu kéo? Tôi vừa mới hoàn nguyên các thay đổi đối với cam kết ngay trước khi hợp nhất, nhưng tôi nhận thấy rằng nó được hợp nhất trong một loạt các cam kết. Vì vậy, bây giờ có tất cả các cam kết từ người này từ vài ngày trước khi hợp nhất. Làm thế nào để bạn hoàn tác điều này?


7
Bất chấp lời khuyên của câu trả lời được chấp nhận, xin vui lòng không ép buộc vào kho lưu trữ nếu repo được chia sẻ với bất kỳ ai khác. Bạn có nguy cơ làm hỏng công việc mà người khác đã thực hiện và GitHub có thể tiếp tục hiển thị yêu cầu kéo đó như đã được hợp nhất. Một câu trả lời khác cho câu hỏi này giải thích một cách an toàn hơn để hoàn tác yêu cầu kéo.
alxndr

2
Lưu ý: ít nhất là bây giờ (tháng 6 năm 2014), GitHub đề xuất nút "Hoàn nguyên" trong GUI web Yêu cầu Kéo của họ. Xem câu trả lời của tôi dưới đây
VonC

1
Vấn đề với nút hoàn nguyên là nó tạo ra một cam kết mới như một nghịch đảo với yêu cầu kéo, điều đó có nghĩa là nếu cuối cùng bạn muốn hợp nhất những thay đổi này, sử dụng nút "Hoàn nguyên" có thể gây khó khăn hơn nhiều.
FluffySamurai

Câu trả lời:


159

Có một câu trả lời tốt hơn cho vấn đề này, mặc dù tôi chỉ có thể phá vỡ từng bước này.

Bạn sẽ cần tìm nạp và kiểm tra các thay đổi ngược dòng mới nhất như vậy, ví dụ:

git fetch upstream
git checkout upstream/master -b revert/john/foo_and_bar

Nhìn vào nhật ký cam kết, bạn sẽ tìm thấy một cái gì đó tương tự như thế này:

commit b76a5f1f5d3b323679e466a1a1d5f93c8828b269
Merge: 9271e6e a507888
Author: Tim Tom <tim@tom.com>
Date:   Mon Apr 29 06:12:38 2013 -0700

    Merge pull request #123 from john/foo_and_bar

    Add foo and bar

commit a507888e9fcc9e08b658c0b25414d1aeb1eef45e
Author: John Doe <john@doe.com>
Date:   Mon Apr 29 12:13:29 2013 +0000

    Add bar

commit 470ee0f407198057d5cb1d6427bb8371eab6157e
Author: John Doe <john@doe.com>
Date:   Mon Apr 29 10:29:10 2013 +0000

    Add foo

Bây giờ bạn muốn hoàn nguyên toàn bộ yêu cầu kéo với khả năng hủy bỏ sau này. Để làm như vậy, bạn sẽ cần lấy ID của cam kết hợp nhất .

Trong ví dụ trên, cam kết hợp nhấtcam kết hàng đầu có ghi "Yêu cầu kéo được hợp nhất # 123 ..." .

Làm điều này để hoàn nguyên cả hai thay đổi ( "Thêm thanh""Thêm foo" ) và bạn sẽ kết thúc bằng một cam kết hoàn nguyên toàn bộ yêu cầu kéo mà bạn có thể hủy bỏ sau này và giữ lịch sử thay đổi sạch sẽ:

git revert -m 1 b76a5f1f5d3b323679e466a1a1d5f93c8828b269

6
Đây phải là câu trả lời chính xác. Có một tài liệu tham khảo trong trang git-Revert để biết thêm chi tiết từ danh sách gửi thư git tại đây kernel.org/pub/software/scm/git/docs/howto/ trộm
Justin Hamade

2
Tại sao git checkout upstream/master -b revert/john/foo_and_bar? Làm gì chính xác?
Magne

4
@Magne - bạn đang tạo một nhánh mới để thực hiện hoàn nguyên, sau đó bạn có thể chọn nơi hợp nhất đảo ngược. Về cơ bản chỉ giúp bạn kiểm soát nhiều hơn những việc cần làm với chi nhánh. Trong trường hợp của tôi, tôi đã gửi yêu cầu kéo mới từ nhánh "cố định" này có chứa sự đảo ngược trở lại nhánh phát triển của chúng tôi, điều đó có nghĩa là yêu cầu kéo mới của tôi cũng có thể được hoàn nguyên nếu cần. Điều này đã được thực hiện cho một bản phát hành nơi chúng tôi quyết định rút ra bản nháp đầu tiên của một tính năng đã được lên lịch lại. Không có mã xấu, chỉ là không đi trong bản phát hành này.
Daniel Nalbach

3
Tôi đã học được một bài học khó khăn về điều này. Chúng tôi đã đẩy một tính năng đến chi nhánh phát triển của mình nhưng nhận ra có vấn đề về dữ liệu và được hoàn nguyên trực tiếp khi phát triển. Sau này khi mọi thứ đã được sửa, chúng tôi muốn thêm các tính năng phát triển mới hơn vào nhánh này để kiểm tra tính tương thích của chúng với tính năng này trước khi đẩy, vì vậy tôi đã hợp nhất phát triển trở lại nhánh tính năng. Điều này cũng áp dụng cam kết đảo ngược, hủy bỏ mọi thay đổi được thực hiện cho nhánh tính năng. > <
Greg

86

Nhìn vào biểu đồ cam kết của bạn (với gitk hoặc một chương trình tương tự). Bạn sẽ thấy các xác nhận từ yêu cầu kéo và bạn sẽ thấy các cam kết của riêng bạn và một cam kết hợp nhất (nếu đó không phải là một hợp nhất chuyển tiếp nhanh). Bạn chỉ cần tìm các cam kết cuối cùng của riêng mình trước khi hợp nhất và đặt lại chi nhánh cho cam kết này.

(Nếu bạn có reflog của chi nhánh, việc tìm kiếm cam kết trước khi hợp nhất sẽ dễ dàng hơn nữa.)


(Chỉnh sửa sau khi có thêm thông tin trong bình luận :)

Được rồi, hãy nhìn vào biểu đồ:

ảnh chụp màn hình 1

Tôi giả sử cam kết cuối cùng (ngoài cùng bên phải) là sự hợp nhất sai của bạn bằng yêu cầu kéo , đã hợp nhất dòng màu xanh nhìn thấy ở đây. Cam kết tốt cuối cùng của bạn sẽ là lần trước trên dòng màu đen, ở đây được đánh dấu màu đỏ:

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

Đặt lại cam kết này, và bạn sẽ ổn thôi.

Điều này có nghĩa là, trong bản sao làm việc tại địa phương của bạn, hãy làm điều này (sau khi đảm bảo rằng bạn không còn những thứ không được cam kết nữa, ví dụ như bởi git stash):

git checkout master
git reset --hard 7a62674ba3df0853c63539175197a16122a739ef
gitk 

Bây giờ hãy xác nhận rằng bạn thực sự đang thực hiện cam kết mà tôi đã đánh dấu ở đó và bạn sẽ không thấy bất kỳ thứ gì được kéo trong tổ tiên của nó.

git push -f origin master

(nếu điều khiển từ xa github của bạn được đặt tên origin- khác thay đổi tên).

Bây giờ mọi thứ sẽ nhìn đúng trên github, quá. Các cam kết vẫn sẽ nằm trong kho lưu trữ của bạn, nhưng không thể truy cập được bởi bất kỳ chi nhánh nào, do đó sẽ không gây hại gì ở đó. (Và tất nhiên chúng sẽ vẫn còn trên kho của RogerPaladin.)

(Có thể có một cách cụ thể chỉ dành cho web của Github để làm điều tương tự, nhưng tôi không quá quen thuộc với Github và hệ thống quản lý yêu cầu kéo của nó.)

Lưu ý rằng nếu bất kỳ ai khác có thể đã kéo chủ của bạn với cam kết sai, thì họ sẽ gặp vấn đề tương tự như bạn hiện có và không thể thực sự đóng góp lại. trước khi đặt lại phiên bản chính mới của bạn.

Nếu có khả năng điều này xảy ra hoặc đơn giản là bạn muốn tránh mọi vấn đề, hãy sử dụng git revertlệnh thay vìgit reset , để hoàn nguyên các thay đổi bằng một cam kết mới, thay vì đặt lại về một lỗi cũ. (Một số người nghĩ rằng bạn không bao giờ nên thiết lập lại với các chi nhánh đã xuất bản.) Xem các câu trả lời khác cho câu hỏi này về cách thực hiện việc này.

Cho tương lai:

Nếu bạn chỉ muốn một số cam kết của chi nhánh RogerPaladin, hãy cân nhắc sử dụng cherry-pickthay vì merge. Hoặc liên lạc với RogerPaladin để chuyển chúng đến một chi nhánh riêng và gửi yêu cầu kéo mới.


Nhưng chúng tôi có rất nhiều cam kết giữa hợp nhất và cam kết đầu tiên của anh ấy. Vì vậy, giống như chúng ta có các cam kết hợp nhất, các cam kết của riêng chúng ta và sau đó là một cam kết khác của anh ta. Có vẻ như nó được hợp nhất trong những cam kết thực sự cũ của mình.
Sẽ

Vâng, nó sẽ hợp nhất trong mọi thứ là tổ tiên của cam kết đã hợp nhất (và chưa phải là tổ tiên của cam kết của bạn). Điều này sẽ không cản trở bạn đặt lại - các cam kết sẽ không được sắp xếp lại theo thứ tự, nếu sau đó bạn không phải là một cuộc nổi loạn.
Paŭlo Ebermann

1
Vâng, bây giờ nó có vẻ đúng (ngoài sự hợp nhất kỳ lạ vào chính nó - nhưng điều này có thể là một trục trặc trong phần mềm đồ thị mạng). :-) Tôi rất vui vì tôi có thể giúp.
Paŭlo Ebermann

6
Tôi nghĩ rằng điều này có thể có vấn đề nếu ai đó đã thực hiện các cam kết xấu và sau đó họ đã gửi một yêu cầu kéo có chứa các cam kết xấu tương tự, do đó họ lẻn nhưng vào repo. Nó sẽ không an toàn hơn để tạo ra một cam kết mới đảo ngược các cam kết xấu? Theo cách đó, nếu các cam kết xấu đã được kéo sang các nhánh / nhánh khác, chúng sẽ bị loại bỏ một cách hiệu quả bởi một lực kéo khác đến nhánh / ngã ba khác đó? Tôi không nói rõ điều này là sự thật, đây là điều tôi nghĩ có thể đúng, ở cùng vị trí với OP và đã dành khoảng một giờ để xem xét các lựa chọn của tôi.
Myles McDonnell

4
@ Tôi sẽ đề nghị không chấp nhận câu trả lời này. Điều này tốt cho mã chưa được đẩy (trong trường hợp này, đây là câu hỏi SO phù hợp hơn, nhưng đối với mã được chia sẻ công khai, thực hiện lệnh git viết lại lịch sử (trong trường hợp này reset --hardvà lực đẩy là thực hành rất tệ). Câu trả lời của @errordeveloper dưới đây cho thấy một cách để làm điều này mà không có bất kỳ lịch sử viết lại hoặc lực đẩy nào.
asmeker 15/2/2016

34

Nếu kéo là điều cuối cùng anh làm thì

git reset --hard HEAD~1

3
Hãy cẩn thận làm theo hướng dẫn này, nó thực sự đặt lại cho tôi 2 bước chứ không phải một.
szeitlin

1
@szeitlin Làm thế nào có thể xảy ra rằng nó đã đưa bạn trở lại 2 bước chứ không phải một? Tôi biết bình luận này đã được để lại hơn 4 năm trước nhưng tôi tò mò liệu có ai biết câu trả lời về việc nó có thể xảy ra như thế nào không. Điều này rất quan trọng đối với tôi. Cảm ơn.
Haradzieniec

Bây giờ tôi không nhớ, nhưng tôi đoán điều đó có thể xảy ra nếu tôi đã thực hiện một cam kết mới khi tôi cố gắng thiết lập lại? Tôi sẽ chỉ kiểm tra nó với một repo giả nếu bạn lo lắng về nó.
szeitlin

Điều này làm việc tốt cho tôi. Nó đã xóa yêu cầu kéo được sáp nhập gần đây. Sau đó git reset --hard HEAD~1, tôi sử dụng git push origin -fđể cập nhật kho lưu trữ từ xa. Nhưng hãy cẩn thận, cẩn thận trước khi làm điều này.
Denis Oluka

24

Bắt đầu từ ngày 24 tháng 6 năm 2014, bạn có thể thử hủy PR một cách dễ dàng (Xem phần " Hoàn nguyên yêu cầu kéo ") với:

Giới thiệu nút hoàn nguyên

bạn có thể dễ dàng hoàn nguyên yêu cầu kéo trên GitHub bằng cách nhấp vào Hoàn nguyên:

https://camo.githubusercontent.com/0d3350caf2bb1cba53123ffeafc00ca702b1b164/68747470733a2f2f6769746875622d696d616765732e73332e616d617a6f6e6177732e636f6d2f68656c702f70756c6c5f72657175657374732f7265766572742d70756c6c2d726571756573742d6c696e6b2e706e67

Bạn sẽ được nhắc tạo một yêu cầu kéo mới với các thay đổi được hoàn nguyên:

https://camo.githubusercontent.com/973efae3cc2764fc1353885a6a45b9a518d9b78b/68747470733a2f2f6769746875622d696d616765732e73332e616d617a6f6e6177732e636f6d2f68656c702f70756c6c5f72657175657374732f7265766572742d70756c6c2d726571756573742d6e65772d70722e706e67

Nó vẫn phải được kiểm tra mặc dù nếu hoàn nguyên đó có sử dụng -mhay không (để hoàn nguyên các kết hợp)

Nhưng Adil H Raza cho biết thêm trong các ý kiến ​​(tháng 12 năm 2019):

Đó là hành vi dự kiến, nó đã tạo ra một nhánh mới và bạn có thể tạo PR từ nhánh mới này cho bạn master.
Bằng cách này trong tương lai bạn có thể hủy hoàn nguyên hoàn nguyên nếu cần, đó là tùy chọn an toàn nhất và không trực tiếp thay đổi master.


Cảnh báo : Korayem chỉ ra trong các ý kiến rằng:

Sau khi hoàn nguyên, giả sử bạn đã thực hiện thêm một số thay đổi trên nhánh Git và tạo PR mới từ cùng một nhánh nguồn / đích.
Bạn sẽ thấy PR chỉ hiển thị những thay đổi mới, nhưng không có gì ở đó trước khi hoàn nguyên .

Korayem đề cập đến chúng tôi " Github: Thay đổi bị bỏ qua sau khi hoàn nguyên ( git cherry-pick, git rebase) " để biết thêm.


tôi đã thử điều này và nó đã tạo ra một nhánh mới thay vì hoàn tác PR trên master?
Грозный

Lạ thật. Bạn có thể hỏi một câu hỏi mới để minh họa cho hành vi đó?
VonC

đó là hành vi dự kiến, nó đã tạo ra một nhánh mới và bạn có thể tạo PR từ nhánh mới này đến chủ của bạn. Bằng cách này trong tương lai bạn có thể hủy hoàn nguyên hoàn nguyên nếu cần, đó là tùy chọn an toàn nhất và không trực tiếp thay đổi chủ của bạn.
Adil H. Raza

1
@ AdilH.Raza Cảm ơn bạn. Tôi đã bao gồm nhận xét của bạn trong câu trả lời để dễ nhìn hơn.
VonC

1
Những ngón tay @VonC vượt qua điều này sẽ cứu các nhà phát triển trên toàn cầu khỏi quá nhiều đau đớn và lãng phí thời gian
Korayem

8

Để hoàn tác một yêu cầu kéo github với các cam kết trong suốt mà bạn không muốn xóa, bạn phải chạy:

git reset --hard --merge <commit hash>

với hàm băm cam kết là PRIOR cam kết để hợp nhất yêu cầu kéo. Điều này sẽ xóa tất cả các xác nhận khỏi yêu cầu kéo mà không ảnh hưởng đến bất kỳ cam kết nào trong lịch sử.

Một cách tốt để tìm thấy điều này là đi đến yêu cầu kéo hiện đang đóng và tìm trường này:

Kéo hình ảnh yêu cầu Kéo hình ảnh yêu cầu

Sau khi bạn chạy git reset, chạy một:

git push origin --force <branch name>

Điều này sẽ hoàn nguyên chi nhánh trở lại trước khi yêu cầu kéo KHÔNG ảnh hưởng đến bất kỳ cam kết nào trong chi nhánh được ghi vào lịch sử cam kết giữa các lần xác nhận từ yêu cầu kéo.

BIÊN TẬP:

Nếu bạn nhấp vào nút hoàn nguyên trên yêu cầu kéo, điều này sẽ tạo ra một cam kết bổ sung trên nhánh. Nó KHÔNG phổ biến hoặc không hòa nhập. Điều này có nghĩa là nếu bạn nhấn nút hoàn nguyên, bạn không thể mở yêu cầu kéo mới để thêm lại tất cả mã này.


Cách tuyệt vời để khiến mọi thứ trở nên thẳng thắn mà không cần bất kỳ sự trở lại điên rồ hay hái anh đào nào
Cumulo Nimbus

Cảm ơn! Đó là những gì tôi đã dự định thực hiện, nhưng đó sẽ là khoảng ~ 200 cam kết để hái anh đào.
FluffySamurai

1

Tôi sử dụng nơi này tất cả các thời gian, cảm ơn.

Tôi đã tìm kiếm cách hoàn tác một yêu cầu kéo và đến đây.

Tôi chuẩn bị chỉ git reset --hard"một thời gian dài trước đây" và nhanh chóng quay trở lại nơi tôi đang ở trước khi thực hiện yêu cầu kéo.

Bên cạnh việc nhìn vào đây, tôi cũng hỏi đồng nghiệp của mình anh ta sẽ làm gì và anh ta có một câu trả lời khá hay: sử dụng kết quả đầu ra trong câu trả lời đầu tiên ở trên:

git reset --hard 9271e6e

Như với hầu hết mọi thứ trong Git, nếu bạn đang làm điều đó theo cách nào đó không dễ dàng, có lẽ bạn đã làm sai.

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.