Câu hỏi Hoàn tác khởi động lại git


Có ai biết làm thế nào để dễ dàng hoàn tác một git rebase?

Cách duy nhất mà bạn nghĩ đến là đi theo cách thủ công:

  • git checkout parent commit cho cả hai 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ế chi nhánh mà tôi đã tạo lại bởi nhánh được tạo thủ công

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

Tuy nhiên, cách tiếp cận của tôi tấn công tôi dưới mức tối ưu và dễ xảy ra lỗi (giả sử tôi vừa mới khởi động lại với 2 nhánh của riêng mình).

Ý tưởng nào?

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


2434
2017-09-25 17:59


gốc


Cũng lưu ý rằng trong quá trình rebase, bạn có thể loại trừ các commit, hoặc squash 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 gốc hoặc chọn lọc thông qua quá trình tái cấu trúc, vì vậy, quá trình làm sạch sẽ không hoạt động. - ANeves
stackoverflow.com/a/692763/2458438 Điều này lý tưởng nhất nên là câu trả lời được chấp nhận. - DDM


Các câu trả lời:


Cách dễ nhất là tìm đầu cam kết của chi nhánh ngay trước khi khởi động lại reflog...

git reflog

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

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

git reset --hard HEAD@{5}

Trong Windows, bạn có thể cần trích dẫn tham chiếu:

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

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

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

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

[core]
    logAllRefUpdates = true

3371
2017-09-25 19:56



Git reflog là tuyệt vời, chỉ cần nhớ bạn có thể có được đầu ra định dạng tốt hơn với git log -g (mẹo từ Scott Chacon's progit.org/book). - karmi
@Zach: git rebase --abort (-i không có ý nghĩa với --abort) là để từ bỏ một rebase chưa được hoàn thành - hoặc bởi vì có xung đột hoặc vì nó tương tác hoặc cả hai; nó không phải là về hoàn tác một rebase thành công đó là những gì câu hỏi là về. Bạn sẽ sử dụng rebase --abort hoặc là reset --hard tùy thuộc vào tình huống của bạn. Bạn không cần phải làm cả hai. - CB Bailey
Cảm ơn! Trong bản cài đặt Git, tôi cần đặt "HEAD {22}" vào dấu ngoặc kép để nó hoạt động. - SidJ
Điều này đã cứu mạng tôi ... thật không may, trong tôi haste tôi đã làm git reset --hard HEAD@{5} - đó là hai cam kết trên những gì tôi muốn làm. Không cần phải nói, Cảm ơn Chúa vì Git đã cho phép tôi tiếp tục và thực hiện điều chỉnh đó, sau khi thực hiện điều chỉnh khác git reflog và nhận ra cam kết mà tôi muốn đi đến chỉ mới được nâng lên 1, tôi nhanh chóng sửa lại và tất cả đều tốt. =) - marcamillion
Chỉ trong trường hợp, tạo bản sao lưu trước: git tag BACKUP. Bạn có thể quay lại nếu có sự cố: git reset --hard BACKUP - kolypto


Trên thực tế, rebase sẽ lưu điểm xuất phát của bạn thành ORIG_HEAD vì vậy điều này thường đơn giản như:

git reset --hard ORIG_HEAD

Tuy nhiên, reset, rebase và merge tất cả lưu bản gốc của bạn HEAD con trỏ vào ORIG_HEAD vì vậy, nếu bạn đã thực hiện bất kỳ lệnh nào trong số các lệnh này kể từ lần rebase bạn đang cố gắng hoàn tác thì bạn sẽ phải sử dụng reflog.


1169
2018-03-28 13:24



Cảm ơn. Điều này đã giúp tôi phục hồi một quả bóng! - kakyo
Trong trường hợp ORIG_HEAD không còn hữu ích nữa, bạn cũng có thể sử dụng branchName@{n} cú pháp, ở đâu n là vị trí thứ n trước của con trỏ nhánh. Vì vậy, ví dụ, nếu bạn rebase featureA chi nhánh vào của bạn master chi nhánh, nhưng bạn không thích kết quả của việc rebase, sau đó bạn chỉ đơn giản là có thể làm git reset --hard featureA@{1} để đặ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 tài liệu Git chính thức cho các bản sửa đổi.
Đây là cách dễ nhất. Theo dõi nó với một git rebase --abort Tuy nhiên. - Seph
Đây phải là IMO trả lời được chấp nhận. - Tomáš Fejfar
ORIG_HEAD VERY RẤT VERY rất hữu ích nếu bạn vừa làm một rebase và muốn hoàn nguyên nó một cách lành mạnh và thanh bình. Nó phải là câu trả lời TOP VOTED (hoặc cũng là câu trả lời được chấp nhận). Nó cần một vết sưng. Câu trả lời bình chọn hàng đầu yêu cầu thực hiện git reset --hard HEAD@{5}liên quan đến việc đếm đầu không cần thiết có nhiều khả năng xảy ra sai. - DDM


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

