Squash X terakhir saya komit bersama menggunakan Git

3590

Bagaimana saya bisa menekan komit X terakhir saya bersama menjadi satu komit menggunakan Git?

markdorison
sumber
12
Pertanyaan serupa: stackoverflow.com/questions/7275508/…
koppor
2
@ Matt TortoiseGit adalah alat Anda. Ini menyediakan fungsi tunggal "Combine to one commit" yang akan memanggil semua langkah secara otomatis di latar belakang. Sayangnya hanya tersedia untuk Windows. Lihat jawaban saya di bawah ini.
Matthias M
1
Untuk menekan hingga komitmen pertama, lihat ini - stackoverflow.com/questions/1657017/…
goelakash
1
posting squash, seseorang perlu melakukan push push stackoverflow.com/questions/10298291/…
vikramvi

Jawaban:

2091

Gunakan git rebase -i <after-this-commit>dan ganti "pilih" pada komit kedua dan selanjutnya dengan "squash" atau "fixup", seperti yang dijelaskan dalam manual .

Dalam contoh ini, <after-this-commit>apakah hash SHA1 atau lokasi relatif dari KEPALA cabang saat ini dari mana komit dianalisis untuk perintah rebase. Misalnya, jika pengguna ingin melihat 5 commit dari HEAD saat ini di masa lalu perintahnya adalah git rebase -i HEAD~5.

Anomie
sumber
260
Ini, saya pikir, menjawab pertanyaan ini sedikit lebih baik stackoverflow.com/a/5201642/295797
Roy Truelove
92
Apa yang dimaksud dengan <after-this-commit>?
2540625
43
<after-this-commit>komit X + 1 yaitu induk dari komit tertua yang ingin Anda squash.
joozek
339
Saya menemukan jawaban ini terlalu singkat untuk dimengerti dengan jelas. Contoh akan membantu.
Ian Ollmann
54
Perbedaan antara rebase -ipendekatan ini dan reset --softadalah, rebase -imemungkinkan saya untuk mempertahankan pembuat komit, sementara reset --softmemungkinkan saya untuk berkomitmen kembali. Kadang-kadang saya perlu menekan komitmen untuk melakukan tarik namun mempertahankan informasi penulis. Terkadang saya perlu mengatur ulang lunak pada komit saya sendiri. Terpilih untuk kedua jawaban yang bagus.
zionyx
3833

Anda dapat melakukan ini dengan cukup mudah tanpa git rebaseatau git merge --squash. Dalam contoh ini, kita akan menekan 3 komit terakhir.

Jika Anda ingin menulis pesan komit baru dari awal, ini sudah cukup:

git reset --soft HEAD~3 &&
git commit

Jika Anda ingin mulai mengedit pesan komit baru dengan gabungan dari pesan komit yang ada (yaitu mirip dengan apa yang git rebase -iakan dimulai dengan daftar instruksi pick / squash / squash / ... / squash ), maka Anda perlu mengekstrak pesan-pesan itu dan meneruskannya mereka untuk git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Kedua metode tersebut menekan tiga komit terakhir menjadi satu komit baru dengan cara yang sama. Soft reset hanya menunjukkan kembali HEAD ke komit terakhir yang tidak ingin Anda tekan. Baik indeks maupun pohon kerja tidak tersentuh oleh soft reset, meninggalkan indeks dalam keadaan yang diinginkan untuk komit baru Anda (yaitu sudah memiliki semua perubahan dari komit yang akan Anda “buang”).

Chris Johnsen
sumber
163
Ha! Saya suka metode ini. Ini adalah yang dekat dengan semangat masalah. Sangat disayangkan bahwa itu membutuhkan begitu banyak voodoo. Sesuatu seperti ini harus ditambahkan ke salah satu perintah dasar. Mungkin git rebase --squash-recent, atau bahkan git commit --amend-many.
Adrian Ratnapala
13
@ ABB: Jika cabang Anda memiliki set "hulu", maka Anda mungkin dapat menggunakan branch@{upstream}(atau hanya @{upstream}untuk cabang saat ini; dalam kedua kasus, bagian terakhir dapat disingkat menjadi @{u}; lihat pembagian git ). Ini mungkin berbeda dari Anda “lalu mendorong komit” (misalnya jika seseorang mendorong sesuatu yang dibangun di atas dorongan terbaru Anda dan kemudian Anda diambil itu), tetapi tampaknya seperti itu mungkin dekat dengan apa yang Anda inginkan.
Chris Johnsen
104
Agak semacam ini mengharuskan saya untuk push -ftetapi sebaliknya itu indah, terima kasih.
2rs2ts
39
@ 2rs2ts git push -f terdengar berbahaya. Berhati-hatilah untuk hanya menekan komitmen lokal. Jangan sentuh komitmen yang didorong!
Matthias M
20
Saya juga perlu menggunakan git push --forcesetelah itu sehingga dibutuhkan komit
Zach Saucier
740

