Penyusunan ulang komit

105

Saat ini saya sedang mengerjakan cabang dan ingin beberapa komit untuk bergabung ke cabang lain:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Huruf menunjukkan komit, dan "x" adalah komit yang tidak relevan.)

Namun saya perhatikan bahwa adalah ide yang baik untuk mengumpulkan beberapa komitmen. Saya ingin "menggabungkan" komit a, d, e dan g menjadi satu tambalan dan mengkomitnya ke master. Komit b dan f harus pergi sebagai salah satu komit ke branchB. Apakah ada cara 'git'-ish yang baik untuk mencapainya?

Peter
sumber
4
Dua jawaban yang sudah diberikan menyebutkan perintah yang benar, tetapi saya pikir dengan tugas dasar seperti ini, ada baiknya memiliki instruksi nyata di sini di StackOverflow, jadi saya telah memposting beberapa. (Saya terkejut saya tidak dapat menemukan pertanyaan sebelumnya yang bagus untuk ditautkan.)
Cascabel

Jawaban:

126

Perintah yang Anda cari adalah git rebase, khususnya -i/--interactiveopsi.

Saya akan berasumsi bahwa Anda ingin meninggalkan komit c di cabang A, dan yang Anda maksud benar-benar ingin memindahkan komit lain ke cabang lain, daripada menggabungkan, karena penggabungan sangat mudah. Mari kita mulai dengan memanipulasi cabang A.

git rebase -i <SHA1 of commit a>^ branchA

The ^berarti komit sebelumnya, sehingga perintah ini mengatakan untuk rebase cabang A menggunakan komit sebelum "a" sebagai basis. Git akan memberi Anda daftar komit dalam rentang ini. Susun ulang dan beri tahu git untuk menekan yang sesuai:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Sekarang sejarahnya akan terlihat seperti ini:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Sekarang, mari ambil komit b + f yang baru saja dipencet untuk branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

Dan hal yang sama untuk a + d + e + g untuk master:

git checkout master
git cherry-pick branchA^

Terakhir, perbarui branchA agar mengarah ke c:

git branch -f branchA branchA^^

Sekarang kita harus memiliki:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Perhatikan bahwa jika Anda memiliki beberapa komit yang ingin Anda pindahkan antar cabang, Anda dapat menggunakan rebase lagi (non-interaktif):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Terakhir, sebuah penafian: Sangat mungkin untuk menyusun ulang commit sedemikian rupa sehingga beberapa tidak lagi berlaku dengan rapi. Ini bisa jadi karena Anda memilih urutan yang buruk (meletakkan tambalan sebelum komit memperkenalkan fitur yang ditambal); dalam hal ini Anda ingin membatalkan rebase ( git rebase --abort). Jika tidak, Anda harus dengan cerdas memperbaiki konflik (seperti yang Anda lakukan dengan konflik gabungan), menambahkan perbaikan, lalu jalankan git rebase --continueuntuk melanjutkan. Instruksi ini juga disediakan oleh pesan kesalahan yang tercetak saat konflik terjadi.

Bertingkat
sumber
bukankah diagram setelah git branch -f branchA branchA^^salah? Yaitu, bukankah seharusnya branchA menunjuk ke c pada titik ini, sehingga baris pertama diagram adalah c (branchA)dan bukan c - [a+d+e+g] - [b+f] (branchA)?
ntc2
@enoksrd: Ya, ada kesalahan, tetapi suntingan Anda memiliki kesalahan di dalamnya. Saya akan memperbaikinya ketika saya mendapat kesempatan di sini.
Cascabel
Daripada melakukan git branch -f branchA branchA^^mengapa Anda tidak bisa melakukan git reset --hard <sha1 of c>di cabang A?
Mikhail Vasilyev
17
git rebase -i HEAD~3

Di mana 3jumlah komit yang perlu disusun ulang ( sumber ).

Ini akan membuka vi , mendaftar komit dari terlama (atas) ke terbaru (bawah).
Sekarang susun ulang baris, simpan , dan keluar dari editor.

ddpakan memindahkan baris saat ini ke bawah
ddkP akan memindahkan baris saat ini ke atas ( sumber )

Nelu
sumber
9

git rebase --interactive adalah perintah yang Anda inginkan.

Contoh:

Status saat ini adalah ini:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Saya ingin menyusun ulang komit 9a24b81( Komitmen ketiga) dan 7bdfb68( Komitmen kedua). Untuk melakukan ini, pertama-tama saya menemukan komit sebelum komit pertama yang ingin kita ubah . Ini adalah komit 186d1e0(Komitmen pertama).

Perintah yang akan dijalankan adalah git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGE, dalam hal ini:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Ini membuka file di editor default dengan konten berikut:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# 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
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Perhatikan bahwa urutan komit dalam file berlawanan dengan yang digunakan untuk git log. Dalam git log, komit terbaru ada di bagian atas. Dalam file ini, komit terbaru ada di bagian bawah.

Seperti yang dijelaskan oleh komentar di bagian bawah file, ada berbagai hal yang dapat saya lakukan, seperti menekan, melepas, dan menyusun ulang komitmen. Untuk memesan ulang komit, saya mengedit file sehingga terlihat seperti ini (komentar bawah tidak ditampilkan):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

The pickperintah pada awal setiap baris berarti "penggunaan (yaitu mencakup) ini melakukan" dan ketika saya menyimpan file suhu dengan perintah rebase dan keluar dari editor, git akan mengeksekusi perintah dan memperbarui repositori dan direktori kerja:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Perhatikan riwayat komit yang ditulis ulang.

Tautan:

codeape
sumber
1
Harap tambahkan detail yang relevan dari tautan Anda, untuk memberikan jawaban yang lengkap dan mandiri. SO cemberut pada "jawaban link-only". Secara khusus, Bagaimana seseorang akan menggunakannya git rebase --interactiveuntuk mencapai tujuan OP?
SherylHohman
Tautan kedua tidak ada sekarang.
devsaw
Menambahkan konten untuk membuat jawaban lengkap dan mandiri. Tautan kedua dihapus.
codeape
-1

git rebase adalah yang Anda inginkan. Lihat opsi --interactive.


sumber
4
Harap tambahkan detail yang relevan dari tautan Anda, untuk memberikan jawaban yang lengkap dan mandiri. SO cemberut pada "jawaban link-only".
SherylHohman