Bagaimana cara menambahkan file yang diubah ke komit yang lebih lama (bukan yang terakhir) di Git

461

Saya telah mengubah beberapa hal selama satu jam terakhir dan melakukan mereka langkah demi langkah, tetapi saya baru sadar bahwa saya lupa menambahkan file yang diubah beberapa komitmen lalu.

Log terlihat seperti ini:

    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

Ada ide?

Leen
sumber
1
Pada dasarnya, duplikat dari Bagaimana cara memodifikasi komit yang ditentukan?
Dan Dascalescu

Jawaban:

694

Gunakan git rebase. Secara khusus:

  1. Menggunakan git stash untuk menyimpan perubahan yang ingin Anda tambahkan.
  2. Gunakan git rebase -i HEAD~10(atau seberapa banyak banyak komitmen yang ingin Anda lihat).
  3. Tandai komit dalam pertanyaan ( a0865...) untuk diedit dengan mengubah kata pickdi awal baris menjadi edit. Jangan hapus baris lain karena itu akan menghapus komit. [^ Vimnote]
  4. Simpan file rebase, dan git akan kembali ke shell dan menunggu Anda untuk memperbaikinya.
  5. Pop simpanan dengan menggunakan git stash pop
  6. Tambahkan file Anda dengan git add <file>.
  7. Ubah komit dengan git commit --amend --no-edit.
  8. Lakukan git rebase --continueyang akan menulis ulang sisa komit Anda terhadap yang baru.
  9. Ulangi dari langkah 2 dan seterusnya jika Anda telah menandai lebih dari satu komit untuk diedit.

[^ vimnote]: Jika Anda menggunakan vimmaka Anda harus menekan Inserttombol untuk mengedit, lalu Escdan ketik :wquntuk menyimpan file, keluar dari editor, dan terapkan perubahannya. Atau, Anda dapat mengkonfigurasi editor git commit yang mudah digunakan dengan git config --global core.editor "nano".

Greg Hewgill
sumber
23
Bagaimana jika Anda memiliki perubahan tidak bertahap yang ingin Anda tambahkan ke edit? Jika saya menyembunyikannya, saya tidak bisa git add.
Sam
15
Anda hanya bisa melepas perubahan sementara pada komit yang bersangkutan, itu akan berfungsi dengan baik.
omnikron
17
Catatan: Saat Anda menandai komit dengan edit, JANGAN HAPUS komit lain yang tercantum dalam file. Jika ya, komit akan dihapus dan Anda harus mengikuti langkah-langkah ini untuk mendapatkannya kembali.
David Tuite
2
Mengenai apa yang dikatakan @DavidTuite, kebiasaan saya ketika melakukan hal-hal di git yang saya tidak yakin bagaimana hasilnya adalah membuat cabang "branchname-ref" untuk menyelamatkan keadaan saat ini dari timeline jika saya mengacaukan semuanya. Setelah selesai, saya menghapusnya.
Raphael
1
Pada langkah 6, sertakan. (titik) dalam perintah. Perintah yang benar:git add .
Tom
323

Untuk "memperbaiki" komit lama dengan perubahan kecil, tanpa mengubah pesan komit dari komit lama, di mana OLDCOMMITada sesuatu seperti 091b73a:

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

Anda juga dapat menggunakan git commit --squash=OLDCOMMITuntuk mengedit pesan komit lama selama rebase.


  • git rebase --interactiveakan memunculkan editor teks (yang dapat dikonfigurasi ) untuk mengonfirmasi (atau mengedit) urutan instruksi rebase . Ada info untuk perubahan instruksi rebase dalam file; cukup simpan dan keluar dari editor ( :wqdalamvim ) untuk melanjutkan rebase.
  • --autosquashsecara otomatis akan menempatkan --fixup=OLDCOMMITkomit dalam urutan yang diinginkan. Perhatikan bahwa --autosquashini hanya valid ketika --interactiveopsi digunakan.
  • The ^dalam OLDCOMMIT^cara itu referensi untuk komit sebelum OLDCOMMIT.

Langkah-langkah di atas baik untuk verifikasi dan / atau memodifikasi urutan instruksi rebase , tetapi juga memungkinkan untuk melompati / mengotomatiskan editor teks rebase interaktif dengan:

Lihat git commit dan git rebase . Seperti biasa, ketika menulis ulang sejarah git , Anda hanya harus memperbaiki atau squash yang belum Anda publikasikan kepada orang lain (termasuk pengguna internet acak dan server yang dibangun).

Joel Purra
sumber
18
Jauh lebih jelas daripada opsi lain, dan itu berfungsi seperti pesona
Chris Mitchelmore
5
@Jonah: editor tidak dibuka untuk mengedit pesan komit , tetapi untuk mengkonfirmasi (atau mengedit) langkah rebase . Itu tidak bisa dihindari; --autosquashhanya valid ketika --interactiveopsi digunakan .
Joel Purra
5
Menggunakan solusi ini saya terjebak git menampilkan VIM. Masalah saya adalah, saya tidak tahu cara menggunakan VIM. Bagaimana cara saya keluar dari itu, bagaimana saya bisa mengendalikan hal ini, ini sangat membingungkan.
Neon Warge
2
@NeonWarge: editor pilihan dapat dikonfigurasi menggunakan misalnya git config --global core.editor "pico". Ada beberapa cara lain untuk mengkonfigurasi git dan / atau mengubah editor default sistem Anda dan sebagainya.
Joel Purra
2
satu baris alias bagus di sini
idanp
61

dengan git 1.7, ada cara yang sangat mudah menggunakan git rebase:

tahapkan file Anda:

git add $files

buat komit baru dan gunakan kembali pesan komit dari komit "rusak" Anda

git commit -c master~4

tambahkan fixup!di baris subjek (atau squash!jika Anda ingin mengedit commit (pesan)):

fixup! Factored out some common XPath Operations

gunakan git rebase -i --autosquashuntuk memperbaiki komit Anda

rajutan
sumber
2
+1. Penggunaan yang baik dari arahan fixup baru (1,7+): stackoverflow.com/questions/2302736/trimming-git-checkins/…
VonC
@knittl Saya mencoba metode Anda untuk menambahkan file lain ke komit lama saya (tidak didorong) tetapi ketika rebasing saya dapatkan You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge'dan jika saya kemudian menggunakan git rebase -i --autosquashsaya hanya mendapatkan noopbaris subjek, rebasing komit ke dirinya sendiri. Adakah yang saya tahu salah?
oschrenk
7
@oschrenk: Anda harus memberikan komit yang ingin Anda rebase, misalnyagit rebase -i --autosquash HEAD~10
knittl
1
Jawaban yang bagus tetapi saya juga perlu menambahkan komit yang akan ditata ulang. Akan lebih bagus jika Anda bisa memperbaruinya.
Paul Odeon
@ PaulOdeon: Saya tidak mengerti pertanyaan Anda. Apa yang Anda coba lakukan, dan di mana Anda mengalami masalah?
Knittl
8

Anda dapat mencoba rebase --interactivesesi untuk mengubah komit lama Anda (asalkan Anda belum mendorong komit tersebut ke repo lain).

Terkadang benda diperbaiki di b.2. tidak dapat diubah ke komit yang tidak cukup sempurna untuk memperbaikinya, karena komit tersebut terkubur dalam-dalam dalam seri tambalan .
Itulah tepatnya apa rebase interaktif: gunakan setelah banyak "a" dan "b", dengan menata ulang dan mengedit komit, dan menekan beberapa komit menjadi satu.

Mulai dengan komit terakhir yang ingin Anda pertahankan apa adanya:

git rebase -i <after-this-commit>

Editor akan bersemangat dengan semua komit di cabang Anda saat ini (mengabaikan komit gabungan), yang terjadi setelah komit yang diberikan.
Anda dapat menyusun ulang komitmen dalam daftar ini sesuai dengan isi hati Anda, dan Anda dapat menghapusnya. Daftarnya kurang lebih seperti ini:

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

Deskripsi oneline murni untuk kesenangan Anda; git rebase tidak akan melihat mereka tetapi pada nama commit ("deadbee" dan "fa1afe1" dalam contoh ini), jadi jangan hapus atau edit nama.

Dengan mengganti perintah "pilih" dengan perintah "edit", Anda dapat memberitahu git rebase untuk berhenti setelah menerapkan komit itu, sehingga Anda dapat mengedit file dan / atau pesan komit, mengubah komit, dan melanjutkan rebasing .

VONC
sumber