2.6k

Có ai biết làm thế nào để dễ dàng hoàn tác một cuộc nổi loạn git?

Cách duy nhất nảy ra trong đầu là đi vào nó bằng tay:

  • kiểm tra git cha mẹ cam kết cho cả hai chi nhánh
  • sau đó tạo một nhánh tạm thời từ đó
  • cherry-pick tất cả các cam kết bằng tay
  • thay thế nhánh mà tôi đã khởi động lại bởi nhánh được tạo thủ công

Trong tình huống hiện tại của tôi, việc này sẽ hiệu quả vì tôi có thể dễ dàng nhận ra các cam kết từ cả hai chi nhánh (một là công cụ của tôi, bên kia là công cụ của đồng nghiệp của tôi).

Tuy nhiên, cách tiếp cận của tôi đánh tôi là tối ưu và dễ bị lỗi (giả sử tôi vừa mới nổi loạn với 2 nhánh của chính mình).

Có ý kiến ​​gì không?

Làm rõ: Tôi đang nói về một cuộc nổi loạn trong đó một loạt các cam kết đã được phát lại. Không chỉ một.

|
  • 5

    Cũng lưu ý rằng trong quá trình rebase, bạn có thể loại trừ các xác nhận hoặc xóa chúng; những thay đổi này không thể hoàn nguyên nếu không có con trỏ đến các nút ban đầu hoặc chọn lọc thông qua reflog, do đó, việc đánh dấu anh đào sẽ không hoạt động.

    – Tạ Huệ An 10:47:35 21/12/2011
3.7k

Cách đơn giản nhất sẽ được tìm ra đầu cam kết của chi nhánh như nó đã được ngay trước rebase bắt đầu vào reflog ...

git reflog

và để thiết lập lại nhánh hiện tại cho nó (với những lưu ý thông thường về việc hoàn toàn chắc chắn trước khi đặt lại với --hardtùy chọn).

Giả sử cam kết cũ có HEAD@{5}trong nhật ký ref:

git reset --hard HEAD@{5}

Trong Windows, bạn có thể cần trích dẫn tài liệu tham khảo:

git reset --hard "HEAD@{5}"

Bạn có thể kiểm tra lịch sử của người đứng đầu ứng cử viên cũ bằng cách thực hiện git log HEAD@{5}( Windows git log "HEAD@{5}" :).

Nếu bạn không bị vô hiệu hóa trên mỗi lần cập nhật chi nhánh, bạn có thể thực hiện git reflog branchname@{1}một cách đơn giản như một cuộc nổi loạn sẽ tách đầu người đứng đầu trước khi gắn lại vào đầu cuối cùng. Tôi sẽ kiểm tra lại điều này, mặc dù gần đây tôi chưa xác minh điều này.

Theo mặc định, tất cả các reflog được kích hoạt cho các kho lưu trữ không trống:

[core]
    logAllRefUpdates = true