Anda dapat menggunakan git merge --squashini, yang sedikit lebih elegan daripada git rebase -i. Misalkan Anda berada di master dan Anda ingin memeras 12 komit terakhir menjadi satu.

PERINGATAN: Pertama-tama pastikan Anda melakukan pekerjaan Anda — periksa apakah git statussudah bersih (karena git reset --hardakan membuang perubahan yang dipentaskan dan tidak dipentaskan)

Kemudian:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

The dokumentasi untukgit merge menggambarkan --squashpilihan secara lebih rinci.


Pembaruan: satu-satunya keuntungan nyata dari metode ini daripada yang lebih sederhana yang git reset --soft HEAD~12 && git commitdisarankan oleh Chris Johnsen dalam jawabannya adalah bahwa Anda mendapatkan pesan komit yang dipopulasi dengan setiap pesan komit yang Anda hancurkan.

Mark Longair
sumber
16
Anda mengatakan ini lebih 'elegan' daripada git rebase -i, tetapi Anda tidak memberikan alasan mengapa. Untuk sementara, saya menganggap ini karena menurut saya sebenarnya yang terjadi adalah sebaliknya dan ini adalah peretasan; tidakkah Anda melakukan lebih banyak perintah daripada yang diperlukan hanya untuk memaksa git mergemelakukan salah satu hal yang git rebasesecara khusus dirancang untuk?
Mark Amery
77
@ Markus Amery: Ada berbagai alasan yang saya katakan bahwa ini lebih elegan. Sebagai contoh, itu tidak melibatkan pemijahan yang tidak perlu editor dan kemudian mencari dan mengganti string dalam file "yang harus dilakukan". Menggunakan git merge --squashjuga lebih mudah digunakan dalam skrip. Pada dasarnya, alasannya adalah bahwa Anda tidak memerlukan "interaktivitas" git rebase -isama sekali untuk ini.
Mark Longair
12
Keuntungan lain git merge --squashadalah lebih kecil kemungkinannya untuk menghasilkan konflik penggabungan dalam menghadapi gerakan / penghapusan / penggantian nama dibandingkan dengan rebasing, terutama jika Anda bergabung dari cabang lokal. (Penafian: berdasarkan hanya pada satu pengalaman, koreksi saya jika ini tidak benar dalam kasus umum!)
Cheezmeister
2
Saya selalu sangat enggan untuk mengatur ulang keras - Saya akan menggunakan tag temporal alih-alih HEAD@{1}hanya berada di sisi yang aman misalnya ketika alur kerja Anda terganggu selama satu jam oleh pemadaman listrik dll.
Tobias Kienzler
8
@BT: Hancurkan komit Anda? :) isi mereka kembali, meskipun itu akan lebih banyak pekerjaan . Jika pekerjaan Anda bahkan tidak dipentaskan, bagaimanapun, saya khawatir ada sedikit yang bisa dilakukan; itu sebabnya jawabannya mengatakan di muka: "Pertama periksa bahwa status git bersih (sejak git reset --hard akan membuang perubahan yang dipentaskan dan tidak dipentaskan) ".
Mark Longair
218

Saya sarankan menghindari git resetjika memungkinkan - terutama untuk pemula Git. Kecuali jika Anda benar-benar perlu mengotomatiskan proses berdasarkan sejumlah komitmen, ada cara yang kurang eksotis ...

  1. Letakkan commit yang akan tergencet di cabang yang berfungsi (jika belum) - gunakan gitk untuk ini
  2. Periksa cabang target (mis. 'Master')
  3. git merge --squash (working branch name)
  4. git commit

Pesan komit akan dipopulasi berdasarkan squash.

