Bagaimana saya bisa menggabungkan dua komit menjadi satu jika saya sudah mulai rebase?

1159

Saya mencoba menggabungkan 2 komitmen menjadi 1, jadi saya mengikuti "squashing commit dengan rebase" dari git ready .

Saya berlari

git rebase --interactive HEAD~2

Di editor yang dihasilkan, saya mengubah pickke squashdan kemudian menyimpan-berhenti, tetapi rebase gagal dengan kesalahan

Tidak dapat 'squash' tanpa komit sebelumnya

Sekarang pohon kerja saya telah mencapai kondisi ini, saya kesulitan memulihkan.

Perintah git rebase --interactive HEAD~2gagal dengan:

Rebase interaktif sudah dimulai

dan git rebase --continuegagal dengan

Tidak dapat 'squash' tanpa komit sebelumnya

michael
sumber
22
Saya memukul ini juga. Kesalahan saya disebabkan oleh fakta bahwa git rebase -i mendaftar komit dalam urutan kebalikan dari git log; komit terbaru ada di bawah!
Pendidik

Jawaban:

1734

Ringkasan

Pesan kesalahan

Tidak dapat 'squash' tanpa komit sebelumnya

berarti Anda kemungkinan berusaha untuk "jatuh ke bawah." Git selalu menekan komit yang lebih baru ke komit yang lebih lama atau "ke atas" seperti yang dilihat pada daftar todo rebase interaktif, yaitu komit pada baris sebelumnya. Mengubah perintah pada baris pertama daftar todo Anda squashakan selalu menghasilkan kesalahan ini karena tidak ada komitmen pertama yang diperas.

Cara Mengatasinya

Pertama kembali ke tempat Anda mulai

$ git rebase --abort

Katakan sejarah Anda

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

Yaitu, a adalah komit pertama, kemudian b, dan akhirnya c. Setelah melakukan c, kami memutuskan untuk menekan b dan c bersama-sama:

(Catatan: Menjalankan git logpipa hasilnya ke pager, lesssecara default di sebagian besar platform. Untuk keluar dari pager dan kembali ke prompt perintah Anda, tekan qtombol.)

Berlari git rebase --interactive HEAD~2memberi Anda editor

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Perhatikan bahwa daftar todo ini berada dalam urutan terbalik dibandingkan dengan output dari git log .)

Mengubah b pickuntuk squashakan mengakibatkan kesalahan yang Anda lihat, tetapi jika bukan Anda labu c ke b (baru komit ke dalam yang lebih tua atau “meremas ke atas”) dengan mengubah daftar todo untuk

pick   b76d157 b
squash a931ac7 c

dan simpan-berhenti editor Anda, Anda akan mendapatkan editor lain yang isinya

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

b

# This is the 2nd commit message:

c

Saat Anda menyimpan dan keluar, konten file yang diedit menjadi pesan komit dari komit gabungan baru:

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Catatan Tentang Sejarah Penulisan Ulang

Rebase interaktif riwayat penulisan ulang. Mencoba untuk mendorong ke remote yang berisi riwayat lama akan gagal karena itu bukan fast-forward.

Jika cabang yang Anda rebound adalah cabang topik atau fitur di mana Anda bekerja sendiri , bukan masalah besar. Mendorong ke repositori lain akan membutuhkan--force opsi, atau Anda mungkin bisa, tergantung pada izin repositori jarak jauh, untuk pertama-tama menghapus cabang lama dan kemudian mendorong versi rebased. Contoh perintah yang berpotensi menghancurkan pekerjaan berada di luar cakupan jawaban ini.

Menulis ulang sejarah yang sudah diterbitkan pada cabang di mana Anda bekerja dengan orang lain tanpa alasan yang sangat baik seperti membocorkan kata sandi atau kekuatan perincian sensitif lainnya bekerja pada kolaborator Anda dan bersifat antisosial dan akan mengganggu pengembang lain. Bagian "Memulihkan Dari Rebase Hulu" dalam git rebasedokumentasi menjelaskan, dengan penekanan tambahan.

Memotong kembali (atau bentuk penulisan ulang lainnya) cabang yang menjadi dasar pekerjaan orang lain adalah ide yang buruk: siapa pun di hilirnya dipaksa untuk secara manual memperbaiki sejarah mereka. Bagian ini menjelaskan cara melakukan perbaikan dari sudut pandang hilir. Perbaikan sebenarnya, bagaimanapun, adalah untuk menghindari rebasing hulu di tempat pertama. ...