để làm sạch sau khi reset.

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


320
2017-07-27 18:21



Điều này đã loại bỏ phần "| REBASE" trong lời nhắc của tôi. +1 - Wouter Thielen
Tôi nghĩ nó nên như vậy git rebase --abort trong (tùy thuộc vào phiên bản git của bạn có lẽ). - Seph
Đó không phải là câu hỏi. Câu hỏi hỏi làm thế nào để hoàn tác một rebase hoàn thành. - Arunav Sanyal
@ArunavSanyal đây là hy vọng cuối cùng nếu hoàn tác không thành công .. - aswzen


Đặt lại nhánh cho đối tượng commit lủng lẳng của thủ thuật cũ của nó dĩ 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 tình cờ bị mất các cam kết đó (f.ex. vì bạn đã thu gom kho lưu trữ của bạn trong thời gian chờ đợi, hoặc đây là một bản sao mới), bạn luôn có thể rebase chi nhánh một lần nữa. Chìa khóa cho điều này là --onto công tắc điện.

Giả sử bạn có một nhánh chủ đề được gọi là tưởng tượng topic, bạn đã phân nhánh master khi đầu của master là 0deadbeef cam kết. Tại một số thời điểm trong khi trên topic chi nhánh, bạn đã làm git rebase master. Bây giờ bạn muốn hoàn tác điều này. Dưới đâ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 trên topic không bật master và 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 bất kỳ hình dạng nào.

Chúc vui vẻ. :-)


75
2017-09-26 02:08



Tôi nghĩ đâ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 off master, sau đó rebased b1 vào một nhánh mới b2, sau đó muốn hoàn nguyên b1 được dựa trên master một lần nữa. Tôi chỉ yêu git - cảm ơn! - ripper234
Đâ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 nhánh hiện tại của tôi, và loại bỏ tất cả những thay đổi không mong muốn! - Alicia Tang
Tôi muốn nói rằng với sự kết hợp của --onto và -i bạn có thể sắp xếp lại lịch sử của mình thành nhiều hình dạng bất kỳ. Sử dụng gitk (hoặc gitx trên mac) để xem các hình dạng bạn thực hiện :-). - rjmunro


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 nontrivial (hầu hết các rebases là tầm thường, nhưng tôi muốn làm điều đó nếu nó trông bất cứ nơi nào phức tạp).

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


62
2018-05-12 20:57



Tôi cũng làm thế. Nó cũng hữu ích để git diff BACKUP..HEAD để đảm bảo bạn đã thay đổi ý của mình. - Paul Bone
Tôi đã 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 làm lại, tôi không còn cảm thấy cần thiết nữa. Các reflog là cơ bản làm điều này thay cho bạn mỗi khi bạn thay đổi HEAD. - Pete Hodgson
Vâng, tôi thích tên có ý nghĩa hơn, bởi vì tìm kiếm đúng mục trong reflog đôi khi không vui chút nào. - Alex Gontmakher
Bạn thậm chí không cần tạo một nhánh dự phòng, bạn có thể sử dụng branchName@{n} cú pháp, ở đây n là vị trí thứ n trước của con trỏ nhánh. Vì vậy, ví dụ, nếu bạn rebase featureA chi nhánh vào của bạn master chi nhánh, nhưng bạn không thích kết quả của việc rebase, sau đó bạn chỉ đơn giản là có thể làm git reset --hard featureA@{1} để đặ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 tài liệu Git chính thức cho các bản sửa đổi.
Trên thực tế, bạn thậm chí không cần phải sử dụng cú pháp ở trên, theo Câu trả lời của Pat Notz, bản gốc HEAD của chi nhánh tạm thời được lưu trữ trong ORIG_HEAD. Nhưng kỹ thuật sử dụng nhãn nhánh dự phòng cũng hoạt động, nó chỉ là các bước khác.


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

git reset --hard origin / {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

53
2017-09-28 12:43



Đó là câu trả lời đúng cho tôi. Việc rebase và commit trước khi rebase có cùng ID commit, và quay trở lại HEAD {1} sẽ không hoàn nguyên rebase! - Bill Kotsias


Trong trường hợp bạn chưa hoàn thành việc rebase và ở giữa nó, các công việc sau đây:

git rebase --abort

49
2017-10-15 20:20



Điều này làm việc cho tôi. Tôi đã hợp nhất các xung đột để nó đủ điều kiện như là 'ở giữa nó'. - Fakeer


Đố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, đọc "cam kết cũ" là "mới nhất của các cam kết cũ". 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 trở lại. Điều này sẽ làm những gì bạn muốn.


14
2017-09-25 21:36



Đúng, cảm ơn lời nhắc ;-) - webmat