Mengapa git melakukan penggabungan maju-cepat secara default?

641

Berasal dari merkuri, saya menggunakan cabang untuk mengatur fitur. Secara alami, saya ingin melihat alur kerja ini dalam sejarah saya juga.

Saya memulai proyek baru saya menggunakan git dan menyelesaikan fitur pertama saya. Ketika menggabungkan fitur, saya menyadari git menggunakan fast-forward, yaitu itu menerapkan perubahan saya langsung ke cabang master jika memungkinkan dan lupa tentang cabang saya.

Jadi untuk memikirkan masa depan: Saya satu-satunya yang mengerjakan proyek ini. Jika saya menggunakan pendekatan default git (penggabungan maju cepat), riwayat saya akan menghasilkan satu cabang master raksasa. Tidak ada yang tahu saya menggunakan cabang terpisah untuk setiap fitur, karena pada akhirnya saya hanya akan memiliki cabang master raksasa. Bukankah itu terlihat tidak profesional?

Dengan alasan ini, saya tidak ingin penggabungan maju cepat dan tidak bisa melihat mengapa itu default. Apa bagusnya itu?

Florian Pilz
sumber
38
Catatan: lihat juga sandofsky.com/blog/git-workflow.html , dan hindari ' no-ff' dengan "checkpoint commit" yang memecah dua bagian atau disalahkan.
VonC
15
apakah kamu menyesal menggunakan git pada proyek one man?
HaveAGuess
27
Benar-benar tidak! Di folder kerja saya, saya memiliki 7 proyek satu orang di mana saya menggunakan git. Biarkan saya ulangi: saya memulai banyak proyek sejak saya menanyakan pertanyaan ini dan semuanya versi melalui git. Sejauh yang saya tahu, hanya git dan lincah mendukung versi lokal, yang penting bagi saya sejak saya terbiasa. Sangat mudah untuk mengaturnya dan Anda selalu memiliki seluruh sejarah bersama Anda. Dalam proyek grup, ini lebih baik lagi, karena Anda dapat melakukan tanpa mengganggu siapa pun dengan kode pekerjaan Anda yang sedang berjalan. Selain itu saya menggunakan github untuk membagikan beberapa proyek saya (mis. Mikro-optparse) di mana git merupakan persyaratan.
Florian Pilz
7
@ Cawas benar, -no-ffjarang merupakan ide yang baik, tetapi masih dapat membantu menjaga riwayat fitur-internal saat merekam hanya satu komit di cabang utama. Itu masuk akal untuk sejarah fitur yang panjang, ketika Anda menggabungkan dari waktu ke waktu perkembangannya di cabang utama.
VonC
12
Ngomong-ngomong, untuk pertanyaan Anda tentang "Bukankah itu [sejarah cabang linear] terlihat tidak profesional?". Tidak ada yang tidak profesional tentang menggunakan sistem kode sumber dengan standarnya. Ini bukan tentang profesionalisme. Ini tentang menentukan filosofi percabangan mana Anda berlangganan Anda. Sebagai contoh, @VonC ditautkan dengan artikel sandofsky di mana ia memperjuangkan fast forward sebagai pendekatan yang unggul. Bukan benar atau salah, hanya filosofi yang berbeda untuk konteks yang berbeda.
Marplesoft

Jawaban:

718

Penggabungan fast-forward masuk akal untuk cabang-cabang yang berumur pendek, tetapi dalam sejarah yang lebih kompleks , penggabungan non-fast-forward dapat membuat sejarah lebih mudah untuk dipahami, dan membuatnya lebih mudah untuk mengembalikan sekelompok komitmen.

Peringatan : Non-fast-forwarding juga memiliki efek samping. Harap tinjau https://sandofsky.com/blog/git-workflow.html , hindari 'no-ff' dengan "checkpoint commit" yang melanggar dua bagian atau disalahkan, dan dengan hati-hati mempertimbangkan apakah itu harus menjadi pendekatan default Anda master.

teks alternatif
(Dari nvie.com , Vincent Driessen , pos " Model percabangan Git yang sukses ")

Memasukkan fitur selesai pada pengembangan

Fitur yang sudah jadi dapat digabungkan ke dalam cabang pengembangan untuk menambahkannya ke rilis yang akan datang:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

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