Greg Bacon
sumber
Jika saya menggunakan rebase untuk menekan komit, komit "gabungan" baru dibuat berisi dua perubahan tetapi hash berbeda. Apakah komit asli dilindungi oleh git juga?
fabsenet
@fabsenet Ya dan tidak. Komit asli masih dapat diakses tetapi kemungkinan tidak lagi dapat dijangkau dari referensi apa pun (tergantung pada riwayat Anda). Komitmen yang tidak direferensikan pada akhirnya dihapuskan melalui proses pengumpulan sampah.
Greg Bacon
saya hanya bermain-main ... saya lakukan git log hashoftheoldcommitdan itu berhasil, tetapi saya ingin melihat git log --graphdengan semua komitmen yang tidak dapat dijangkau ini disertakan
fabsenet
squash ini adalah alat yang baik untuk mengatur komit sebelum push, tetapi jika saya mendorong komit, saya tidak bisa menekannya? git mengatakan: Berhasil diubah dan diperbarui KEPALA terlepas.
Sérgio
Saya tidak mendapatkan editor pada contoh kedua, bash git, tampaknya dibekukan dalam beberapa proses. Apa yang harus dilakukan?
411

Jika ada banyak komit, Anda dapat menggunakannya git rebase -iuntuk memecah dua komit menjadi satu.

Jika hanya ada dua komit yang ingin Anda gabungkan, dan mereka adalah "dua terbaru", perintah berikut dapat digunakan untuk menggabungkan dua komit menjadi satu:

git reset --soft "HEAD^"
git commit --amend
pengguna3828059
sumber
6
Apa downside dibandingkan dengan rebase? Saya menemukan satu yang lebih mudah digunakan.
Guillaume86
17
Anda tidak dapat bergabung dengan perintah sewenang-wenang - hanya dua yang terakhir melakukan .
dr0i
50
@ dr0i Anda dapat menggabungkan komit sebanyak yang Anda inginkan, asalkan komit X terakhir, dan bukan di suatu tempat di tengah. Jalankan saja git reset --soft HEAD~10, di mana 10 adalah jumlah komit yang ingin Anda gabungkan.
fregante
2
Ini dapat digunakan jika Anda tidak memiliki set asal remote, dan Anda hanya memiliki dua komit.
atedja
8
Anda juga dapat mengatur ulang ke komit tertentu jika Anda tidak ingin menghitung berapa banyak dari HEADdengan menggunakan di git reset --soft 47b5c5...mana 47b5c5...SHA1 ID dari komit.
dguay
112

Rebase: Anda Tidak Akan Membutuhkannya:

Cara sederhana untuk skenario yang paling sering.

Umumnya:

Sebenarnya jika semua yang Anda inginkan hanya cukup menggabungkan beberapa komit baru menjadi satu tetapi tidak perlu drop, reworddan pekerjaan rebase lainnya.

Anda cukup melakukannya:

git reset --soft "HEAD~n"
  • Dengan asumsi ~nadalah jumlah komit untuk lembut un-komit (yaitu ~1, ~2, ...)

Kemudian, gunakan perintah berikut untuk mengubah pesan komit.

git commit --amend

yang hampir sama dengan jarak jauh squashdan satupick .

Dan itu berfungsi untuk n melakukan tetapi tidak hanya dua melakukan seperti yang diminta di atas.

pambda
sumber
3
Ini bagus jika Anda ingin melakukan pembersihan tambahan selain hanya menekan komit, seperti menghapus 1 komit di tengah atau mengubah baris kode.
styfle
1
Dengan asumsi ~nadalah jumlah komit untuk lembut un-komit (yaitu ~1, ~2, ...)
Ray
1
Bagaimana jika saya ingin menggabungkan bukan nkomitmen terakhir, tetapi nkomitmen di tengah? Bisakah saya melakukannya dengan mudah?
chumakoff
1
Maka git rebase -iapa yang perlu Anda lakukan squashbekerja. @chumakoff
pambda
3
Jadi untuk bergabung dengan nkomit terbaru menjadi satu, gunakan pertama kali git reset --soft @~m, di manam = n - 1
Łukasz Rajchel
55

Pertama, Anda harus memeriksa berapa banyak komitmen yang Anda miliki:

