Cara membagi komit terakhir menjadi dua di Git

277

Saya memiliki dua cabang kerja, master dan forum dan saya baru saja membuat beberapa modifikasi di cabang forum , yang ingin saya pilih menjadi master . Tapi sayangnya, komit yang ingin saya pilih juga mengandung beberapa modifikasi yang tidak saya inginkan.

Solusinya mungkin entah bagaimana menghapus komit yang salah dan menggantinya dengan dua komit terpisah, satu dengan perubahan yang ingin saya pilih sebagai master, dan yang lainnya tidak termasuk di dalamnya.

Saya sudah mencoba melakukannya

git reset --hard HEAD^

yang menghapus semua perubahan, jadi saya harus kembali dengan

git reset ORIG_HEAD

Jadi pertanyaan saya adalah, apa cara terbaik untuk membagi komitmen terakhir menjadi dua komitmen terpisah?

Jakub Arnold
sumber

Jawaban:

332

Anda harus menggunakan indeks. Setelah melakukan reset campuran (" git reset HEAD ^"), tambahkan set perubahan pertama ke dalam indeks, kemudian komit. Kemudian lakukan sisanya.

Anda dapat menggunakan " git add " untuk menempatkan semua perubahan yang dibuat dalam file ke indeks. Jika Anda tidak ingin membuat setiap modifikasi yang dibuat dalam file, hanya beberapa di antaranya, Anda dapat menggunakan "git add -p".

Mari kita lihat sebuah contoh. Misalkan saya memiliki file bernama myfile, yang berisi teks berikut:

something
something else
something again

Saya memodifikasinya di komit terakhir saya sehingga sekarang terlihat seperti ini:

1
something
something else
something again
2

Sekarang saya memutuskan bahwa saya ingin membaginya menjadi dua, dan saya ingin penyisipan baris pertama berada di komit pertama, dan penyisipan baris terakhir berada di komit kedua.

Pertama saya kembali ke induk HEAD, tetapi saya ingin menyimpan modifikasi dalam sistem file, jadi saya menggunakan "git reset" tanpa argumen (yang akan melakukan apa yang disebut reset "campuran"):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

Sekarang saya menggunakan "git add -p" untuk menambahkan perubahan yang ingin saya komit ke indeks (= saya tahap mereka). "git add -p" adalah alat interaktif yang menanyakan Anda tentang perubahan apa pada file yang harus ditambahkan ke indeks.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

Lalu saya melakukan perubahan pertama ini:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

Sekarang saya dapat melakukan semua perubahan lainnya (yaitu angka "2" yang dimasukkan di baris terakhir):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

Mari kita periksa log untuk melihat komitmen apa yang kita miliki:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
hcs42
sumber
1
Saya perlahan mulai terbiasa dengan git dari Mercurial selama satu setengah minggu terakhir, dan ada perintah pintas berguna git reset [--patch|-p] <commit>yang dapat Anda gunakan untuk menyelamatkan Anda dari masalah keharusan git add -psetelah mengatur ulang. Apakah saya benar? Menggunakan git 1.7.9.5.
trojjer
2
Berikut ini sedikit lebih banyak tentang teknik ini, termasuk rebasing jika itu adalah komit yang lebih lama, atau Anda perlu mengubah komit N menjadi komit: emmanuelbernard.com/blog/2014/04/14/… .
Chris Westin
84

Tujuan:

  • Saya ingin membagi commit masa lalu ( splitme) menjadi dua.
  • Saya ingin mempertahankan pesan komit .

Rencana:

  1. rebase interaktif dari yang sebelumnya splitme.
  2. sunting splitme.
  3. Setel ulang file untuk dibagi menjadi komit kedua.
  4. Ubah komit, pertahankan pesan, modifikasi seperlunya.
  5. Tambahkan kembali file yang dipisahkan dari komit pertama.
  6. Komit dengan pesan baru.
  7. Lanjutkan rebase.

Langkah rebase (1 & 7) dapat dilewati jika splitmekomit terbaru.

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

Jika saya ingin file split dilakukan terlebih dahulu, saya akan rebase -i lagi dan beralih urutan

git rebase -i splitme^
# swap order of splitme and 'just some files'
spazm
sumber
1
git reset HEAD^adalah potongan puzzle yang hilang. Bekerja dengan baik -pjuga. Terima kasih!
Marius Gedminas
10
Penting untuk mencatat -- $filesargumennya git reset. Dengan lintasan yang dilewati, git resetpulihkan file-file itu ke keadaan komit yang direferensikan tetapi tidak mengubah komit apa pun. Jika Anda meninggalkan jalur, maka Anda "kehilangan" komit yang ingin Anda ubah di langkah berikutnya.
spidol duelin
2
Metode ini mencegah Anda dari menyalin dan menempelkan pesan komit pertama Anda lagi, dibandingkan dengan jawaban yang diterima.
Calvin
Juga: jika Anda ingin mengatur ulang semua file, cukup gunakan git reset HEAD^ -- .. Sangat mengejutkan, ini bukan perilaku git reset HEAD^.
allidoiswin
52

Untuk mengubah komit saat ini menjadi dua komit, Anda dapat melakukan sesuatu seperti berikut ini.