|
  • 1

    Git reflog là tuyệt vời, chỉ cần nhớ rằng bạn có thể nhận được đầu ra được định dạng tốt hơn với git log -g(mẹo từ progit.org/book của Scott Chacon ).

    – Trịnh Huy Khiêm 10:14:51 23/07/2010
  • 1

    @Zach: git rebase --abort( -ikhông có ý nghĩa gì --abort) là từ bỏ một cuộc nổi loạn chưa hoàn thành - vì có xung đột hoặc vì nó tương tác hoặc cả hai; đó không phải là về việc hoàn tác một cuộc nổi loạn thành công mà đó là câu hỏi nói về cái gì. Bạn có thể sử dụng rebase --aborthoặc reset --hardtùy thuộc vào tình huống mà bạn gặp phải. Bạn không cần phải làm cả hai.

    – Ngô Thu Hà 20:40:26 15/06/2011
  • 1

    Chỉ trong trường hợp, tạo một bản sao lưu đầu tiên : git tag BACKUP. Bạn có thể quay lại nếu có sự cố xảy ra:git reset --hard BACKUP

    – Dương Ngọc Hoa 14:00:18 05/11/2012
  • 1

    Nếu bạn đã thực hiện nhiều cam kết thì ĐẦU @ {#} mà bạn đang tìm kiếm sẽ được mở đầu bằng commit:trái ngược với rebase:. Nghe có vẻ rõ ràng nhưng nó làm tôi bối rối một chút.

    – Đặng Nguyên Đức 20:43:35 26/08/2013
  • 1

    Tham gia bữa tiệc sau một cuộc nổi loạn tình cờ: D. Sẽ không phải là một git reset --hard ORIG_HEADthủ thuật cũng ngay lập tức sau cuộc nổi loạn tình cờ?

    – Tạ Quỳnh Thanh 08:37:35 23/09/2013
1.3k

Trên thực tế, rebase lưu điểm bắt đầu của bạn để ORIG_HEADđiều này thường đơn giản như:

git reset --hard ORIG_HEAD

Tuy nhiên, các reset, rebasemergetất cả lưu ban đầu của bạn HEADtrỏ vào ORIG_HEADnhư vậy, nếu bạn đã thực hiện bất kỳ của những lệnh từ rebase bạn đang cố gắng lùi lại sau đó bạn sẽ phải sử dụng reflog.

|
  • 1

    Đây chính xác là những gì tôi cần, tôi đã phạm sai lầm khi sử dụng rebase với git tfs. Điều này đã đưa tôi trở lại một điểm mà tôi có thể cam kết.

    – Trịnh Huy Khiêm 18:05:20 18/08/2011
  • 1

    Trong trường hợp ORIG_HEADkhông còn hữu ích, bạn cũng có thể sử dụng branchName@{n}cú pháp, nvị trí trước thứ n của con trỏ nhánh. Vì vậy, ví dụ, nếu bạn rebase featureAnhánh lên nhánh của bạn master, nhưng bạn không thích kết quả của rebase, thì bạn chỉ cần làm git reset --hard featureA@{1}lại để đặt lại nhánh trở lại chính xác vị trí của nó trước khi bạn thực hiện rebase. Bạn có thể đọc thêm về cú pháp nhánh @ {n} tại các tài liệu Git chính thức để sửa đổi .

    – Ngô Thu Hà 05:17:33 24/05/2013
  • 1

    Đây là cách dễ nhất. Theo dõi nó với một git rebase --abortmặc dù.

    – Dương Ngọc Hoa 19:24:39 16/09/2015
  • 1

    Và hãy để tôi bổ sung cho nó: git reset --hard ORIG_HEADcó thể sử dụng nhiều lần để quay đi quay lại. Nói, nếu A --- rebase thành --- B --- rebase thành --- C, bây giờ tôi đang ở C, tôi có thể quay lại A bằng cách sử dụng hai lầngit reset --hard ORIG_HEAD

    – Đặng Nguyên Đức 07:52:50 05/01/2019
  • 1

    @Seph Bạn có thể giải thích lý do tại sao bạn đề nghị theo dõi với git rebase --abort?

    – Tạ Quỳnh Thanh 08:50:22 28/01/2019
340

Câu trả lời của Charles hoạt động, nhưng bạn có thể muốn làm điều này:

git rebase --abort

để dọn dẹp sau reset.

Nếu không, bạn có thể nhận được thông báo “ Interactive rebase already started”.

|
95

git reflogsẽ hiển thị cho bạn tất cả các thay đổi trước và sau khi rebase và cho phép bạn tìm đúng thay đổi để đặt lại. Nhưng tôi ngạc nhiên không ai đề cập đến cách siêu đơn giản khác ở đây:

Rebase rời khỏi trạng thái cũ như ORIG_HEAD, vì vậy bạn có thể hoàn nguyên rebase cuối cùng bằng cách chạy:

git reset --hard ORIG_HEAD
|
  • 1

    Điều này chỉ hoạt động miễn là bạn không gọi thủ công 'git reset' trong quá trình khởi động lại tương tác! Ngoài ra, phiên bản git hiện tại của tôi (2.20.1) dường như sửa đổi ORIG_HEAD nếu bạn áp dụng một cam kết sửa lỗi trong khi khởi động lại.

    – Hoàng Hương Thu 16:45:26 07/01/2019
80

Đặt lại nhánh vào đối tượng cam kết lơ lửng của mẹo cũ tất nhiên là giải pháp tốt nhất, bởi vì nó khôi phục trạng thái trước đó mà không tốn bất kỳ nỗ lực nào. Nhưng nếu bạn đã mất các cam kết đó (ví dụ: vì bạn đã thu thập rác trong kho lưu trữ của mình trong thời gian này hoặc đây là một bản sao mới), bạn luôn có thể khởi động lại chi nhánh một lần nữa. Chìa khóa cho việc này là công --ontotắc.

Giả sử bạn có một nhánh chủ đề được gọi một cách tưởng tượng topic, rằng bạn đã phân nhánh masterkhi tiền boa master0deadbeefcam kết. Tại một số điểm trong khi trên topicchi nhánh, bạn đã làm git rebase master. Bây giờ bạn muốn hoàn tác điều này. Đây là cách thực hiện:

git rebase --onto 0deadbeef master topic

Điều này sẽ thực hiện tất cả các cam kết topicmà không phải trên mastervà phát lại chúng trên đầu trang 0deadbeef.

Với --onto, bạn có thể sắp xếp lại lịch sử của mình thành khá nhiều hình dạng bất kỳ .

Chúc vui vẻ. :-)