bangsawan
sumber
4
Ini adalah metode paling aman: tidak ada reset lunak / keras (!!), atau reflog digunakan!
TeChn4K
14
Akan lebih bagus jika Anda memperluas (1).
Adam
2
@ Adam: Pada dasarnya, ini berarti menggunakan antarmuka GUI gitkuntuk memberi label pada baris kode yang Anda tekan dan juga memberi label pada basis yang akan digunakan squash. Dalam kasus normal, kedua label ini sudah ada, sehingga langkah (1) dapat dilewati.
Nobar
3
Perhatikan bahwa metode ini tidak menandai cabang kerja yang sepenuhnya digabungkan, jadi menghapusnya membutuhkan penghapusan paksa. :(
Kyrstellaine
2
Untuk (1), saya telah menemukan git branch your-feature && git reset --hard HEAD~Ncara yang paling nyaman. Namun, itu melibatkan git reset lagi, yang coba dihindari oleh jawaban ini.
eis
134

Berdasarkan jawaban Chris Johnsen ,

Tambahkan alias "squash" global dari bash: (atau Git Bash di Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... atau menggunakan Prompt Perintah Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Sekarang Anda ~/.gitconfigharus mengandung alias ini:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Pemakaian:

git squash N

... Yang secara otomatis terjepit bersama dengan Nkomitmen terakhir , inklusif.

Catatan: Pesan komit yang dihasilkan adalah kombinasi dari semua komit terjepit, secara berurutan. Jika Anda tidak puas dengan itu, Anda selalu git commit --amenddapat memodifikasinya secara manual. (Atau, edit alias agar sesuai dengan selera Anda.)

EthanB
sumber
6
Menarik, tetapi saya lebih suka mengetik sendiri pesan commit yang tergencet, sebagai ringkasan deskriptif dari banyak komitmen saya, daripada memasukkannya secara otomatis untuk saya. Jadi saya lebih suka menentukan git squash -m "New summary."dan telah Nmenentukan secara otomatis jumlah komit yang tidak dicopot.
Acumenus
1
@ ABB, Ini terdengar seperti pertanyaan terpisah. (Saya tidak berpikir persis apa yang diminta OP; Saya tidak pernah merasa perlu untuk itu dalam alur kerja git squash saya.)
EthanB
3
Ini sangat manis. Secara pribadi saya ingin versi yang menggunakan pesan komit dari komit pertama yang tergencet. Akan bagus untuk hal-hal seperti tweak spasi putih.
funroll
@funroll Setuju. Menjatuhkan pesan komit terakhir adalah kebutuhan yang sangat umum bagi saya. Kita harus bisa memikirkan itu ...
Steve Clay
2
@ABB dapat Anda gunakan git commit --amenduntuk lebih lanjut mengubah pesan, tetapi alias ini memungkinkan Anda memiliki awal yang baik tentang apa yang seharusnya ada dalam pesan komit.
dashesy
131

Berkat posting blog praktis ini saya menemukan bahwa Anda dapat menggunakan perintah ini untuk menekan 3 komit terakhir:

git rebase -i HEAD~3

Ini berguna karena berfungsi bahkan ketika Anda berada di cabang lokal tanpa informasi pelacakan / repo jarak jauh.

Perintah akan membuka editor rebase interaktif yang kemudian memungkinkan Anda untuk menyusun ulang, squash, reword, dll seperti biasa.


Menggunakan editor rebase interaktif:

Editor rebase interaktif menunjukkan tiga komit terakhir. Batasan ini ditentukan oleh HEAD~3saat menjalankan perintah git rebase -i HEAD~3.

Komit terbaru,, HEADditampilkan pertama kali pada baris 1. Baris yang dimulai dengan #komentar / dokumentasi.

Dokumentasi yang ditampilkan cukup jelas. Pada baris mana pun Anda dapat mengubah perintah dari pickmenjadi perintah pilihan Anda.

Saya lebih suka menggunakan perintah fixupkarena ini "menekan" komit berubah menjadi komit pada baris di atas dan membuang pesan komit.

Karena komit pada baris 1 adalah HEAD, dalam kebanyakan kasus Anda akan membiarkan ini sebagai pick. Anda tidak dapat menggunakan squashatau fixupkarena tidak ada komit lain untuk memeras komit ke.

Anda juga dapat mengubah urutan komitmen. Ini memungkinkan Anda untuk menekan atau memperbaiki komit yang tidak berdekatan secara kronologis.

editor rebase interaktif


Contoh praktis sehari-hari

Saya baru-baru ini melakukan fitur baru. Sejak itu, saya telah melakukan dua perbaikan bug. Tapi sekarang saya telah menemukan bug (atau mungkin hanya kesalahan ejaan) di fitur baru yang saya komit. Menyebalkan sekali! Saya tidak ingin komit baru mencemari riwayat komit saya!

Hal pertama yang saya lakukan adalah memperbaiki kesalahan dan membuat komit baru dengan komentar squash this into my new feature!.

Saya kemudian menjalankan git logatau gitkdan mendapatkan SHA komit dari fitur baru (dalam hal ini 1ff9460).

Selanjutnya, saya membawa editor rebase interaktif dengan git rebase -i 1ff9460~. The ~setelah komit SHA memberitahu editor untuk memasukkan yang komit dalam editor.

Selanjutnya, saya memindahkan komit yang berisi perbaikan ( fe7f1e0) ke bawah komit fitur, dan berubah pickmenjadi fixup.

Saat menutup editor, perbaikannya akan dimasukkan ke dalam fitur commit dan riwayat komit saya akan terlihat bagus dan bersih!

Ini bekerja dengan baik ketika semua komit adalah lokal, tetapi jika Anda mencoba untuk mengubah komit yang sudah didorong ke remote Anda benar-benar dapat menyebabkan masalah bagi pengembang lain yang telah memeriksa cabang yang sama!

masukkan deskripsi gambar di sini

br3nt
sumber
5
Anda harus memilih yang teratas dan menekan sisanya? Anda harus mengedit jawaban Anda untuk menjelaskan cara menggunakan editor rebase interaktif secara lebih detail
Kolob Canyon
2
Ya, tinggalkan pickbaris 1. Jika Anda memilih squashatau fixupuntuk komit pada baris 1, git akan menampilkan pesan yang mengatakan "kesalahan: tidak dapat 'memperbaiki' tanpa komit sebelumnya". Maka itu akan memberi Anda opsi untuk memperbaikinya: "Anda dapat memperbaikinya dengan 'git rebase --edit-todo' dan kemudian jalankan 'git rebase --continue'." atau Anda dapat membatalkan dan memulai dari awal: "Atau Anda dapat membatalkan rebase dengan 'git rebase --abort'."
br3nt
56

Jika Anda menggunakan TortoiseGit, Anda dapat fungsinya Combine to one commit:

  1. Buka menu konteks TortoiseGit
  2. Pilih Show Log
  3. Tandai komit yang relevan di tampilan log
  4. Pilih Combine to one commitdari menu konteks

Gabungkan komit

Fungsi ini secara otomatis menjalankan semua langkah git tunggal yang diperlukan. Sayangnya hanya tersedia untuk Windows.

Matthias M
sumber
Sejauh yang saya ketahui, ini tidak akan berfungsi untuk menggabungkan komitmen.
Thorkil Holm-Jacobsen
1
Meskipun tidak dikomentari oleh orang lain, ini bahkan berfungsi untuk melakukan yang tidak di KEPALA. Misalnya, kebutuhan saya adalah untuk menghancurkan beberapa komitmen WIP yang saya lakukan dengan deskripsi yang lebih waras sebelum mendorong. Bekerja dengan indah. Tentu saja, saya masih berharap saya bisa belajar melakukannya dengan perintah.
Charles Roberto Canato
55

Untuk melakukan ini, Anda dapat menggunakan perintah git berikut.

 git rebase -i HEAD~n

n (= 4 di sini) adalah jumlah komit terakhir. Maka Anda mendapat opsi berikut,

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

Perbarui seperti di bawah picksatu komit dan squashyang lainnya ke yang terbaru,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

Untuk detail klik pada Tautan

Jakir Hosen Khan
sumber
48

Berdasarkan artikel ini saya menemukan metode ini lebih mudah untuk usecase saya.

Cabang 'dev' saya berada di depan 'origin / dev' sebanyak 96 komit (jadi komit ini belum didorong ke remote).

Saya ingin memeras komitmen ini menjadi satu sebelum mendorong perubahan. Saya lebih suka mereset cabang ke status 'asal / dev' (ini akan membuat semua perubahan dari 96 komit tidak dipentaskan) dan kemudian melakukan perubahan sekaligus:

git reset origin/dev
git add --all
git commit -m 'my commit message'
trudolf
sumber
1
Apa yang saya butuhkan. Squash down melakukan dari cabang fitur saya, dan kemudian saya git ceri memilih komit ke master saya.
David Victor
1
Ini tidak merusak komitmen sebelumnya!
IgorGanapolsky
bisa Anda jelaskan sedikit lebih jauh @igorGanapolsky?
trudolf
3
@trudolf Ini tidak benar-benar menekan (memilih individu yang berkomitmen untuk squash). Ini lebih dari melakukan semua perubahan Anda sekaligus.
IgorGanapolsky
15
ya, maka itu menghancurkan semua komitmen Anda menjadi satu. Selamat!
trudolf
45

Di cabang yang ingin Anda gabungkan komit, jalankan:

git rebase -i HEAD~(n number of commits back to review)

contoh:

git rebase -i HEAD~1

Ini akan membuka editor teks dan Anda harus mengganti 'pick' di depan setiap komit dengan 'squash' jika Anda ingin komit ini digabungkan. Dari dokumentasi:

p, pilih = gunakan komit

s, squash = gunakan komit, tetapi berbaur menjadi komit sebelumnya

Misalnya, jika Anda ingin menggabungkan semua komit menjadi satu, 'pilih' adalah komit pertama yang Anda buat dan semua yang akan datang (ditempatkan di bawah yang pertama) harus disetel ke 'squash'. Jika menggunakan vim, gunakan : x dalam mode sisipkan untuk menyimpan dan keluar dari editor.

Kemudian untuk melanjutkan rebase:

git rebase --continue

Untuk lebih lanjut tentang ini dan cara-cara lain untuk menulis ulang riwayat komit Anda lihat posting bermanfaat ini

aabiro
sumber
2
Tolong jelaskan juga apa yang dilakukan --continuedan vim :x.
not2qubit
Rebase akan terjadi di blok saat melewati komit di cabang Anda, setelah Anda git addkonfigurasi yang benar dalam file Anda yang Anda gunakan git rebase --continueuntuk pindah ke komit berikutnya dan mulai bergabung. :xadalah salah satu perintah yang akan menyimpan perubahan file saat menggunakan vim, lihat ini
aabiro
32

Jawaban Anomies bagus, tapi saya merasa tidak aman tentang hal ini jadi saya memutuskan untuk menambahkan beberapa tangkapan layar.

Langkah 0: git log

Lihat di mana Anda berada git log. Yang paling penting, cari hash dari commit pertama yang tidak ingin Anda hancurkan. Jadi hanya:

masukkan deskripsi gambar di sini

Langkah 1: git rebase

Jalankan git rebase -i [your hash], dalam kasus saya:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Langkah 2: pilih / squash apa yang Anda inginkan

Dalam kasus saya, saya ingin menekan semua komit yang pertama kali. Pemesanan adalah dari pertama hingga terakhir, demikian juga sebaliknya git log. Dalam kasus saya, saya ingin:

masukkan deskripsi gambar di sini

Langkah 3: Sesuaikan pesan

Jika Anda hanya memilih satu komit dan menghancurkan sisanya, Anda dapat menyesuaikan satu pesan komit:

masukkan deskripsi gambar di sini

Itu dia. Setelah Anda menyimpan ini ( :wq), Anda selesai. Lihatlah dengan git log.

Martin Thoma
sumber
2
akan menyenangkan untuk melihat hasil akhir, misalnya,git log
Timothy LJ Stewart
2
Tidak, terima kasih. Inilah tepatnya cara saya mengurangi komitmen saya.
Axalix
@Axalix Apakah Anda menghapus semua baris Anda? Itulah bagaimana Anda kehilangan komitmen Anda.
a3y3
31

Prosedur 1

1) Identifikasi hash pendek komit

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Di sini pun git log --onelinebisa digunakan untuk mendapatkan hash pendek.

2) Jika Anda ingin melakukan squash (menggabungkan), lakukan dua komit terakhir