git log

Ada dua status:

Pertama, hanya ada dua komitmen:

Sebagai contoh:

commit A
commit B

(Dalam hal ini, Anda tidak dapat menggunakan git rebase untuk melakukannya) yang perlu Anda lakukan sebagai berikut.

$ git reset --soft HEAD^1

$ git commit --amend

Yang lain adalah bahwa ada lebih dari dua komitmen; Anda ingin menggabungkan komit C dan D.

Sebagai contoh:

commit A
commit B
commit C
commit D

(dalam kondisi ini, Anda dapat menggunakan git rebase)

git rebase -i B

Dan daripada menggunakan "squash" untuk melakukannya. Sisanya menipis sangat mudah. Jika Anda masih belum tahu, silakan baca http://zerodie.github.io/blog/2012/01/19/git-rebase-i/

Haimei
sumber
Reset --soft dan commit --amend adalah satu-satunya cara yang berfungsi jika Anda sudah memiliki rebase yang sedang berjalan (dan memilih 'edit' alih-alih 'squash' untuk komit ini). +1
Jacek Lach
1
Menggabungkan dua komitmen pertama dan satu-satunya dalam repositori, tepatnya pada kasus tepi saya :-)
Chris Huang-Leaver
1
Silakan tambahkan yang git push -f origin mastermungkin perlu.
Rishabh Agrahari
33

Dengan asumsi Anda berada di cabang topik Anda sendiri. Jika Anda ingin menggabungkan 2 komit terakhir menjadi satu dan terlihat seperti pahlawan, cabut komit tepat sebelum Anda membuat dua komit terakhir.

git checkout -b temp_branch HEAD^2

Kemudian squash melakukan cabang lain di cabang baru ini:

git merge branch_with_two_commits --squash

Itu akan membawa perubahan tetapi tidak melakukannya. Jadi, komit saja dan selesai.

git commit -m "my message"

Sekarang Anda dapat menggabungkan cabang topik baru ini kembali ke cabang utama Anda.

Homan
sumber
5
Ini sebenarnya jawaban yang paling membantu bagi saya, karena itu tidak memerlukan rebasing manual, tetapi alih-alih hancurkan semua komit dari seluruh cabang menjadi satu komit. Sangat bagus.
Robert
Terima kasih untuk ini! Ini adalah bagaimana membuat git melakukan bagaimana saya membayangkan squashing dilakukan di kepala saya!
Marjan Venema
jawaban yang luar biasa, jauh lebih sederhana daripada alternatifnya
Rupanya, jawaban ini tidak cocok untuk kasus kapan adan cmengharuskan untuk digabung bersama dan tetap bseperti itu.
Talha Ashraf
2
Apakah ada yang berubah di git versi terbaru? Ketika saya mencoba perintah pertama ( git checkout -b combine-last-two-commits "HEAD^2") di git versi 2.17, saya mendapatkan kesalahan:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
mhucka
23

Anda dapat membatalkan rebase dengan

git rebase --abort

dan ketika Anda menjalankan perintah rebase interaktif lagi squash; komit harus di bawah pick komit dalam daftar

Leom Burke
sumber
16

Saya sering menggunakan git reset --mixed untuk mengembalikan versi dasar sebelum banyak komit yang ingin Anda gabungkan, kemudian saya membuat komit baru, dengan cara itu Anda dapat membiarkan komit Anda terbaru, pastikan versi Anda HEAD setelah Anda mendorong ke server.

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Jika saya ingin menggabungkan dua komit menjadi satu, pertama saya gunakan:

git reset --mixed 249cf9392da197573a17c8426c282

"249cf9392da197573a17c8426c282" adalah versi ketiga, juga merupakan versi dasar Anda sebelum Anda bergabung, setelah itu, saya membuat komit baru:

git add .
git commit -m 'some commit message'

Itu semua, harapan adalah cara lain untuk semua orang.

FYI, dari git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.
VinceStyling
sumber
Saya belum membaca dokumen untuk '- dicampur' tapi saya yakin orang lain membaca posting dan bertanya-tanya hal yang sama: Apa keuntungan menggunakan - dicampur? Mungkin tingkatkan posting Anda dengan memasukkan potongan halaman manual.
funroll
@funroll Saya tidak tahu - dicampur jauh sebelum saya menulis jawaban ini, menurut pengalaman saya sendiri, operasi campuran akan berubah menentukan versi yang saya sampaikan sebagai argumen sebagai versi HEAD repositori, dan tidak ada yang bisa hilang setelah versi itu, jadi kami masih bisa menangani perubahan itu.
VinceStyling
14

