Cách hoàn tác Cam kết git - được thực hiện thay vì Cam git cam kết


1295

Tôi vô tình sửa đổi cam kết trước đây của tôi. Cam kết nên được tách riêng để giữ lịch sử của những thay đổi tôi đã thực hiện đối với một tệp cụ thể.

Có cách nào để hoàn tác cam kết cuối cùng không? Nếu tôi làm một cái gì đó như git reset --hard HEAD^, cam kết đầu tiên cũng được hoàn tác.

(Tôi chưa được đẩy đến bất kỳ thư mục từ xa)

Câu trả lời:


2291

Những gì bạn cần làm là tạo một cam kết mới với các chi tiết giống như HEADcam kết hiện tại , nhưng với cha mẹ là phiên bản trước đó HEAD. git reset --softsẽ di chuyển con trỏ nhánh sao cho lần xác nhận tiếp theo xảy ra ở trên một lần xác nhận khác với vị trí của đầu chi nhánh hiện tại.

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
git commit -C HEAD@{1}

33
Rất tuyệt, +1. Tôi thậm chí đã làm điều đó với quan điểm sửa đổi lần thứ hai vào git reflogđể tìm số chính xác, vd {2}.
JJD

179
Nói rõ hơn, lệnh đầu tiên là "hoàn tác" thực sự. Nó tạo ra HEAD, thư mục làm việc (không thay đổi) và trạng thái chỉ mục trước git commit --amend. Thứ 2 là "làm lại" thành một cam kết mới. Những công việc cho bất kỳ git commit, không chỉ --amend.
cdunn2001

60
Vì vậy, nếu bạn không sửa đổi với một thông điệp cam kết mới mà bạn cần cứu vãn, phần thứ hai có thể chỉ là một thông thường git commit.
Matt Montag

18
Vì một số lý do, tôi đã gặp lỗi khi chạy git reset --soft HEAD@{1}: fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions. Khi tôi thay thế HEAD@{1}bằng hàm băm cam kết tương đương được hiển thị trong git reflog(cảm ơn JJD!), Câu trả lời này đã hoạt động tuyệt vời!
Tim Camber

20
@TimArnold tùy thuộc vào vỏ của bạn, bạn có thể cần đặt dấu ngoặc đơn hoặc dấu ngoặc kép xung quanh HEAD@{1}. Nếu tôi chạy echo HEAD@{1}trong tcsh chẳng hạn, đầu ra là HEAD@1do các dấu ngoặc nhọn được tcsh giải thích. Nếu tôi sử dụng dấu ngoặc đơn, dấu ngoặc nhọn được giữ nguyên.
Kelvin

136

sử dụng nhật ký ref :

git branch fixing-things HEAD@{1}
git reset fixing-things

sau đó bạn chỉ nên có tất cả các thay đổi được sửa đổi trước đó trong bản sao làm việc của mình và có thể cam kết lại

để xem danh sách đầy đủ các loại chỉ số trước đó git reflog


7
Điều này cũng xóa sạch chỉ mục - vẫn hữu ích, nhưng vượt xa một "hoàn tác" đơn giản.
cdunn2001

3
Có sự khác biệt nào giữa HEAD@{1}HEAD~1không?
Neaumusic

15
@neaumusic: vâng! HEAD~1hoàn toàn giống với HEAD^và định danh cha mẹ của cam kết hiện tại. HEAD@{1}mặt khác đề cập đến cam kết mà CHÍNH chỉ ra trước cái này, nghĩa là chúng có nghĩa là các cam kết khác nhau khi bạn kiểm tra một chi nhánh khác hoặc sửa đổi một cam kết.
knittl

@knittl ah không có thắc mắc tôi đã không nghĩ rằng điều này là có thể trước đây, cảm ơn một lần nữa, thông tin tốt
neaumusic

9
bước nắm tay là dư thừa. Đơn giản git reset HEAD@{1}là đủ.
dwelle

79

Tìm các cam kết sửa đổi của bạn bằng cách:

git log --reflog

Lưu ý: Bạn có thể thêm --patchđể xem phần thân của các xác nhận cho rõ ràng. Giống nhưgit reflog .

sau đó đặt lại ĐẦU của bạn thành bất kỳ cam kết nào trước đó tại thời điểm nó ổn bằng cách:

git reset SHA1 --hard

Lưu ý: Thay thế SHA1 bằng hàm băm cam kết thực sự của bạn. Cũng lưu ý rằng lệnh này sẽ mất bất kỳ thay đổi không được cam kết nào, vì vậy bạn có thể bỏ qua chúng trước đó. Ngoài ra, sử dụng --softthay thế để giữ lại những thay đổi mới nhất và sau đó cam kết chúng.

Sau đó chọn cherry cam kết khác mà bạn cần trên đầu trang:

git cherry-pick SHA1

26
Nếu bạn làm như vậy git reset SHA1 --soft, bạn có thể giữ lại những thay đổi mới nhất và sau đó cam kết chúng.
Pravj

24