# git rebase -i deab3412 

3) Ini membuka nanoeditor untuk digabung. Dan sepertinya di bawah ini

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Ubah nama kata pickuntuk squashyang hadir sebelum abcd1234. Setelah ganti nama itu harus seperti di bawah ini.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Sekarang simpan dan tutup nanoeditor. Tekan ctrl + odan tekan Enteruntuk menyimpan. Dan kemudian tekan ctrl + xuntuk keluar dari editor.

6) Kemudian nanoeditor kembali terbuka untuk memperbarui komentar, jika perlu memperbaruinya.

7) Sekarang tergencet dengan sukses, Anda dapat memverifikasinya dengan memeriksa log.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) Sekarang dorong untuk repo. Catatan untuk menambahkan +tanda sebelum nama cabang. Ini berarti dorongan paksa.

# git push origin +master

Catatan: Ini didasarkan pada penggunaan git on ubuntushell. Jika Anda menggunakan os yang berbeda ( Windowsatau Mac) maka perintah di atas sama kecuali editor. Anda mungkin mendapatkan editor yang berbeda.

Prosedur 2

  1. Pertama tambahkan file yang diperlukan untuk komit
git add <files>
  1. Kemudian komit menggunakan --fixupopsi dan OLDCOMMITharus di mana kita perlu menggabungkan (squash) komit ini.
