Bagaimana cara memasukkan komit antara dua komit arbitrer di masa lalu?

127

Misalkan saya memiliki riwayat komit berikut di cabang khusus lokal saya:

A -- B -- C

Bagaimana cara menyisipkan komit baru antara Adan B?

BartoszKP
sumber
3
komit disebut Ą ? :)
Antek
Saya memiliki pertanyaan yang sangat mirip tentang cara memasukkan versi baru di masa lalu alih-alih komit.
Pavel P

Jawaban:

178

Ini bahkan lebih mudah daripada jawaban OP.

  1. git rebase -i <any earlier commit>. Ini menampilkan daftar komit di editor teks Anda yang dikonfigurasi.
  2. Temukan komit yang ingin Anda masukkan setelahnya (anggap saja itu a1b2c3d). Di editor Anda, untuk baris itu, ubah pickkeedit .
  3. Mulailah rebase dengan menutup editor teks Anda (simpan perubahan Anda). Ini membuat Anda berada pada prompt perintah dengan komit yang Anda pilih sebelumnya ( a1b2c3d) seolah-olah itu baru saja dilakukan .
  4. Lakukan perubahan Anda dan git commit( BUKAN mengubah, tidak seperti kebanyakan edit). Ini membuat komit baru setelah yang Anda pilih.
  5. git rebase --continue. Ini memutar ulang komit yang berurutan, membiarkan komit baru Anda dimasukkan di tempat yang benar.

Berhati-hatilah karena ini akan menulis ulang sejarah, dan menghancurkan siapa pun yang mencoba menariknya.

SLaks
sumber
1
Ini menambahkan komit baru setelah komit, yang setelah komit yang saya rebasing ke (juga komit terakhir), alih-alih tepat setelah yang saya ubah. Hasilnya sama seperti jika saya baru saja membuat komit baru di akhir dengan perubahan yang ingin saya sisipkan. Sejarah saya menjadi A -- B -- C -- Dbukan diinginkan A -- D -- B -- C.
XedinTak Diketahui
2
@XedinTak Diketahui: Maka Anda tidak menggunakan Rebase dengan benar.
SLaks
3
Sekarang Dbisa menjadi komit di mana saja. Misalkan kita memiliki A - B - Cdan kita memiliki beberapa komit Dyang bahkan tidak ada di cabang ini. Kami tahu SHA-nya, tapi kami bisa melakukannya git rebase -i HEAD~3. Sekarang di antara garis Adan B pick, kami menyisipkan baris baru pick yang mengatakan pick SHA, memberikan hash yang diinginkan D. Tidak perlu hash lengkap, cukup hash yang dipersingkat. git rebase -ihanya cherry picks apa pun yang terdaftar dengan pickbaris di buffer; mereka tidak harus yang asli yang terdaftar untuk Anda.
Kaz
1
@Kaz Sepertinya jawaban yang berbeda dan valid.
BartoszKP
3
Lebih mudah lagi, Anda dapat menggunakan breakkata kunci di editor pada barisnya sendiri di antara dua komit (atau pada baris pertama, untuk memasukkan komit sebelum komit yang Anda tentukan).
SimonT
30

Ternyata cukup sederhana, jawabannya ada di sini . Misalkan Anda berada di cabang branch. Lakukan langkah-langkah ini:

  • buat cabang sementara dari komit setelah Anda ingin memasukkan komit baru (dalam hal ini komit A):

    git checkout -b temp A
    
  • melakukan perubahan dan mengkomitnya, membuat komit, sebut saja N:

    git commit -a -m "Message"
    

    (atau git adddiikuti oleh git commit)

  • rebase komit yang ingin Anda miliki setelah komit baru (dalam hal ini komit B dan C) ke komit baru:

    git rebase temp branch
    

(mungkin Anda perlu menggunakan -puntuk mempertahankan penggabungan, jika ada - terima kasih kepada komentar tidak lagi ada oleh ciekawy )

  • hapus cabang sementara:

    git branch -d temp
    