Antara:

git reset --soft HEAD^

Ini membatalkan komit terakhir tetapi membiarkan semuanya dipentaskan. Anda kemudian dapat menghapus stage file-file tertentu:

git reset -- file.file

Opsional mengembalikan sebagian file-file itu:

git add -p file.file

Buat komit pertama yang baru:

git commit

Tahap dan komit sisa perubahan dalam komit kedua:

git commit -a

Atau:

Batalkan dan hapus panggung semua perubahan dari komit terakhir:

git reset HEAD^

Selektif putaran perubahan pertama:

git add -p

Melakukan:

git commit

Komit sisa perubahan:

git commit -a

(Pada langkah mana pun, jika Anda membatalkan ikatan komit yang menambahkan file baru dan ingin menambahkan ini ke komit kedua Anda harus menambahkannya secara manual karena commit -ahanya tahapan perubahan pada file yang sudah dilacak.)

CB Bailey
sumber
22

Jalankan git gui, pilih tombol radio "Amend last commit", dan unstage (Commit> Unstage From Commit, atau Ctrl- U) perubahan yang Anda tidak ingin komit pertama. Saya pikir itu cara termudah untuk melakukannya.

Hal lain yang bisa Anda lakukan adalah memilih cherry perubahan tanpa melakukan ( git cherry-pick -n) dan kemudian secara manual atau dengan git guimemilih perubahan yang diinginkan sebelum melakukan.

Michael Krelin - hacker
sumber
15
git reset HEAD^

--hard adalah yang membunuh perubahan Anda.

semanticart
sumber
13

Saya terkejut tidak ada yang menyarankan git cherry-pick -n forum. Ini akan membuat perubahan dari forumkomit terbaru tetapi tidak komit - Anda kemudian dapat resetmenghilangkan perubahan yang tidak Anda butuhkan dan komit yang ingin Anda simpan.

dahlbyk
sumber
3

Metode double-revert-squash

  1. Buat komit lain yang menghapus perubahan yang tidak diinginkan. (Jika ini per file, ini sangat mudah: git checkout HEAD~1 -- files with unwanted changesdan git commit. Jika tidak, file dengan perubahan campuran dapat sebagian dipentaskan git reset filedan git add -p filesebagai langkah perantara.) Sebut ini kembalikan .
  2. git revert HEAD- Buat komitmen lain, yang menambahkan kembali perubahan yang tidak diinginkan. Ini adalah pengembalian ganda
  3. Dari 2 komit yang sekarang Anda buat, tekan yang pertama ke komit untuk dibagi ( git rebase -i HEAD~3). Komit ini sekarang menjadi bebas dari perubahan yang tidak diinginkan, untuk komit yang kedua.

Manfaat

  • Mempertahankan pesan komit
  • Bekerja bahkan jika komit untuk membagi bukan yang terakhir. Ini hanya mensyaratkan bahwa perubahan yang tidak diinginkan tidak bertentangan dengan komitmen di kemudian hari
pengguna2394284
sumber
1

Karena Anda memetik ceri, Anda dapat:

  1. cherry-pickdengan --no-commitopsi yang ditambahkan.
  2. resetdan gunakan add --patch, add --editatau hanya adduntuk menampilkan apa yang ingin Anda simpan.
  3. commit perubahan bertahap.
    • Untuk menggunakan kembali pesan komit asli, Anda dapat menambahkan --reuse-message=<old-commit-ref>atau --reedit-message=<old-commit-ref>opsi ke commitperintah.
  4. Singkirkan perubahan yang belum dipentaskan dengan reset --hard.

Cara lain, mempertahankan atau mengedit pesan komit asli:

  1. cherry-pick komit asli seperti biasa.
  2. Balikkan perubahan yang tidak Anda inginkan dan gunakan adduntuk melakukan tahap pembalikan.
    • Langkah ini akan mudah jika Anda menghapus apa yang Anda tambahkan, tetapi sedikit rumit jika Anda menambahkan apa yang Anda hapus atau membalikkan perubahan.
  3. commit --amend untuk efek pembalikan pada komit yang dipetik cherry.
    • Anda akan mendapatkan pesan komit yang sama lagi, yang dapat Anda simpan atau revisi seperlunya.
ADTC
sumber
0

Ini mungkin solusi lain yang ditargetkan untuk kasus-kasus di mana ada komit besar dan sejumlah kecil file perlu dipindahkan ke komit baru. Ini akan berfungsi jika satu set <path>file harus diekstraksi dari komit terakhir di HEAD dan semua dipindahkan ke komit baru. Jika diperlukan banyak komitmen, solusi lain dapat digunakan.

Pertama buat tambalan ke area yang dipentaskan dan tidak dipentaskan yang akan berisi perubahan untuk mengembalikan kode sebelum modifikasi dan setelah modifikasi masing-masing:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

Untuk memahami apa yang akan terjadi (panah dan komentar bukan bagian dari perintah):

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

Kembalikan <path>perubahan pada komit terakhir:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

Buat komit baru dengan <path>perubahan:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

Ini memiliki efek membuat komit baru yang berisi perubahan yang diambil dari komit terakhir.

Jose Cifuentes
sumber