|
  • 1

    Tôi nghĩ rằng đây là lựa chọn tốt nhất vì tính linh hoạt của nó. Tôi đã phân nhánh b1 khỏi master, sau đó rebound b1 thành một nhánh b2 mới, sau đó muốn hoàn nguyên b1 để dựa vào master một lần nữa. Tôi chỉ yêu git - cảm ơn!

    – Hoàng Hương Thu 12:59:23 17/08/2011
  • 1

    Đây là lựa chọn tốt nhất ở đây! Nó giữ tất cả những thay đổi tôi có trên chi nhánh hiện tại của mình và loại bỏ tất cả những thay đổi không mong muốn!

    – Hoàng Bạch Quỳnh 20:43:03 15/03/2016
  • 1

    Tôi muốn nói rằng với sự kết hợp --onto-ibạn có thể sắp xếp lại lịch sử của mình thành bất kỳ hình dạng nào. Sử dụng gitk (hoặc gitx trên mac) để xem hình dạng bạn tạo :-).

    – Trịnh Duy Việt 12:59:36 27/03/2018
65

Tôi thực sự đặt một thẻ dự phòng trên chi nhánh trước khi tôi thực hiện bất kỳ hoạt động không cần thiết nào (hầu hết các cuộc nổi loạn là tầm thường, nhưng tôi sẽ làm điều đó nếu nó trông phức tạp ở bất cứ đâu).

Sau đó, khôi phục là dễ dàng như git reset --hard BACKUP.

|
  • 1

    Tôi cũng làm điều này Nó cũng hữu ích để git diff BACKUP..HAD để đảm bảo bạn đã thay đổi những gì bạn có ý định.

    – Hoàng Hương Thu 07:43:27 12/04/2011
  • 1

    Tôi cũng đã từng làm điều đó, nhưng vì tôi cảm thấy thoải mái hơn với việc reflog nên tôi không còn cảm thấy cần thiết nữa. Các reflog về cơ bản là làm điều này thay mặt bạn mỗi khi bạn thay đổi CHÍNH.

    – Hoàng Bạch Quỳnh 19:44:55 01/08/2012
  • 1

    Chà, tôi thích những cái tên có ý nghĩa, bởi vì tìm kiếm đúng mặt hàng trong reflog đôi khi không vui chút nào.

    – Trịnh Duy Việt 21:13:40 02/08/2012
  • 1

    Bạn thực sự thậm chí không cần phải tạo một nhánh dự phòng, bạn chỉ cần sử dụng branchName@{n}cú pháp, đây nlà vị trí thứ n của con trỏ nhánh. Vì vậy, ví dụ, nếu bạn rebase featureAnhánh lên nhánh của bạn master, nhưng bạn không thích kết quả của rebase, thì bạn chỉ cần làm git reset --hard featureA@{1}lại để đặt lại nhánh trở lại chính xác vị trí của nó trước khi bạn thực hiện rebase. Bạn có thể đọc thêm về branch@{n}cú pháp tại các tài liệu Git chính thức để sửa đổi .

    – Đỗ Tấn Phát 05:12:48 24/05/2013
  • 1

    Trên thực tế, bạn thậm chí không cần sử dụng cú pháp ở trên, theo câu trả lời của Pat Notz , bản gốc HEADcủa chi nhánh được lưu trữ tạm thời ORIG_HEAD. Nhưng kỹ thuật sử dụng nhãn nhánh sao lưu cũng hoạt động, đó chỉ là nhiều bước hơn.

    – Tạ Thời Nhiệm 05:21:52 24/05/2013
58

Trong trường hợp bạn đã đẩy chi nhánh của mình đến kho lưu trữ từ xa (thường là nguồn gốc) và sau đó bạn đã thực hiện một rebase thành công (không hợp nhất) ( git rebase --abortđưa ra "Không có rebase trong tiến trình"), bạn có thể dễ dàng đặt lại nhánh bằng lệnh:

thiết lập lại git - nguồn gốc gốc / {BranchName}

Thí dụ:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean
|
  • 1

    Đó là câu trả lời đúng cho tôi. Rebase và commit trước khi rebase có cùng ID xác nhận và quay trở lại HEAD {1} sẽ không hoàn nguyên rebase!

    – Hoàng Hương Thu 05:34:28 02/08/2017
51

