Dalam git, apa perbedaan antara menggabungkan - squash dan rebase?

363

Saya baru mengenal git dan saya mencoba memahami perbedaan antara squash dan rebase. Seperti yang saya pahami, Anda melakukan squash saat melakukan rebase.

HAH
sumber

Jawaban:

360

Keduanya git merge --squashdan git rebase --interactivedapat menghasilkan komit "tergencet".
Tetapi mereka melayani tujuan yang berbeda.

akan menghasilkan komit terjepit di cabang tujuan, tanpa menandai hubungan penggabungan.
(Catatan: itu tidak langsung menghasilkan komit: Anda memerlukan tambahan git commit -m "squash branch")
Ini berguna jika Anda ingin membuang cabang sumber sepenuhnya, pergi dari (skema diambil dari pertanyaan SO ):

 git checkout stable

      X                   stable
     /                   
a---b---c---d---e---f---g tmp

untuk:

git merge --squash tmp
git commit -m "squash tmp"

      X-------------------G stable
     /                   
a---b---c---d---e---f---g tmp

dan kemudian menghapus tmpcabang.


Catatan: git mergememiliki --commitopsi , tetapi tidak dapat digunakan dengan --squash. Itu tidak pernah mungkin untuk digunakan --commitdan --squashbersama.
Sejak Git 2.22.1 (Q3 2019), ketidakcocokan ini dibuat eksplisit:

Lihat komit 1d14d0c (24 Mei 2019) oleh Vishal Verma ( reloadbrain) .
(Digabung oleh Junio ​​C Hamano - gitster- dalam komit 33f2790 , 25 Jul 2019)

merge: tolak --commitdengan--squash

Sebelumnya, ketika --squashdisuplai, ' option_commit' diam-diam dijatuhkan. Ini bisa mengejutkan bagi pengguna yang mencoba menimpa perilaku tanpa komitmen dari squash menggunakan --commitsecara eksplisit.

git/git builtin/merge.c#cmd_merge() sekarang termasuk:

if (option_commit > 0)
    die(_("You cannot combine --squash with --commit."));

memutar ulang beberapa atau semua komit Anda pada basis baru, memungkinkan Anda untuk menekan (atau lebih baru-baru ini "memperbaiki", lihat pertanyaan SO ini ), langsung ke:

git checkout tmp
git rebase -i stable

      stable
      X-------------------G tmp
     /                     
a---b

Jika Anda memilih untuk menekan semua komit tmp(tetapi, berlawanan dengan merge --squash, Anda dapat memilih untuk memutar ulang beberapa, dan menekan yang lain).

Jadi perbedaannya adalah:

  • squashtidak menyentuh cabang sumber Anda (di tmpsini) dan membuat komit tunggal di mana Anda inginkan.
  • rebasememungkinkan Anda untuk melanjutkan pada cabang sumber yang sama (masih tmp) dengan:
    • basis baru
    • sejarah yang lebih bersih
VONC
sumber
11
Gadalah c--d--e--f--gtergencet bersama-sama?
Wayne Conrad
8
@Wayne: ya, G dalam contoh tersebut mewakili tmpkomit yang tergencet bersama.
VonC
3
@ Th4wn: Karena Git beralasan dengan snapshot dari semua proyek, Gtidak akan mewakili konten yang sama dengan g, karena perubahan yang diperkenalkan oleh X.
VonC
1
@VonC: tidak yakin tentang komentar terakhir itu. Jika Anda memiliki git merge --no-ff tempalih - alih git merge --squash temp, maka Anda mendapatkan sejarah yang berantakan, tetapi Anda juga dapat melakukan hal-hal seperti git revert e, jauh lebih mudah. Ini adalah sejarah yang berantakan, tapi jujur ​​dan pragmatis, dan cabang utama masih cukup bersih.
naught101
2
@ naught101 Saya setuju. Seperti yang dijelaskan dalam stackoverflow.com/a/7425751/6309 , ini juga tentang tidak melanggar git bisectatau git blameketika digunakan terlalu sering (seperti pada git pull --no-ff: stackoverflow.com/questions/12798767/… ). Lagipula tidak ada satu pendekatan, itulah sebabnya artikel ini menjelaskan tiga ( stackoverflow.com/questions/9107861/… )
VonC
183

Gabung komit: mempertahankan semua komit di cabang Anda dan interleaves dengan komit di cabang dasarmasukkan deskripsi gambar di sini

Gabung Squash: mempertahankan perubahan tetapi menghilangkan komitmen individu dari sejarah masukkan deskripsi gambar di sini

Rebase: Ini menggerakkan seluruh cabang fitur untuk memulai di ujung cabang master, secara efektif menggabungkan semua komit baru di master

masukkan deskripsi gambar di sini

Lebih banyak di sini

Md Ayub Ali Sarker
sumber
81

Gabung squash menggabungkan pohon (urutan komit) menjadi komit tunggal. Artinya, itu menghancurkan semua perubahan yang dilakukan dalam n komit menjadi satu komit.

Rebasing mendasarkan ulang, yaitu, memilih basis baru (induk komit) untuk pohon. Mungkin istilah lincah untuk ini lebih jelas: mereka menyebutnya transplantasi karena hanya itu: mengambil tanah baru (induk berkomitmen, root) untuk pohon.

Saat melakukan rebase interaktif, Anda diberikan pilihan untuk memencet, memilih, mengedit, atau melewatkan komit yang akan Anda rebase.

Harapan itu jelas!

Mauricio Scheffer
sumber
7
Kapan saya harus rebase dan kapan saya harus squash?
Martin Thoma
31

Mari kita mulai dengan contoh berikut:

masukkan deskripsi gambar di sini

Sekarang kami memiliki 3 opsi untuk menggabungkan perubahan cabang fitur ke cabang master :

  1. Menggabungkan komit
    Akan menyimpan semua komit riwayat cabang fitur dan memindahkannya ke cabang master
    Akan menambah komit dummy tambahan.

  2. Rebase dan penggabungan
    Akan menambahkan semua komit histori dari cabang fitur di depan cabang utama
    TIDAK akan menambah komit dummy tambahan.

  3. Squash dan gabung
    Akan mengelompokkan semua cabang fitur berkomitmen menjadi satu komit kemudian menambahkannya di depan cabang utama
    Akan menambah komit dummy tambahan.

Anda dapat menemukan di bawah ini bagaimana cabang master akan menjaga masing-masing dari mereka.

masukkan deskripsi gambar di sini

Dalam semua kasus:
Kami dapat dengan aman HAPUS cabang fitur .

ahmednabil88
sumber
1
dapatkah kau jelaskan apa yang dummy commit di gambar 2 ?? Saya seorang pemula di git.
Yusuf
1
@ Yufuf, ini hanya komit tambahan yang berisi pembaruan kedua cabang, itu adalah pesan komit standar = "Cabang XYZ menjadi master"
ahmednabil88