Setelah ini, sejarahnya terlihat sebagai berikut:

A -- N -- B -- C

Ada kemungkinan bahwa beberapa konflik akan muncul saat rebasing.

Jika cabang Anda bukan cabang lokal, ini akan memperkenalkan sejarah penulisan ulang, jadi bisa menyebabkan masalah serius.

BartoszKP
sumber
2
Saya tidak bisa mengikuti jawaban yang diterima oleh SLaks, tetapi ini berhasil untuk saya. Setelah saya mendapatkan riwayat komit yang saya inginkan, saya harus git push --forcemengubah repo jarak jauh.
escapecharacter
1
Saat menggunakan rebase menggunakan opsi -Xtheirs otomatis menyelesaikan konflik dengan benar, jadi git rebase temp branch -Xtheirs. Jawaban yang berguna untuk memasukkan skrip!
David C
Untuk pemula seperti saya, saya ingin menambahkannya setelahnya git rebase temp branch, tetapi sebelumnya git branch -d temp, yang harus Anda lakukan hanyalah memperbaiki dan menyelesaikan konflik dan masalah penggabungan git rebase --continue, yaitu tidak perlu melakukan apa pun, dll.
Pugsley
19

Solusi yang lebih mudah:

  1. Buat komit baru Anda di akhir, D.Sekarang Anda memiliki:

    A -- B -- C -- D
    
  2. Lalu lari:

    $ git rebase -i hash-of-A
    
  3. Git akan membuka editor Anda dan akan terlihat seperti ini:

    pick 8668d21 B
    pick 650f1fc C
    pick 74096b9 D
    
  4. Pindahkan saja D ke atas seperti ini, lalu simpan dan keluar

    pick 74096b9 D
    pick 8668d21 B
    pick 650f1fc C
    
  5. Sekarang Anda akan memiliki:

    A -- D -- B -- C
    
Matthew
sumber
6
Ide yang bagus, namun mungkin sulit untuk memperkenalkan D pada C, jika Anda bermaksud agar perubahan ini ditulis. kepada A.
BartoszKP
Saya memiliki situasi di mana saya memiliki 3 komitmen yang ingin saya buat ulang bersama dan komitmen di tengah yang tidak terkait. Ini sangat bagus untuk bisa memindahkan komit itu sebelumnya atau nanti ke baris komit.
melepaskan
13

Dengan asumsi bahwa riwayat komit adalah preA -- A -- B -- C, jika Anda ingin menyisipkan komit antara Adan B, langkah-langkahnya adalah sebagai berikut:

  1. git rebase -i hash-of-preA

  2. Git akan membuka editor Anda. Kontennya mungkin seperti ini:

    pick 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Ubah yang pertama pickmenjadi edit:

    edit 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Simpan dan keluar.

  3. Ubah kode Anda lalu git add . && git commit -m "I"

  4. git rebase --continue

Sekarang riwayat komit Git Anda adalah preA -- A -- I -- B -- C


Jika Anda menemui konflik, Git akan berhenti pada komit ini. Anda dapat menggunakan git diffuntuk menemukan penanda konflik dan mengatasinya. Setelah menyelesaikan semua konflik, Anda perlu menggunakan git add <filename>untuk memberi tahu Git bahwa konflik telah diselesaikan dan kemudian menjalankan kembaligit rebase --continue .

Jika Anda ingin membatalkan rebase, gunakan git rebase --abort.

haolee
sumber
11

Berikut adalah strategi yang menghindari melakukan "edit hack" selama rebase terlihat pada jawaban lain yang saya baca.

Dengan menggunakan git rebase -iAnda mendapatkan daftar komit sejak komit itu. Tambahkan saja "break" di bagian atas file, ini akan menyebabkan rebase rusak pada saat itu.

break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>

Setelah diluncurkan, git rebasesekarang akan berhenti di titik "istirahat". Anda sekarang dapat mengedit file Anda dan membuat komit Anda secara normal. Anda kemudian dapat melanjutkan rebase dengan git rebase --continue. Ini dapat menyebabkan konflik yang harus Anda perbaiki. Jika Anda tersesat, jangan lupa Anda selalu dapat membatalkan penggunaan git rebase --abort.

