Bagaimana saya bisa memisahkan sebuah komit Git yang terkubur dalam sejarah?

292

Saya gagal dalam sejarah saya dan ingin melakukan beberapa perubahan. Masalahnya adalah, saya memiliki komit dengan dua perubahan yang tidak terkait, dan komit ini dikelilingi oleh beberapa perubahan lain dalam sejarah lokal saya (non-push).

Saya ingin memisahkan komit ini sebelum mendorongnya, tetapi sebagian besar panduan yang saya lihat berkaitan dengan pemisahan komit terbaru Anda, atau perubahan lokal yang tidak dikomit. Apakah layak untuk melakukan ini pada sebuah komit yang terkubur dalam sejarah sedikit, tanpa harus "melakukan kembali" komitmen saya sejak saat itu?

Ben
sumber

Jawaban:

450

Ada panduan untuk membagi komitmen di halaman rebase . Ringkasan singkatnya adalah:

  • Lakukan rebase interaktif termasuk komit target (mis. git rebase -i <commit-to-split>^ branch) Dan tandai untuk diedit.

  • Saat rebase mencapai komit itu, gunakan git reset HEAD^untuk mengatur ulang sebelum komit, tetapi pertahankan pohon kerja Anda tetap utuh.

  • Tambahkan perubahan secara bertahap dan komit, buat komitmen sebanyak yang diinginkan. add -pdapat bermanfaat untuk menambahkan hanya beberapa perubahan dalam file yang diberikan. Gunakan commit -c ORIG_HEADjika Anda ingin menggunakan kembali pesan komit asli untuk komit tertentu.

  • Jika Anda ingin menguji apa yang Anda lakukan (ide bagus!) Gunakan git stashuntuk menyembunyikan bagian yang belum Anda lakukan (atau stash --keep-indexbahkan sebelum Anda melakukannya), uji, kemudian git stash popuntuk mengembalikan sisanya ke pohon kerja. Terus membuat komitmen sampai Anda mendapatkan semua modifikasi yang dilakukan, yaitu memiliki pohon kerja yang bersih.

  • Jalankan git rebase --continueuntuk melanjutkan menerapkan komit setelah komit sekarang-split.

Cascabel
sumber
17
... tapi jangan lakukan itu jika Anda sudah mendorong sejarah sejak komit untuk dibagi.
wilhelmtell
29
@wilhelmtell: Saya menghilangkan pelat boiler "" berpotensi berbahaya; lihat 'pulih dari hulu rebase' "karena OP secara eksplisit mengatakan dia tidak mendorong sejarah ini.
Cascabel
2
dan Anda membuat bacaan yang sempurna. Saya mencoba untuk menghindari 'boilerplate' ketika saya tentukan itu belum berbagi sejarah :) Dalam hal apa pun, saya telah berhasil dengan saran Anda. Ini adalah rasa sakit yang besar untuk melakukan hal-hal ini setelah fakta. Saya telah belajar pelajaran di sini, dan itu adalah untuk memastikan komitmen dimasukkan dengan benar untuk memulai!
Ben
2
Langkah pertama mungkin lebih baik dinyatakan sebagai git rebase -i <sha1_of_the_commit_to_split>^ branch. Dan git guimerupakan alat yang bagus untuk tugas pemisahan, yang dapat digunakan untuk menambahkan berbagai bagian file ke dalam berbagai komitmen.
Qiang Xu
3
@QiangXu: Yang pertama adalah saran yang masuk akal. Yang kedua adalah persis mengapa saya menyarankan git add -p, yang bisa melakukan lebih dari yang git guibisa di departemen ini (terutama mengedit bakhil, pementasan segalanya mulai dari cowok saat ini, dan mencari bakhil oleh regex).
Cascabel
3

Inilah cara melakukannya dengan Magit .

Katakan komit ed417ae adalah yang ingin Anda ubah; itu berisi dua perubahan yang tidak terkait dan dimakamkan di bawah satu atau lebih komit. Tekan lluntuk menampilkan log, dan navigasikan ke ed417ae:

log awal

Kemudian tekan runtuk membuka pop-up rebase

rebase popup

dan muntuk memodifikasi komit pada titik.

Perhatikan bagaimana @ada sekarang di komit yang ingin Anda bagi - itu berarti HEAD sekarang di komit itu:

memodifikasi komit