Bạn luôn có thể chia một cam kết, Từ hướng dẫn

  • Bắt đầu một rebase tương tác với git rebase -i commit ^, trong đó cam kết là cam kết bạn muốn tách. Trong thực tế, bất kỳ phạm vi cam kết nào cũng được, miễn là nó chứa cam kết đó.
  • Đánh dấu cam kết bạn muốn chia bằng hành động "chỉnh sửa".
  • Khi chỉnh sửa cam kết đó, hãy thực hiện git reset ĐẦU ^. Hiệu quả là ĐẦU được lặp lại bởi một, và chỉ số theo sau. Tuy nhiên, cây làm việc vẫn giữ nguyên.
  • Bây giờ thêm các thay đổi vào chỉ mục mà bạn muốn có trong lần xác nhận đầu tiên. Bạn có thể sử dụng git add (có thể tương tác) hoặc git-gui (hoặc cả hai) để làm điều đó.
  • Cam kết chỉ mục hiện tại với bất kỳ thông điệp cam kết nào là phù hợp.
  • Lặp lại hai bước cuối cùng cho đến khi cây làm việc của bạn sạch sẽ.
  • Tiếp tục cuộc nổi loạn với git rebase - liên tục.

26
cách quá phức tạp. git refloglà tất cả những gì bạn cần
knittl

2
Có rất nhiều bước có, nhưng mỗi bước là không phức tạp và dễ làm. Điều này làm việc cho tôi và nhận được phiếu bầu của tôi.
OzBandit

5
ngoài ra, câu trả lời này cho phép bạn chọn lọc các thay đổi mà bạn vô tình 'sửa đổi', để cung cấp một số giá trị bổ sung cho phương pháp git reset --soft HEAD @ {1} (đã giải quyết vấn đề của tôi BTW)
Wiebe Tijsma

2
Bạn cũng có thể chọn lọc các thay đổi với phương thức reflog. Chỉ cần làm git resetthay vì git reset --soft, sau đó làm git add --patch.
geekofalltrades

1
Điều này vẫn viết lại lịch sử và đòi hỏi một lực đẩy. Tùy thuộc vào tình huống của bạn mà có thể hoặc không thể là một vấn đề.
Pajn

20

Có thể đáng lưu ý rằng nếu bạn vẫn còn trong trình soạn thảo của mình với thông điệp cam kết, bạn có thể xóa thông báo cam kết và nó sẽ hủy bỏ git commit --amendlệnh.


Đây là một trong những.
atilkan

Đã cứu tôi nhưng ^^

14

Có lẽ có thể sử dụng git reflog để có được hai cam kết trước khi sửa đổi và sau khi sửa đổi.

Sau đó sử dụng git diff before_commit_id after_commit_id > d.diff để có được khác biệt giữa trước khi sửa đổi và sau khi sửa đổi.

Lần sử dụng tiếp theo git checkout before_commit_id để quay lại trước khi cam kết

Và lần sử dụng cuối cùng git apply d.diff để áp dụng thay đổi thực sự bạn đã làm.

Điều đó giải quyết vấn đề của tôi.


11

Nếu bạn đã đẩy cam kết vào điều khiển từ xa và sau đó sửa đổi sai các thay đổi đối với cam kết đó, điều này sẽ khắc phục vấn đề của bạn. Phát hành a git logđể tìm SHA trước khi cam kết. (điều này giả định từ xa được đặt tên là nguồn gốc). Bây giờ ban hành các lệnh này bằng cách sử dụng SHA đó.

git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit

3
Đây là một trường hợp đặc biệt của câu hỏi chung chung hơn, nhưng nó đáp ứng chính xác nhu cầu của tôi.
dmckee --- ex-moderator mèo con

8

Bạn có thể làm dưới đây để hoàn tác git commit —amend

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

Bây giờ thay đổi của bạn là như trước. Vậy là bạn đã hoàn thành việc hoàn tácgit commit —amend

Bây giờ bạn có thể làm git push origin <your_branch_name>, để đẩy đến chi nhánh.


3

Gần 9 năm sau, nhưng không thấy biến thể này đề cập đến việc hoàn thành điều tương tự (đó là một sự kết hợp của một vài trong số này, tương tự như câu trả lời hàng đầu ( https://stackoverflow.com/a/1459264/4642530 ) .

Tìm kiếm tất cả các đầu tách ra trên chi nhánh

git reflog show origin/BRANCH_NAME --date=relative

Sau đó tìm hàm băm SHA1

Đặt lại về SHA1 cũ

git reset --hard SHA1

Sau đó đẩy nó lên.

git push origin BRANCH_NAME

Làm xong.

Điều này sẽ hoàn nguyên bạn trở lại cam kết cũ hoàn toàn.

(Bao gồm ngày của đầu cam kết được ghi đè trước đó)


Có, nhưng tôi thường muốn đặt lại --softđể giữ những thay đổi của mình. Tôi chỉ muốn nó được cam kết riêng
Juan Mendes

2
  1. Thanh toán cho chi nhánh tạm thời với cam kết cuối cùng

    git branch temp HEAD@{1}

  2. Đặt lại cam kết cuối cùng

    git reset temp

  3. Bây giờ, bạn sẽ có tất cả các tệp cam kết cũng như cam kết trước đó. Kiểm tra trạng thái của tất cả các tập tin.

    git status

  4. Đặt lại các tập tin cam kết của bạn từ giai đoạn git.

    git reset myfile1.js (Sớm)

  5. Reattach cam kết này

    git commit -C HEAD@{1}

  6. Thêm và cam kết các tập tin của bạn để cam kết mới.

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.