Jakub Narębski juga menyebutkan para configmerge.ff :

Secara default, Git tidak membuat komit gabungan tambahan saat menggabungkan komit yang merupakan turunan dari komit saat ini. Alih-alih, ujung cabang saat ini diteruskan dengan cepat.
Ketika diatur ke false, variabel ini memberitahu Git untuk membuat komit gabungan tambahan dalam kasus seperti itu (setara dengan memberikan --no-ffopsi dari baris perintah).
Ketika diatur ke ' only', hanya penggabungan maju-cepat yang diizinkan (setara dengan memberikan --ff-onlyopsi dari baris perintah).


Maju-cepat adalah default karena:

  • cabang berumur pendek sangat mudah dibuat dan digunakan di Git
  • cabang berumur pendek sering kali mengisolasi banyak komitmen yang dapat diatur ulang secara bebas di dalam cabang itu
  • komitmen tersebut sebenarnya adalah bagian dari cabang utama: setelah direorganisasi, cabang utama diteruskan dengan cepat untuk memasukkannya.

Tetapi jika Anda mengantisipasi alur kerja yang berulang pada satu cabang topik / fitur (yaitu, saya bergabung, maka saya kembali ke cabang fitur ini dan menambahkan beberapa komit), maka akan berguna untuk hanya memasukkan penggabungan di cabang utama, daripada semua komit menengah dari cabang fitur.

Dalam hal ini, Anda bisa mengatur file konfigurasi seperti ini :

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

OP menambahkan dalam komentar:

Saya melihat beberapa hal dalam fast-forward untuk cabang-cabang [berumur pendek], tetapi menjadikannya tindakan standar berarti bahwa git mengasumsikan Anda ... sering memiliki cabang-cabang [berumur pendek]. Masuk akal?

Jefromi menjawab:

Saya pikir masa hidup cabang sangat bervariasi dari pengguna ke pengguna. Di antara pengguna yang berpengalaman, mungkin ada kecenderungan untuk memiliki cabang yang lebih pendek.

Bagi saya, cabang yang berumur pendek adalah cabang yang saya buat untuk membuat operasi tertentu lebih mudah (rebasing, kemungkinan, atau patching cepat dan pengujian), dan kemudian segera hapus setelah saya selesai.
Itu artinya kemungkinan harus diserap ke dalam cabang topik yang bercabangnya , dan cabang topik akan digabung sebagai satu cabang. Tidak ada yang perlu tahu apa yang saya lakukan secara internal untuk membuat serangkaian komitmen yang mengimplementasikan fitur yang diberikan.

Secara umum, saya menambahkan:

itu benar-benar tergantung pada alur kerja pengembangan Anda :

  • jika linier, satu cabang masuk akal.
  • Jika Anda perlu mengisolasi fitur dan mengerjakannya untuk jangka waktu yang lama dan berulang kali menggabungkannya, beberapa cabang masuk akal.

Lihat " Kapan Anda harus bercabang? "

Sebenarnya, ketika Anda mempertimbangkan model cabang Mercurial, pada intinya satu cabang per repositori (meskipun Anda dapat membuat kepala anonim, bookmark, dan bahkan cabang bernama )
Lihat "Git dan Mercurial - Bandingkan dan Kontras" .

Mercurial, secara default, menggunakan codelines ringan anonim, yang dalam terminologinya disebut "kepala".
Git menggunakan cabang bernama ringan, dengan pemetaan injeksi untuk memetakan nama-nama cabang dalam repositori jarak jauh ke nama-nama cabang pelacakan jarak jauh.
Git "memaksa" Anda untuk menamai cabang (dengan pengecualian satu cabang yang tidak disebutkan namanya, yang merupakan situasi yang disebut " kepala terpisah "), tapi saya pikir ini bekerja lebih baik dengan alur kerja cabang-berat seperti alur kerja cabang topik, yang berarti banyak cabang dalam paradigma repositori tunggal.