git commit --fixup=OLDCOMMIT

Sekarang ini menciptakan komit baru di atas KEPALA dengan fixup1 <OLDCOMMIT_MSG>.

  1. Kemudian jalankan perintah di bawah ini untuk menggabungkan (squash) komit baru ke OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

Di sini ^berarti komitmen sebelumnya untuk OLDCOMMIT. rebasePerintah ini membuka jendela interaktif pada editor (vim atau nano) bahwa kita tidak perlu melakukan apa-apa cukup simpan dan keluar sudah cukup. Karena opsi yang diteruskan ke ini akan secara otomatis memindahkan komit terbaru ke komit lama dan mengubah operasi ke fixup(setara dengan squash). Kemudian rebase berlanjut dan selesai.

Prosedur 3

  1. Jika perlu menambahkan perubahan baru ke komit terakhir berarti --amenddapat digunakan dengan git-commit.
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

Di sini, --amendgabungkan perubahan baru ke komit terakhir cdababcddan hasilkan ID komit baru1d4ab2e1

Kesimpulan

  • Keuntungan dari prosedur pertama adalah menekan banyak komit dan menyusun ulang. Tetapi prosedur ini akan sulit jika kita perlu menggabungkan perbaikan ke komit yang sangat lama.
  • Jadi prosedur 2 membantu menggabungkan komit ke komit yang sangat lama dengan mudah.
  • Dan prosedur ke-3 berguna dalam kasus untuk memeras perubahan baru untuk komit terakhir.
rashok
sumber
Itu hanya memperbarui dua komit terakhir bahkan saya mengatur ulang ke komit ke komit terakhir ke-6, tidak tahu mengapa
Carlos Liu
Bahkan Anda dapat mengatur ulang pesanan komit. Ini bekerja dengan baik.
rashok
29

Untuk memeras 10 komit terakhir menjadi 1 komit tunggal:

git reset --soft HEAD~10 && git commit -m "squashed commit"

Jika Anda juga ingin memperbarui cabang jarak jauh dengan komit terjepit:

git push -f
Ayan
sumber
--force berbahaya ketika banyak orang bekerja di cabang bersama karena secara membabi buta memperbarui remote dengan salinan lokal Anda. --Paksa-dengan-sewa bisa lebih baik karena memastikan bahwa jarak jauh tidak memiliki komitmen dari orang lain sejak Anda mengambilnya terakhir kali.
bharath
27

Jika Anda berada di cabang jarak jauh (disebut feature-branch) yang diklon dari Golden Repository ( golden_repo_name), maka inilah teknik untuk memecah komit Anda menjadi satu:

  1. Lihat repo emas

    git checkout golden_repo_name
    
  2. Buat cabang baru darinya (golden repo) sebagai berikut

    git checkout -b dev-branch
    
  3. Squash bergabung dengan cabang lokal Anda yang sudah Anda miliki

    git merge --squash feature-branch
    
  4. Komit perubahan Anda (ini akan menjadi satu-satunya komit yang masuk di dev-branch)

    git commit -m "My feature complete"
    
  5. Dorong cabang ke repositori lokal Anda

    git push origin dev-branch
    
Sandesh Kumar
sumber
Karena saya baru saja menginjak ~ 100 commit (untuk menyinkronkan cabang svn melalui git-svn), ini jauh lebih cepat daripada rebasing secara interaktif!
bijak
1
Membaca, saya melihat komentar @ Chris, yang biasa saya lakukan (rebase --soft ...) - sayang sekali stackoverflow tidak lagi menempatkan jawaban dengan ratusan upvote di atas ...
sage
1
setuju dengan Anda @sage, mari berharap mereka mungkin melakukannya di masa depan
Sandesh Kumar
Ini jalan yang benar. Pendekatan rebase baik, tetapi hanya digunakan untuk squash sebagai solusi terakhir.
Axalix
20

Apa yang bisa benar-benar nyaman:
Temukan hash komit yang ingin Anda tekan, katakan d43e15.

Sekarang gunakan

git reset d43e15
git commit -am 'new commit name'
Ariel Gabizon
sumber
2
Ini. Mengapa tidak lebih banyak orang menggunakan ini? Ini jauh lebih cepat daripada rebasing dan menghancurkan komitmen individu.
a3y3
17

Ini adalah super-duper Kludgy, tetapi dalam semacam cara yang keren, jadi saya hanya akan melemparkannya ke atas ring:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

Terjemahan: berikan "editor" baru untuk git yang, jika nama file yang akan diedit adalah git-rebase-todo(prompt rebase interaktif) mengubah semua kecuali "pilih" menjadi "squash", dan jika tidak, akan menghasilkan vim - sehingga saat Anda diminta untuk mengedit pesan komit terjepit, Anda mendapatkan vim. (Dan jelas saya menekan lima commit terakhir di branch foo, tetapi Anda bisa mengubahnya sesuka Anda.)

Saya mungkin akan melakukan apa yang disarankan Mark Longair .

Cascabel
sumber
7
+1: itu menyenangkan dan instruktif, karena sama sekali tidak jelas bagi saya bahwa Anda dapat memasukkan sesuatu yang lebih kompleks daripada nama program dalam variabel lingkungan GIT_EDITOR.
Mark Longair
16

Jika Anda ingin memadatkan setiap komit ke dalam komit tunggal (mis. Saat merilis proyek untuk pertama kalinya), coba:

git checkout --orphan <new-branch>
git commit
William Denniss
sumber
15

2020 Solusi sederhana tanpa rebase:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2 berarti dua komit terakhir akan tergencet. Anda dapat menggantinya dengan nomor apa pun

Xys
sumber
14

Saya pikir cara termudah untuk melakukan ini adalah dengan membuat cabang baru dari master dan melakukan gabungan --squash dari cabang fitur.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Maka Anda memiliki semua perubahan yang siap untuk dilakukan.

pengguna1376350
sumber
12

Satu kalimat sederhana yang selalu berfungsi, mengingat Anda saat ini berada di cabang yang ingin Anda squash, master adalah cabang tempat asalnya, dan komit terbaru berisi pesan komit dan penulis yang ingin Anda gunakan:

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
Kolin
sumber
4
Saya benar-benar marah dengan frustasi tentang squashing commit dan betapa bodohnya komit - hanya menggunakan pesan terakhir dan remas semuanya untuk satu komit! Kenapa begitu sulit ???? Liner yang satu ini melakukan itu untuk saya. Terima kasih dari lubuk hati saya yang marah.
Locane
12

jika misalnya Anda ingin menekan 3 komit terakhir ke komit tunggal di cabang (repositori jarak jauh) di misalnya: https://bitbucket.org

