Cách thêm tệp đã thay đổi vào một cam kết cũ (không phải cuối cùng) trong Git


461

Tôi đã thay đổi một số thứ trong giờ qua và cam kết từng bước một, nhưng tôi nhận ra rằng tôi đã quên thêm một tệp đã thay đổi một số cam kết trước đây.

Nhật ký trông như thế này:

    GIT TidyUpRequests u:1 d:0> git log 
    commit fc6734b6351f6c36a587dba6dbd9d5efa30c09ce 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:43:55 2010 +0200

        The Main program now tests both Webservices at once

    commit 8a2c6014c2b035e37aebd310a6393a1ecb39f463 
    Author: David Klein <>
    Date:   Tue Apr 27 09:43:27 2010 +0200

        ISBNDBQueryHandler now uses the XPath functions from XPath.fs too

    commit 06a504e277fd98d97eed4dad22dfa5933d81451f 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:30:34 2010 +0200

        AmazonQueryHandler now uses the XPath Helper functions defined in XPath.fs

    commit a0865e28be35a3011d0b6091819ec32922dd2dd8 <--- changed file should go here
    Author: David Klein <> 
    Date:   Tue Apr 27 09:29:53 2010 +0200

        Factored out some common XPath Operations

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


Câu trả lời:


694

Sử dụng git rebase. Đặc biệt:

  1. Sử dụng git stashđể lưu trữ những thay đổi bạn muốn thêm.
  2. Sử dụng git rebase -i HEAD~10(hoặc tuy nhiên nhiều cam kết trở lại mà bạn muốn xem).
  3. Đánh dấu cam kết trong câu hỏi ( a0865...) để chỉnh sửa bằng cách thay đổi từ pickở đầu dòng thành edit. Đừng xóa các dòng khác vì điều đó sẽ xóa các xác nhận. [^ Vimnote]
  4. Lưu tệp rebase và git sẽ quay trở lại trình bao và chờ bạn sửa lỗi cam kết đó.
  5. Bật stash bằng cách sử dụng git stash pop
  6. Thêm tập tin của bạn với git add <file>.
  7. Sửa đổi cam kết với git commit --amend --no-edit.
  8. Làm một git rebase --continuecái sẽ viết lại phần còn lại của cam kết của bạn so với cái mới.
  9. Lặp lại từ bước 2 trở đi nếu bạn đã đánh dấu nhiều hơn một cam kết để chỉnh sửa.

[^ vimnote]: Nếu bạn đang sử dụng vimthì bạn sẽ phải nhấn Insertphím để chỉnh sửa, sau đó Escnhập vào :wqđể lưu tệp, thoát trình chỉnh sửa và áp dụng các thay đổi. Ngoài ra, bạn có thể cấu hình một git sử dụng cam kết biên tập với git config --global core.editor "nano".


23
Điều gì xảy ra nếu bạn có những thay đổi chưa được chỉnh sửa mà bạn muốn thêm vào chỉnh sửa? Nếu tôi cất chúng, tôi không thể git add.
Sam

15
Sam bạn chỉ có thể hủy bỏ các thay đổi trong khi trên cam kết trong câu hỏi, nó sẽ hoạt động tốt.
omnikron

17
Lưu ý: Khi bạn đánh dấu cam kết bằng edit, KHÔNG XÓA các cam kết khác được liệt kê trong tệp. Nếu bạn làm như vậy, các cam kết sẽ bị xóa và bạn sẽ phải làm theo các bước sau để lấy lại chúng.
David Tuite 4/12/13

2
Về những gì @DavidTuite đã nói, một thói quen của tôi khi thực hiện công việc trên git mà tôi không chắc chắn sẽ tạo ra một nhánh "nhánh tên-ref" để lưu trạng thái hiện tại của dòng thời gian trong trường hợp tôi làm hỏng mọi thứ. Khi tôi hoàn thành, tôi xóa nó.
Raphael

1
Trong bước 6, bao gồm. (dấu chấm) trong lệnh. Lệnh đúng:git add .
Tom

323

Để "sửa" một cam kết cũ với một thay đổi nhỏ, mà không thay đổi thông báo cam kết của cam kết cũ, nơi OLDCOMMITgiống như 091b73a:

git add <my fixed files>
git commit --fixup=OLDCOMMIT
git rebase --interactive --autosquash OLDCOMMIT^

Bạn cũng có thể sử dụng git commit --squash=OLDCOMMITđể chỉnh sửa thông điệp cam kết cũ trong quá trình rebase.


  • git rebase --interactivesẽ đưa ra một trình soạn thảo văn bản ( có thể được cấu hình ) để xác nhận (hoặc chỉnh sửa) chuỗi lệnh rebase . Có thông tin cho các thay đổi hướng dẫn rebase trong tệp; chỉ cần lưu và thoát khỏi trình soạn thảo ( :wqinvim ) để tiếp tục với rebase.
  • --autosquashsẽ tự động đặt bất kỳ --fixup=OLDCOMMITcam kết nào theo thứ tự mong muốn. Lưu ý rằng --autosquashchỉ có hiệu lực khi --interactivetùy chọn được sử dụng.
  • Các ^trong OLDCOMMIT^phương tiện đó là một tham chiếu đến các cam kết ngay trước OLDCOMMIT.