VONC
sumber
Wow, itu cepat. ;) Saya tahu tentang opsi --no-ff, tetapi hanya mencari tahu tentang fast-forward setelah saya mengacaukan fitur saya. Saya melihat beberapa hal di fast-forward untuk cabang hidup pendek, tetapi menjadikannya tindakan standar berarti, bahwa git mengasumsikan Anda paling sering memiliki cabang hidup pendek. Masuk akal?
Florian Pilz
@Florian: Saya percaya ini adalah pandangan yang masuk akal dari prosesnya. Saya telah menambahkan contoh konfigurasi yang akan mengatur cara Anda ingin mengelola gabungan untuk dikuasai.
VonC
Terima kasih, file config akan membantu menghindari gotcha semacam itu. :) Hanya menerapkan konfigurasi ini secara lokal dengan "git config branch.master.mergeoptions '--no-ff'"
Florian Pilz
2
@BehrangSaeedzadeh: rebasing adalah topik lain (yang belum disebutkan di halaman ini): selama Anda belum mendorong featurecabang ke repo publik, Anda dapat rebase ulang mastersebanyak yang Anda inginkan. Lihat stackoverflow.com/questions/5250817/…
VonC
4
@BehrangSaeedzadeh: rebase dengan sendirinya tidak akan membuat sejarah linear. Ini adalah bagaimana Anda mengintegrasikan perubahan featurecabang Anda kembali ke masteryang membuat sejarah kata itu linear atau tidak. Penggabungan cepat foward sederhana akan membuatnya linier. Yang masuk akal jika Anda telah membersihkan sejarah featurecabang itu sebelum penggabungan maju-cepat, hanya menyisakan komit yang signifikan, sebagaimana disebutkan dalam stackoverflow.com/questions/7425541/… .
VonC
42

Mari saya memperluas sedikit pada VonC 's jawaban yang sangat komprehensif :


Pertama, jika saya mengingatnya dengan benar, fakta bahwa Git secara default tidak membuat komit gabungan dalam kasus fast-forward berasal dari mempertimbangkan cabang tunggal "repositori yang sama", di mana tarikan bersama digunakan untuk menyinkronkan kedua repositori tersebut (a alur kerja Anda dapat menemukan contoh pertama di sebagian besar dokumentasi pengguna, termasuk "Manual Pengguna Git" dan "Kontrol Versi dengan Contoh"). Dalam hal ini Anda tidak menggunakan tarikan untuk menggabungkan cabang yang sepenuhnya terealisasi, Anda menggunakannya untuk mengimbangi pekerjaan lain. Anda tidak ingin memiliki fakta sementara dan tidak penting ketika Anda melakukan sinkronisasi disimpan dan disimpan dalam repositori, disimpan untuk masa depan.

Perhatikan bahwa kegunaan cabang fitur dan memiliki beberapa cabang dalam repositori tunggal baru muncul kemudian, dengan penggunaan VCS yang lebih luas dengan dukungan penggabungan yang baik, dan dengan mencoba berbagai alur kerja berbasis gabungan. Itulah sebabnya misalnya Mercurial awalnya hanya mendukung satu cabang per repositori (ditambah kiat anonim untuk melacak cabang jarak jauh), seperti terlihat dalam revisi lama "Mercurial: The Definitive Guide".


Kedua, ketika mengikuti praktik terbaik menggunakan cabang fitur , yaitu bahwa cabang fitur harus semuanya mulai dari versi stabil (biasanya dari rilis terakhir), untuk dapat memilih dan memilih fitur yang akan disertakan dengan memilih cabang fitur mana yang akan digabung, Anda biasanya tidak dalam situasi maju cepat ... yang membuat masalah ini diperdebatkan. Anda perlu khawatir tentang membuat gabungan sebenarnya dan tidak maju cepat ketika menggabungkan cabang pertama (dengan asumsi bahwa Anda tidak menempatkan perubahan komit tunggal langsung pada 'master'); semua penggabungan nanti lainnya tentu saja dalam situasi non-maju cepat.

HTH

Jakub Narębski
sumber
Mengenai cabang fitur dari rilis stabil: Bagaimana jika fitur yang saya kembangkan untuk rilis berikutnya tergantung pada perubahan untuk fitur lain yang telah saya kembangkan dan gabungkan menjadi master? Tentunya itu berarti saya harus membuat cabang fitur kedua ini dari master?
dOxxx
1
@dOxxx: Ya, ada pengecualian, seperti misalnya di mana satu cabang dibangun di atas yang lain (baik secara langsung, atau setelah menggabungkan sebelumnya menjadi master).
Jakub Narębski