Strategi ini dapat digeneralisasikan untuk memasukkan komit di mana saja, cukup letakkan "istirahat" di tempat Anda ingin memasukkan komit.

Setelah menulis ulang sejarah, jangan lupa git push -f. Peringatan biasa tentang orang lain yang mengambil cabang Anda berlaku.

axerologementy
sumber
Maaf, tapi saya kesulitan memahami bagaimana ini "menghindari rebase". Anda sedang berlari di rebasesini. Tidak banyak perbedaan apakah Anda akan membuat komit selama rebase atau sebelumnya.
BartoszKP
Waduh, maksud saya menghindari "edit hack" selama rebase, saya kira saya mengucapkannya dengan buruk.
axerologementy
Baik. Jawaban saya juga tidak menggunakan fitur "edit" rebase. Namun, ini adalah pendekatan valid lainnya - terima kasih! :-)
BartoszKP
Sejauh ini, ini adalah solusi terbaik!
Theodore R. Smith
6

Sudah banyak jawaban bagus di sini. Saya hanya ingin menambahkan solusi "tanpa rebase", dalam 4 langkah mudah.


Ringkasan

git checkout A
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD

Penjelasan

(Catatan: satu keuntungan dari solusi ini adalah Anda tidak menyentuh cabang Anda sampai langkah terakhir, ketika Anda 100% yakin Anda baik-baik saja dengan hasil akhirnya, jadi Anda memiliki langkah "pra-konfirmasi" yang sangat berguna memungkinkan untuk pengujian AB .)


Keadaan awal (saya asumsikan masteruntuk nama cabang Anda)

A -- B -- C <<< master <<< HEAD

1) Mulailah dengan mengarahkan HEAD ke tempat yang tepat

git checkout A

     B -- C <<< master
    /
   A  <<< detached HEAD

(Secara opsional di sini, daripada melepaskan HEAD, kita dapat membuat cabang sementara dengan git checkout -b temp A, yang perlu kita hapus di akhir proses. Kedua varian bekerja, lakukan seperti yang Anda inginkan karena yang lainnya tetap sama)


2) Buat komit D baru untuk dimasukkan

# at this point, make the changes you wanted to insert between A and B, then

git commit -am "Message for commit D"

     B -- C <<< master
    /
   A -- D <<< detached HEAD (or <<< temp <<< HEAD)

3) Kemudian bawa salinan dari komit B dan C yang terakhir hilang (akan menjadi baris yang sama jika ada lebih banyak komit)

git cherry-pick A..C

# (if any, resolve any potential conflicts between D and these last commits)

     B -- C <<< master
    /
   A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)

(Pengujian AB nyaman di sini jika diperlukan)

Sekarang saatnya untuk memeriksa kode Anda, menguji apa pun yang perlu diuji, dan Anda juga dapat membedakan / membandingkan / memeriksa apa yang Anda miliki dan apa yang akan Anda dapatkan setelah operasi.


4) Tergantung pada pengujian Anda antara Cdan C', apakah OK atau KO.

(BAIK) 4-OK) Akhirnya, pindahkan ref darimaster

git branch -f master HEAD

     B -- C <<< (B and C are candidates for garbage collection)
    /
   A -- D -- B' -- C' <<< master

(OR) 4-KO) Biarkan mastertidak berubah

Jika Anda membuat cabang sementara, hapus saja dengan git branch -d <name>, tetapi jika Anda memilih rute HEAD yang terpisah, tidak ada tindakan yang diperlukan sama sekali pada saat ini, komit baru akan memenuhi syarat untuk pengumpulan sampah tepat setelah Anda melampirkan kembali HEADdengangit checkout master

Dalam kedua kasus tersebut (OK atau KO), pada saat ini hanya checkout masterlagi untuk memasang kembali HEAD.

RomainValeri
sumber