Các bước trên là tốt để xác minh và / hoặc sửa đổi trình tự lệnh rebase , nhưng cũng có thể bỏ qua / tự động hóa trình soạn thảo văn bản rebase tương tác bằng cách:

Xem git commitgit rebase . Như mọi khi, khi viết lại lịch sử git , bạn chỉ nên sửa lỗi hoặc squash mà bạn chưa công bố cho bất kỳ ai khác (bao gồm cả người dùng internet ngẫu nhiên và xây dựng máy chủ).


18
Rõ ràng hơn nhiều so với các tùy chọn khác, và nó hoạt động như một cơ duyên
Chris Mitchelmore

5
@Jonah: trình chỉnh sửa không được mở để chỉnh sửa thông điệp cam kết , nhưng để xác nhận (hoặc chỉnh sửa) các bước rebase . Nó không thể tránh được; --autosquashchỉ có hiệu lực khi --interactivetùy chọn được sử dụng .
Joel Purra

5
Sử dụng giải pháp này tôi bị kẹt git hiển thị VIM. Vấn đề của tôi là, tôi không biết cách sử dụng VIM. Làm thế quái nào tôi thoát khỏi nó, làm thế nào tôi có thể kiểm soát điều này, điều này rất khó hiểu.
Cuộc chiến tranh

2
@NeonWarge: trình soạn thảo lựa chọn có thể định cấu hình bằng cách sử dụng ví dụ git config --global core.editor "pico". Có một số cách khác để định cấu hình git và / hoặc thay đổi trình soạn thảo mặc định của hệ thống, v.v.
Joel Purra

2
một dòng bí danh đẹp ở đây
idanp

61

với git 1.7, có một cách thực sự dễ dàng bằng cách sử dụng git rebase:

giai đoạn tập tin của bạn:

git add $files

tạo một cam kết mới và sử dụng lại thông điệp cam kết của cam kết "bị hỏng" của bạn

git commit -c master~4

thêm fixup!vào dòng tiêu đề (hoặc squash!nếu bạn muốn chỉnh sửa cam kết (tin nhắn)):

fixup! Factored out some common XPath Operations

sử dụng git rebase -i --autosquashđể sửa chữa cam kết của bạn


2
+1. Sử dụng tốt chỉ thị sửa lỗi mới (1.7+): stackoverflow.com/questions/2302736/trimming-git-checkins/
VonC

@knittl Tôi đã thử phương pháp của bạn để thêm một tệp khác vào một cam kết cũ của tôi (không bị đẩy) nhưng khi khởi động lại tôi nhận được You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge'và nếu sau đó git rebase -i --autosquashtôi sử dụng tôi chỉ nhận được một noopdòng chủ đề, từ chối cam kết vào chính nó. Bất cứ ý tưởng những gì tôi làm sai?
oschrenk

7
@oschrenk: Bạn cần cung cấp một cam kết mà bạn muốn rebase, ví dụ:git rebase -i --autosquash HEAD~10
knittl

1
Câu trả lời tốt nhưng tôi cũng cần thêm một cam kết để phản đối. Sẽ là tuyệt vời nếu bạn có thể cập nhật nó.
Paul Odeon

@PaulOdeon: Tôi không hiểu câu hỏi của bạn. Bạn đang cố gắng làm gì, và bạn đang gặp vấn đề ở đâu?
knittl

8

Bạn có thể thử một rebase --interactivephiên để sửa đổi cam kết cũ của mình (với điều kiện bạn chưa đẩy các cam kết đó sang một repo khác).

Đôi khi điều cố định trong b.2. không thể sửa đổi thành cam kết không hoàn hảo mà nó sửa, bởi vì cam kết đó bị chôn sâu trong một loạt các bản vá .
Đó chính xác là những gì rebase tương tác dành cho: sử dụng nó sau nhiều "a" và "b", bằng cách sắp xếp lại và chỉnh sửa các cam kết, và nghiền nát nhiều lần xác nhận thành một.

Bắt đầu với cam kết cuối cùng mà bạn muốn giữ nguyên trạng:

git rebase -i <after-this-commit>

Một trình soạn thảo sẽ được kích hoạt với tất cả các cam kết trong nhánh hiện tại của bạn (bỏ qua các cam kết hợp nhất), xuất hiện sau cam kết đã cho.
Bạn có thể sắp xếp lại các cam kết trong danh sách này theo nội dung trái tim của bạn và bạn có thể xóa chúng. Danh sách trông ít nhiều như thế này:

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

Các mô tả trực tuyến là hoàn toàn cho niềm vui của bạn; git rebase sẽ không nhìn vào chúng mà chỉ nhìn vào tên cam kết ("deadbee" và "fa1afe1" trong ví dụ này), vì vậy đừng xóa hoặc chỉnh sửa tên.

Bằng cách thay thế lệnh "chọn" bằng lệnh "chỉnh sửa", bạn có thể yêu cầu git rebase dừng lại sau khi áp dụng cam kết đó, để bạn có thể chỉnh sửa các tệp và / hoặc thông báo cam kết, sửa đổi cam kết và tiếp tục khởi động lạ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.