Trong trường hợp bạn chưa hoàn thành cuộc nổi loạn và ở giữa nó, các công việc sau:

git rebase --abort
|
17

Sử dụng reflogkhông làm việc cho tôi.

Những gì làm việc cho tôi là tương tự như được mô tả ở đây . Mở tệp trong .git / log / refs được đặt tên theo nhánh được khởi động lại và tìm dòng có chứa "rebase finsihed", đại loại như:

5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

Kiểm tra cam kết thứ hai được liệt kê trên dòng.

git checkout 88552c8f

Sau khi xác nhận điều này có chứa những thay đổi đã mất của tôi, tôi rẽ nhánh và thở phào nhẹ nhõm.

git log
git checkout -b lost_changes
|
15

Đối với nhiều cam kết, hãy nhớ rằng bất kỳ cam kết nào tham chiếu tất cả lịch sử dẫn đến cam kết đó. Vì vậy, trong câu trả lời của Charles, hãy đọc "cam kết cũ" là "cam kết mới nhất". Nếu bạn đặt lại cam kết đó, thì tất cả lịch sử dẫn đến cam kết đó sẽ xuất hiện lại. Điều này nên làm những gì bạn muốn.

|
11

Nếu bạn nổi dậy thành công chống lại chi nhánh từ xa và git rebase --abortbạn vẫn không thể thực hiện một số thủ thuật để cứu công việc của mình và không bị ép buộc. Giả sử chi nhánh hiện tại của bạn bị từ chối do nhầm lẫn được gọi your-branchvà đang theo dõiorigin/your-branch

  • git branch -m your-branch-rebased # đổi tên chi nhánh hiện tại
  • git checkout origin/your-branch # thanh toán đến trạng thái mới nhất được biết là có nguồn gốc
  • git checkout -b your-branch
  • kiểm tra git log your-branch-rebased, so sánh git log your-branchvà xác định các cam kết bị thiếuyour-branch
  • git cherry-pick COMMIT_HASH cho mọi cam kết trong your-branch-rebased
  • đẩy những thay đổi của bạn Xin lưu ý rằng hai chi nhánh địa phương được liên kết với remote/your-branchvà bạn chỉ nên đẩyyour-branch
|
11

Theo giải pháp của @ ALLan và @Zearin, tôi ước mình chỉ có thể bình luận nhưng tôi không đủ danh tiếng, vì vậy tôi đã sử dụng lệnh sau:

Thay vì làm git rebase -i --abort (chú ý -i ) Tôi đã phải chỉ đơn giản là làm git rebase --abort( mà không cần sự -i ).

Sử dụng cả hai -i--abortcùng một lúc khiến Git hiển thị cho tôi danh sách các tùy chọn sử dụng.

Vì vậy, trạng thái chi nhánh trước đây và hiện tại của tôi với giải pháp này là:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$
|
3

Giả sử tôi khởi động lại chủ nhân cho nhánh tính năng của mình và tôi nhận được 30 cam kết mới phá vỡ thứ gì đó. Tôi đã thấy rằng thường thì dễ dàng nhất để loại bỏ các cam kết xấu.

git rebase -i HEAD~31

Rebase tương tác cho 31 lần xác nhận gần nhất (sẽ không hại nếu bạn chọn quá nhiều).

Chỉ cần thực hiện các cam kết mà bạn muốn loại bỏ và đánh dấu chúng bằng "d" thay vì "chọn". Bây giờ các xác nhận đã bị xóa một cách hiệu quả khi hoàn tác rebase (nếu bạn chỉ xóa các xác nhận bạn vừa nhận được khi khởi động lại).

|
1

Nếu bạn ở trên một chi nhánh, bạn có thể sử dụng:

git reset --hard @{1}

Không chỉ có một nhật ký tham chiếu cho HEAD (thu được bởi git reflog), còn có các bản cập nhật cho mỗi nhánh (thu được bởi git reflog <branch>). Vì vậy, nếu bạn đang ở trên masterthì git reflog mastersẽ liệt kê tất cả các thay đổi cho chi nhánh đó. Bạn có thể tham khảo rằng những thay đổi bằng cách master@{1}, master@{2}vv

git rebase thường sẽ thay đổi CHÍNH nhiều lần nhưng nhánh hiện tại sẽ chỉ được cập nhật một lần.

@{1}chỉ đơn giản là một phím tắt cho nhánh hiện tại , vì vậy nó tương đương với master@{1}nếu bạn đang bật master.

git reset --hard ORIG_HEADsẽ không hoạt động nếu bạn sử dụng git resettrong quá trình tương tác rebase.

|

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.