Bagaimana cara menekan dua komitmen yang tidak berurutan?

183

Saya sedikit baru dengan fitur rebasing keseluruhan dalam git. Katakanlah saya membuat komitmen berikut:

A -> B -> C -> D

Setelah itu, saya menyadari bahwa Dberisi perbaikan yang tergantung pada beberapa kode baru yang ditambahkan A, dan bahwa komit ini dimiliki bersama. Bagaimana saya terjepit A& Dbersama dan pergi B& Csendiri?

Nik Reiman
sumber

Jawaban:

258

Anda dapat menjalankan git rebase --interactivedan menyusun ulang D sebelum B dan memasukkan squash D ke A.

Git akan membuka editor, dan Anda melihat file seperti ini, mis: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Sekarang Anda mengubah file itu menjadi seperti ini:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

Dan git sekarang akan menggabungkan perubahan A dan D menjadi satu komit, dan menempatkan B dan C sesudahnya. Ketika Anda tidak ingin menyimpan pesan komit D, alih-alih squash, Anda akan menggunakan fixupkata kunci. Untuk lebih lanjut fixup, Anda dapat berkonsultasi dengan git rebasedokumen , atau memeriksa pertanyaan ini yang memiliki beberapa jawaban bagus.

Rudi
sumber
5
Awalnya, saya membacanya sebagai "rebase D ke A, squash D menjadi A, lalu rebase B ke DA". Tidak jelas dari jawaban bahwa ini bisa dilakukan dengan menyusun ulang baris dalam editor teks.
Victor Sergienko
3
Jika cabang Anda adalah lokal, Anda akan mendapatkan There is no tracking information for the current branchkesalahan saat rebasing. Dalam hal ini Anda perlu menentukan jumlah komit Anda ingin bekerja dengan, seperti ini: git rebase -i HEAD~4. Lihat jawaban ini .
johndodo
1
Saya menggunakan mode interaktif (git rebase -i) selama bertahun-tahun, saya baru sadar itu bisa dipesan ulang . Terima kasih 🤟🏻
CalvinChe
43

Catatan: Anda tidak boleh mengubah komit yang telah didorong ke repo lain dengan cara apa pun kecuali Anda tahu konsekuensinya .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Ketik i(Masukkan VIM dalam mode penyisipan)

Ubah daftar agar terlihat seperti ini (Anda tidak harus menghapus atau memasukkan pesan komit). Jangan salah mengeja squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Ketik Esckemudian ZZ(Simpan dan keluar dari VIM)

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Tipe i

Ubah teks menjadi seperti apa Anda ingin pesan komit baru terlihat. Saya merekomendasikan ini sebagai deskripsi perubahan dalam komit Adan D:

new_commit_message_for_A_and_D

Ketikkan EsckemudianZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Anda sekarang telah membuat komit baru E. Berkomitmen Adan Dtidak lagi dalam sejarah Anda tetapi tidak hilang. Anda masih dapat memulihkannya pada saat ini dan untuk sementara waktu git rebase --hard D( git rebase --hardakan menghancurkan perubahan lokal! ).

Nate
sumber
3

Bagi mereka yang menggunakan SourceTree :

Pastikan Anda belum mendorong komit.

  1. Repositori> Rebase Interaktif ...
  2. Seret D (komit yang lebih baru) untuk berada tepat di atas A (komit yang lebih lama)
  3. Pastikan komit D disorot
  4. Klik Squash with previous
Adam Johns
sumber
1

Rebase interaktif berfungsi dengan baik hingga Anda memiliki cabang fitur besar dengan 20-30 komit dan / atau beberapa penggabungan dari master atau / dan memperbaiki konflik saat Anda melakukan komit di cabang Anda. Bahkan dengan menemukan komit saya melalui sejarah dan mengganti pickdengan squashtidak bekerja di sini. Jadi saya mencari cara lain dan menemukan artikel ini . Saya melakukan perubahan untuk mengerjakan ini di cabang terpisah:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Sebelum ini saya mendapatkan permintaan tarik saya dengan sekitar ~ 30 komit dengan 2-3 gabungan dari master + memperbaiki konflik. Dan setelah ini saya mendapatkan PR yang jelas dengan satu komit.

PS di sini adalah skrip bash untuk melakukan langkah-langkah ini di automode.

Oleksiy Guzenko
sumber
Solusi pertama dalam artikel itu benar-benar bagus, terima kasih atas tautannya
Hoody
-1

master checkout $ git

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A
Kapas
sumber
Saya pikir maksud Anda --oneline? Dan sepertinya Anda terjatuh Cdan B, bukan itu yang ingin OP.
bstpierre
Tidak bekerja untuk saya. Ia memindahkan KEPALA dan master saya ke A, tetapi tidak menggabungkan D menjadi A ( git show A) dan D, C dan B hilang dalam ref-log saya. Harus git rebase Dkembali.
Nate