Apa yang saya lakukan adalah

  1. reset git --Kepala Kepala ~ 3 &&
  2. git melakukan
  3. git push origin (branch_name) --force
Albert Ruelan
sumber
3
Berhati-hatilah, Karena jika Anda menggunakan kekuatan maka tidak ada cara untuk mengambil komit sebelumnya sejak Anda menghapusnya
Albert Ruelan
12

⚠️ PERINGATAN: "X terakhir saya komit" mungkin ambigu.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

Dalam riwayat https://github.com/fleetwood-mac/band-history repositori yang sangat singkat ini, Anda telah membuka permintaan tarik untuk menggabungkan komitmen Bill Clinton ke komit asli ( MASTER) Fleetwood Mac.

Anda membuka permintaan tarik dan di GitHub Anda melihat ini:

Empat komitmen:

  • Tambahkan Danny Kirwan
  • Tambahkan Christine Perfect
  • LA1974
  • Bill Clinton

Berpikir bahwa tidak ada yang mau membaca riwayat repositori lengkap. (Sebenarnya ada repositori, klik tautan di atas!) Anda memutuskan untuk menghancurkan komit ini. Jadi kamu pergi dan lari git reset --soft HEAD~4 && git commit. Maka kamugit push --force ke GitHub untuk membersihkan PR Anda.

Dan apa yang terjadi? Anda baru saja membuat komitmen tunggal yang mendapatkan dari Fritz ke Bill Clinton. Karena Anda lupa bahwa kemarin Anda mengerjakan versi Buckingham Nicks dari proyek ini. Dan git logtidak cocok dengan yang Anda lihat di GitHub.

🐻 MORAL KISAH

  1. Menemukan file yang tepat yang Anda ingin mendapatkan untuk , dan git checkoutmereka
  2. Temukan komit sebelumnya yang ingin Anda simpan dalam riwayat, dan git reset --softitu
  3. Buat git commitbengkok itu langsung dari dari ke ke
William Entriken
sumber
1
Ini 100% cara termudah untuk melakukan ini. Jika KEPALA Anda saat ini adalah keadaan yang benar yang Anda inginkan, maka Anda dapat melewati # 1.
Stan
Ini adalah satu-satunya cara saya tahu yang memungkinkan untuk menulis ulang riwayat komit pertama.
Vadorequest
9

Jika Anda tidak peduli tentang pesan komit di antara komitmen, Anda dapat menggunakan

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend
Zsolt Z.
sumber
7

Jika Anda bekerja dengan GitLab, Anda bisa mengklik opsi Squash di Gabung Permintaan seperti yang ditunjukkan di bawah ini. Pesan komit akan menjadi judul Permintaan Gabung.

masukkan deskripsi gambar di sini

arush436
sumber
6
git rebase -i HEAD^^

di mana jumlah ^ adalah X

(dalam hal ini, tekan dua komit terakhir)

Yoaz Menda
sumber
6

Selain jawaban luar biasa lainnya, saya ingin menambahkan bagaimana git rebase -iselalu membingungkan saya dengan perintah komit - lebih lama ke yang lebih baru atau sebaliknya? Jadi ini adalah alur kerja saya:

  1. git rebase -i HEAD~[N], dengan N adalah jumlah komit yang ingin saya gabung, mulai dari yang terbaru . Jadi git rebase -i HEAD~5berarti "squash the 5 commit terakhir menjadi yang baru";
  2. editor muncul, menunjukkan daftar commit yang ingin saya gabungkan. Sekarang mereka ditampilkan dalam urutan terbalik : komit yang lebih tua ada di atas. Tandai sebagai "squash" atau "s" semua komit di sana kecuali yang pertama / yang lebih tua : itu akan digunakan sebagai titik awal. Simpan dan tutup editor;
  3. editor muncul lagi dengan pesan default untuk komit baru: ubah sesuai kebutuhan Anda, simpan dan tutup. Squash selesai!

Sumber & bacaan tambahan: # 1 , # 2 .

Bodoh
sumber
6

Bagaimana dengan jawaban untuk pertanyaan yang terkait dengan alur kerja seperti ini?

  1. banyak komit lokal, dicampur dengan banyak gabungan dari master ,
  2. akhirnya push to remote,
  3. PR dan gabungkan KE master oleh reviewer . (Ya, itu akan lebih mudah bagi pengembang untuk merge --squashsetelah PR, tetapi tim berpikir itu akan memperlambat proses.)

Saya belum melihat alur kerja seperti itu di halaman ini. (Itu mungkin mataku.) Jika aku mengertirebase dengan benar, banyak penggabungan akan membutuhkan beberapa resolusi konflik . Aku bahkan tidak mau memikirkan hal itu!

Jadi, ini sepertinya bekerja untuk kita.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. edit dan lakukan banyak secara lokal, gabungkan master secara teratur
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // menempatkan semua perubahan di panggung
  7. git commit 'one message to rule them all'
  8. git push
  9. Reviewer melakukan PR dan bergabung untuk menguasainya.
