Apa perbedaan antara `git merge` dan` git merge --no-ff`?

Jawaban:

1080

The --no-ffbendera mencegah git mergedari mengeksekusi "fast-forward" jika mendeteksi bahwa saat ini Anda HEADadalah nenek moyang komit Anda mencoba untuk menggabungkan. Sebuah fast-forward adalah ketika, alih-alih membangun komit gabungan, git hanya memindahkan pointer cabang Anda ke titik di komit yang masuk. Ini biasanya terjadi ketika melakukan git pulltanpa perubahan lokal.

Namun, kadang-kadang Anda ingin mencegah perilaku ini terjadi, biasanya karena Anda ingin mempertahankan topologi cabang tertentu (misalnya Anda menggabungkan dalam cabang topik dan Anda ingin memastikannya terlihat seperti itu ketika membaca sejarah). Untuk melakukan itu, Anda bisa melewati --no-ffflag dan git mergeakan selalu membuat gabungan alih-alih penerusan cepat.

Demikian pula, jika Anda ingin mengeksekusi git pullatau menggunakan git mergesecara eksplisit fast-forward, dan Anda ingin bail out jika tidak bisa fast-forward, maka Anda bisa menggunakan --ff-onlyflag. Dengan cara ini Anda dapat secara teratur melakukan sesuatu seperti git pull --ff-onlytanpa berpikir, dan kemudian jika itu salah, Anda dapat kembali dan memutuskan apakah Anda ingin bergabung atau rebase.

Lily Ballard
sumber
87
Untuk lebih langsung menjawab pertanyaan OP: mereka tidak selalu berbeda, tetapi jika ya, jelas dari gitkatau git log --graphbahwa penggabungan maju tidak membuat komit penggabungan, sedangkan yang tidak maju cepat.
Cascabel
11
Akan menyenangkan untuk memperluas alasan untuk menghindari ff: penulis menyebutkan "topologi cabang spesifik" berarti bahwa --no-ff komit penggabungan tambahan berfungsi sebagai penanda penggabungan. Pro adalah penanda gabungan eksplisit dengan nama penulis dan merger. Kontra adalah sejarah non-linear yang terlihat seperti seperangkat rel kereta api konvergen. Efek samping psikologis yang mungkin dari penggabungan ini adalah kontributor kehilangan minat karena proses peninjauan yang lebih lama: blog.spreedly.com/2014/06/24/…
Vlad
6
Apakah adil untuk mengatakan bahwa --no-ffdari fitur untuk dikembangkan atau dikembangkan untuk dikuasai mirip dengan menggabungkan permintaan tarik?
merlinpatt
9
@merlinpatt Tentu. Jika Anda menggabungkan permintaan tarik di GitHub, itu setara dengan --no-ff.
Lily Ballard
1
Ini penjelasan yang bagus. Sobat, tidak ada cukup penjelasan git yang baik di luar sana bagi orang-orang untuk memahami mengapa sejarah bersih git benar-benar penting (termasuk saya!)
dudewad
1037

Jawaban grafis untuk pertanyaan ini

Berikut adalah situs dengan penjelasan yang jelas dan ilustrasi grafis tentang penggunaan git merge --no-ff:

perbedaan antara git merge --no-ff dan git merge

Sampai saya melihat ini, saya benar-benar bingung dengan git. Menggunakan --no-ffmemungkinkan seseorang meninjau riwayat untuk melihat dengan jelas cabang yang Anda periksa untuk dikerjakan. (tautan itu menunjuk ke alat visualisasi "jaringan" github) Dan inilah referensi hebat lainnya dengan ilustrasi. Referensi ini melengkapi yang pertama dengan lebih fokus pada mereka yang kurang mengenal git.


Info dasar untuk pemula seperti saya

Jika Anda seperti saya, dan bukan seorang Git-guru, jawaban saya di sini menjelaskan tentang menangani penghapusan file dari pelacakan git tanpa menghapusnya dari sistem file lokal, yang sepertinya tidak terdokumentasi dengan baik tetapi sering kali terjadi. Situasi newb lain adalah mendapatkan kode saat ini , yang masih berhasil menghindari saya.


Contoh Alur Kerja

Saya memperbarui paket ke situs web saya dan harus kembali ke catatan saya untuk melihat alur kerja saya; Saya pikir bermanfaat untuk menambahkan contoh pada jawaban ini.

Alur kerja git saya:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

Bawah: penggunaan aktual, termasuk penjelasan.
Catatan: output di bawah ini terpotong; git cukup bertele-tele.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

Perhatikan 3 hal dari atas:
1) Dalam output Anda dapat melihat perubahan dari upgrade paket ECC, termasuk penambahan file baru.
2) Juga perhatikan ada dua file (tidak ada dalam /eccfolder) yang saya hapus terlepas dari perubahan ini. Alih-alih membingungkan penghapusan file dengan ecc, saya akan membuat cleanupcabang yang berbeda nanti untuk mencerminkan penghapusan file-file itu.
3) Saya tidak mengikuti alur kerja saya! Saya lupa tentang git ketika saya mencoba membuat ECC bekerja kembali.

Bawah: daripada melakukan semua-inklusif seperti git commit -am "updated ecc package"biasanya, saya hanya ingin menambahkan file di /eccfolder. File-file yang dihapus itu bukan bagian dari saya git add, tetapi karena sudah dilacak di git, saya harus menghapusnya dari komit cabang ini:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To [email protected]:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Script untuk mengotomatisasi di atas