$ git rebase --abort

Jalankan kode ini kapan saja jika Anda ingin membatalkan git rebase

$ git rebase -i HEAD~2

Untuk mengajukan kembali dua komitmen terakhir. Perintah di atas akan membuka editor kode

  • [ Komit terbaru akan berada di bagian bawah ]. Ubah komit terakhir ke squash. Karena squash akan berbaur dengan komit sebelumnya.
  • Kemudian tekan tombol esc dan ketik: wq untuk menyimpan dan menutup

Setelah: wq Anda akan berada dalam mode rebase aktif

Catatan : Anda akan mendapatkan editor lain jika tidak ada pesan peringatan / kesalahan, Jika ada kesalahan atau peringatan editor lain tidak akan ditampilkan, Anda dapat membatalkan dengan menjalankann $ git rebase --abort jika Anda melihat kesalahan atau peringatan lain hanya melanjutkan dengan menjalankan$ git rebase --continue

Anda akan melihat 2 pesan komit Anda. Pilih satu atau tulis pesan komit Anda sendiri, simpan dan keluar [: wq]

Catatan 2: Anda mungkin perlu memaksakan perubahan Anda ke repo jarak jauh jika Anda menjalankan perintah rebase

$ git push -f

$ git push -f origin master

Gnanasekar S
sumber
1
Catatan 2: git push -f origin/masteradalah jawaban lain yang tidak ada. +1
Rishabh Agrahari
2

Karena saya gunakan git cherry-pick untuk hampir semua hal, bagi saya itu wajar untuk melakukannya bahkan di sini.

Mengingat bahwa saya telah branchXmemeriksa dan ada dua komitmen di ujungnya, yang ingin saya buat satu komit yang menggabungkan konten mereka, saya melakukan ini:

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Jika saya ingin memperbarui branchXjuga (dan saya kira ini adalah sisi bawah dari metode ini) saya juga harus:

git checkout branchX
git reset --hard <the_new_commit>
Martin G
sumber
1

Jika cabang master Anda git logterlihat seperti berikut:

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

dan Anda ingin menggabungkan dua komitmen teratas lakukan saja langkah-langkah mudah berikut:

  1. Pertama berada di checkout sisi aman komit terakhir kedua di cabang terpisah. Anda dapat memberi nama cabang apa saja.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Sekarang, pilih saja perubahan Anda dari komit terakhir ke cabang baru ini sebagai: git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e . (Selesaikan konflik jika timbul)
  3. Jadi sekarang, perubahan komit terakhir Anda ada di komit terakhir kedua Anda. Tapi Anda masih harus berkomitmen, jadi pertama-tama tambahkan perubahan yang baru saja Anda pilih dan jalankan git commit --amend.

Itu dia. Anda dapat mendorong versi gabungan ini di cabang "gabungan komitmen" jika Anda mau.

Juga, Anda dapat membuang dua komit back-to-back di cabang master Anda sekarang. Cukup perbarui cabang master Anda sebagai:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull
Usman
sumber
0

Jika Anda ingin menggabungkan dua komit terbaru dan hanya menggunakan pesan komit yang lebih lama, Anda dapat mengotomatiskan proses menggunakan expect .

Saya berasumsi:

  • Anda menggunakan vi sebagai editor Anda
  • Komitmen Anda masing-masing adalah satu baris

Saya diuji dengan git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact
erwaman
sumber
Tidak jelas apa yang dilakukan skrip Anda.
buhtz
@ Buhtz saya menambahkan beberapa komentar. Beri tahu saya jika Anda masih merasa bingung, dan jika demikian, bagian mana.
erwaman
Masih belum jelas apa yang dilakukan scrpit Anda. Juga expecttidak dijelaskan.
buhtz
@ Buhtz bagian mana yang tidak jelas? Saya memberikan tautan ke halaman dengan lebih banyak dokumentasi untuk expect.
erwaman
Deskripsi umum skrip tidak ada. Tidak jelas apa fungsinya. Tidak ada bagian yang tidak jelas. Maksud naskah itu sendiri tidak jelas.
buhtz