allenjom
sumber
Dari banyak pendapat saya suka pendekatan Anda. Ini sangat mudah dan cepat
Artem Solovev
5

Saya menemukan solusi yang lebih umum bukan untuk menentukan komit 'N', melainkan cabang / komit-id yang ingin Anda tekan di atas. Ini lebih rentan kesalahan daripada menghitung komit hingga komit tertentu — cukup tentukan tag secara langsung, atau jika Anda benar-benar ingin menghitung, Anda dapat menentukan HEAD ~ N.

Dalam alur kerja saya, saya memulai cabang, dan komit pertama saya di cabang itu merangkum tujuan (yaitu biasanya apa yang akan saya dorong sebagai pesan 'final' untuk fitur ke repositori publik.) Jadi ketika saya selesai, semua Yang ingin saya lakukan adalah git squash masterkembali ke pesan pertama dan kemudian saya siap untuk mendorong.

Saya menggunakan alias:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

Ini akan membuang sejarah yang tergencet sebelum melakukannya — ini memberi Anda kesempatan untuk memulihkan dengan mengambil ID komit lama dari konsol jika Anda ingin kembali. (Pengguna Solaris mencatat bahwa ia menggunakan -iopsi sed GNU , pengguna Mac dan Linux tidak masalah dengan ini.)

Ethan
sumber
Saya mencoba alias tetapi saya tidak yakin apakah pengganti menggantikan memiliki efek apa pun. Apa yang harus mereka lakukan?
raine
Sed pertama hanya membuang sejarah ke konsol. Sed kedua menggantikan semua 'pick' dengan 'f' (fixup) dan menulis ulang file editor di tempat (opsi -i). Jadi yang kedua melakukan semua pekerjaan.
Ethan
Anda benar, menghitung N-jumlah komit tertentu sangat rawan kesalahan. Itu telah mengacaukan saya beberapa kali dan menghabiskan waktu berjam-jam untuk membatalkan rebase.
IgorGanapolsky
Hai Ethan, saya ingin tahu apakah alur kerja ini akan menyembunyikan kemungkinan konflik pada penggabungan. Jadi tolong pertimbangkan jika Anda memiliki dua cabang master dan slave. Jika budak memiliki konflik dengan master dan kami gunakan git squash mastersaat kami diperiksa pada budak. apa yang akan terjadi akankah kita menyembunyikan konflik?
Sergio Bilello
@Sergio Ini adalah kasus penulisan ulang sejarah, jadi Anda mungkin akan memiliki konflik jika Anda menekan komit yang telah didorong, dan kemudian mencoba untuk menggabungkan / me-rebase versi yang terjepit kembali. (Beberapa kasus sepele mungkin lolos begitu saja.)
Ethan
5

Dalam pertanyaan itu bisa jadi ambigu apa yang dimaksud dengan "terakhir".

misalnya git log --graphmenghasilkan yang berikut (disederhanakan):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

Kemudian komit terakhir berdasarkan waktu adalah H0, bergabung, B0. Untuk menekan mereka, Anda harus rebase cabang gabungan Anda di komit H1.

Masalahnya adalah bahwa H0 mengandung H1 dan H2 (dan umumnya lebih banyak melakukan sebelum penggabungan dan setelah percabangan) sementara B0 tidak. Jadi, Anda harus mengelola perubahan dari setidaknya H0, menggabungkan, H1, H2, B0.

Dimungkinkan untuk menggunakan rebase tetapi dengan cara yang berbeda maka dalam jawaban lain disebutkan:

rebase -i HEAD~2

Ini akan menunjukkan kepada Anda opsi pilihan (sebagaimana disebutkan dalam jawaban lain):

pick B1
pick B0
pick H0

Masukkan squash alih-alih pilih ke H0:

pick B1
pick B0
s H0

Setelah menyimpan dan keluar, rebase akan menerapkan komit setelah H1. Itu berarti bahwa itu akan meminta Anda untuk menyelesaikan konflik lagi (di mana HEAD akan menjadi H1 pada awalnya dan kemudian mengumpulkan komitmen saat mereka diterapkan).

Setelah rebase akan selesai, Anda dapat memilih pesan untuk H0 dan B0 terjepit:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

PS Jika Anda baru saja melakukan reset ke BO: (misalnya, menggunakan reset --mixedyang dijelaskan secara lebih rinci di sini https://stackoverflow.com/a/18690845/2405850 ):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

kemudian Anda menekan B0 perubahan H0, H1, H2 (kehilangan sepenuhnya berkomitmen untuk perubahan setelah bercabang dan sebelum bergabung.

littlewhywhat
sumber
4

1) git reset --kepala KEPALA ~ n

n - Jumlah komit, perlu squash

2) git commit -m "pesan komit baru"

3) git dorong asal branch_name --force

Poonkodi
sumber