Setelah menggunakan proses ini 10+ kali dalam sehari, saya telah menulis skrip batch untuk menjalankan perintah, jadi saya membuat git_update.sh <branch> <"commit message">skrip yang hampir tepat untuk melakukan langkah-langkah di atas. Berikut adalah sumber inti untuk skrip itu.

Alih-alih git commit -amsaya memilih file dari daftar "dimodifikasi" yang dihasilkan melalui git statusdan kemudian menempelkan mereka dalam skrip ini. Ini terjadi karena saya membuat lusinan pengeditan tetapi menginginkan berbagai nama cabang untuk membantu mengelompokkan perubahan.

Chris K
sumber
11
Bisakah Anda masih menghapus cabang dengan aman setelah Anda menggabungkan --no-ffopsi?
Andy Fleming
17
@DesignerGuy ya Anda bisa menghapus cabang lama dengan aman. Pikirkan branch es hanya sebagai penunjuk ke komit tertentu.
Zyphrax
2
Saya menemukan teks ini dari halaman tertaut sangat membantu: tanpa --no-ff "tidak mungkin untuk melihat dari sejarah Git mana dari objek komit bersama-sama telah menerapkan fitur - Anda harus membaca secara manual semua pesan log."
Lorne Laliberte
satu gambar selalu lebih baik dari seribu kata!
rupps
1
Grafik tidak menunjukkan cabang fitur pada gambar kedua, mengapa tidak? Masih ada kan?
ADJenks
269

Gabungkan Strategi

Penggabungan Eksplisit : Membuat komit gabungan baru. (Ini yang akan kamu dapatkan jika kamu digunakan --no-ff.)

masukkan deskripsi gambar di sini

Penggabungan Maju Cepat: Maju cepat, tanpa membuat komit baru:

masukkan deskripsi gambar di sini

Rebase : Menetapkan level dasar baru:

masukkan deskripsi gambar di sini

Squash: Hancurkan atau remas (sesuatu) dengan paksa sehingga menjadi rata:

masukkan deskripsi gambar di sini

Premraj
sumber
11
Grafik yang menarik tetapi tidak benar-benar menunjukkan kasus --no-ff dan karenanya tidak cukup menjawab pertanyaan di sini
Vib
22
Yang pertama, "gabungan eksplisit", berjudul di sini sebagai "gabungan", adalah gabungan non-ff.
bkribbs
3
grafis yang sangat membantu saya
exexzian
2
tidak menjawab pertanyaan per katakan, tetapi grafik ini luar biasa, UPVOTE!
SovietFrontier
3
Jadi apa perbedaan antara Rebase dan penggabungan maju-cepat?
ThanosFisherman
217

The --no-ffMemastikan opsi yang maju penggabungan cepat tidak akan terjadi, dan bahwa komit baru objek akan selalu dibuat . Ini dapat diinginkan jika Anda ingin git mempertahankan riwayat cabang fitur.             git merge --no-ff vs git merge Pada gambar di atas, sisi kiri adalah contoh dari sejarah git setelah menggunakan git merge --no-ffdan sisi kanan adalah contoh menggunakan di git mergemana penggabungan ff dimungkinkan.

EDIT : Versi gambar ini sebelumnya hanya mengindikasikan satu induk tunggal untuk komit gabungan. Komitmen gabungan memiliki beberapa komitmen induk yang digunakan git untuk mempertahankan riwayat "cabang fitur" dan cabang asli. Beberapa tautan induk disorot dalam warna hijau.

Daniel Smith
sumber
3
Apa yang ditunjukkan panah hijau vs panah hitam?
rtconner
11
panah hijau menunjukkan tautan antara komit dan induk di mana komit memiliki lebih dari satu induk. Komit normal (hitam) hanya memiliki satu orangtua.
Daniel Smith
9
@DanielSmith Bagaimana Anda menggambar grafik elegan ini?
chancyWu
4
@ chancyWu dengan Adobe Illustrator
Daniel Smith
Sepertinya di perusahaan saya semua permintaan tarik digabung dengan opsi --no-ff. Dalam keadaan itu, apakah masuk akal untuk rebase sebelum saya membuat permintaan tarik?
Nickpick
37

Ini adalah pertanyaan lama, dan ini agak halus disebutkan dalam posting lain, tetapi penjelasan yang membuat klik ini untuk saya adalah bahwa penggabungan non-maju akan memerlukan komit terpisah .

Parris Varney
sumber
tolong tinjau alur kerja dalam jawaban saya di atas: apakah ini berarti saya perlu komitmen tambahan di luar apa yang saya dapatkan sekarang? Terima kasih.
Chris K
3
@ Chris git merge --no-ff eccAnda hanya akan memiliki komit gabungan tambahan git loguntuk master cabang. Itu secara teknis tidak diperlukan jika master menunjuk ke leluhur langsung dari komit ecc aktif tetapi dengan menentukan opsi --no-ff Anda memaksa penciptaan komit gabungan tersebut. Judulnya:Merge branch 'ecc'
Ankur Agarwal
Ringkasan hebat! :)
Eduard
4

Bendera --no-ff menyebabkan penggabungan untuk selalu membuat objek komit baru, bahkan jika penggabungan dapat dilakukan dengan fast-forward. Ini menghindari kehilangan informasi tentang keberadaan historis cabang fitur dan grup bersama-sama semua komit yang bersama-sama menambahkan fitur

jsina
sumber