Kami ingin memindahkan KEPALA ke orang tua, jadi navigasikan ke orang tua (47e18b3) dan tekan x( magit-reset-quickly, pasti ojika Anda menggunakan evil-magit) dan masukkan untuk mengatakan "ya saya maksud komit pada titik". Log Anda sekarang akan terlihat seperti:

log setelah mengatur ulang

Sekarang, tekan quntuk pergi ke status Magit biasa, kemudian gunakan uperintah unstage biasa untuk menghapus apa yang tidak masuk dalam komit pertama, komit csisanya seperti biasa, kemudian stage dan cabaikan apa yang terjadi di komit kedua, dan ketika dilakukan: tekan runtuk membuka popup rebase

rebase popup

dan yang lainnya runtuk melanjutkan, dan Anda selesai! llsekarang menunjukkan:

semua selesai log

tidak tahu malu
sumber
1

Untuk membagi komit <commit>dan menambahkan komit baru sebelum ini , dan simpan tanggal penulis <commit>, - langkah-langkahnya sebagai berikut:

  1. Edit komit sebelumnya <commit>

    git rebase -i <commit>^^
    

    NB: mungkin juga perlu diedit <commit>.

  2. Cherry memilih <commit>ke dalam indeks

    git cherry-pick -n <commit>
    
  3. Reset secara interaktif perubahan yang tidak diperlukan dari indeks dan reset pohon kerja

    git reset -p && git checkout-index -f -a
    

    Sebagai alternatif, cukup sembunyikan perubahan yang tidak dibutuhkan secara interaktif: git stash push -p -m "tmp other changes"

  4. Buat perubahan lain (jika ada) dan buat komit baru

    git commit -m "upd something" .
    

    Secara opsional, ulangi item 2-4 untuk menambahkan lebih banyak komitmen menengah.

  5. Lanjutkan rebasing

    git rebase --continue
    
ruvim
sumber
0

Ada versi yang lebih cepat jika Anda hanya ingin mengekstraksi konten hanya dari satu file. Ini lebih cepat karena rebase interaktif sebenarnya tidak interaktif lagi (dan tentu saja bahkan lebih cepat jika Anda ingin mengekstrak dari komit terakhir, maka tidak perlu rebase sama sekali)

  1. Gunakan editor Anda dan hapus baris yang ingin Anda ekstrak the_file. Tutup the_file. Itu satu-satunya edisi yang Anda butuhkan, sisanya hanya perintah git.
  2. Tahap penghapusan dalam indeks:

    git  add  the_file
    
  3. Kembalikan baris yang baru saja Anda hapus kembali ke file tanpa mempengaruhi indeks !

    git show HEAD:./the_file > the_file
    
  4. "SHA1" adalah komit yang ingin Anda ekstrak dari:

    git commit -m 'fixup! SHA1' 
    
  5. Buat yang kedua, komitmen baru dengan konten yang akan diekstrak dipulihkan oleh langkah 3:

    git commit -m 'second and new commit' the_file 
    
  6. Jangan edit, jangan berhenti / lanjutkan - terima saja segalanya:

    git rebase --autosquash -i SHA1~1
    

Tentu saja bahkan lebih cepat ketika komit untuk mengekstrak dari adalah komit terakhir:

4. git commit -C HEAD --amend
5. git commit -m 'second and new commit' thefile
6. no rebase, nothing

Jika Anda menggunakan magitmaka langkah 4, 5 dan 6 adalah tindakan tunggal: Komit, Perbaikan instan

Maret
sumber
-2

Jika Anda belum mendorong, gunakan saja git rebase. Lebih baik lagi, gunakan git rebase -iuntuk memindahkan komit secara interaktif. Anda dapat memindahkan komit yang menyinggung ke depan, lalu membaginya sesuka Anda dan memindahkan tambalan kembali (jika perlu).

Gintautas Miliauskas
sumber
14
Tidak perlu memindahkannya ke mana pun. Membaginya di tempat itu.
Cascabel
1
Sayangnya, ini tidak berfungsi untuk saya karena beberapa riwayat setelah komit bergantung padanya, jadi saya sedikit dibatasi. Namun, ini akan menjadi pilihan pertama saya.
Ben
@ Ben: tidak apa-apa - komit setelah itu tidak perlu diubah sama sekali (dengan asumsi Anda menyimpan semua perubahan, daripada membuang beberapa dari mereka). Informasi lebih lanjut di sini - stackoverflow.com/questions/1440050/…
Ether