Kapan Anda menggunakan Git rebase dan bukannya Git?

1549

Kapan direkomendasikan untuk menggunakan Git rebase vs. Git merge?

Apakah saya masih perlu bergabung setelah rebase yang berhasil?

Coocoo4Cocoa
sumber
16
ini adalah baik: atlassian.com/git/tutorials/merging-vs-rebasing
stackexchanger
6
Satu masalah dengan orang yang suka menggunakan rebase adalah menghalangi mereka mendorong kode mereka secara teratur. Jadi menginginkan riwayat bersih mencegah mereka berbagi kode, yang menurut saya lebih penting.
static_rtti
9
@static_rtti: Itu tidak benar. Anda menggunakan aliran berbasis rebase yang salah jika itu mencegah Anda mendorong perubahan Anda secara teratur.
juzzlin
5
Sangat memalukan bahwa jawaban Andrew Arnott dan jawaban Pace tidak diposting sebelumnya, karena mereka menjawab pertanyaan ini lebih komprehensif daripada jawaban sebelumnya yang telah mengumpulkan banyak suara.
Mark Booth

Jawaban:

1136

Versi pendek

  • Menggabungkan mengambil semua perubahan dalam satu cabang dan menggabungkannya ke cabang lain dalam satu komit.
  • Rebase bilang aku ingin titik di mana aku bercabang untuk pindah ke titik awal yang baru

Jadi kapan Anda menggunakan keduanya?

Menggabungkan

  • Katakanlah Anda telah membuat cabang untuk tujuan mengembangkan satu fitur. Saat Anda ingin mengembalikan perubahan itu ke master, Anda mungkin ingin menggabungkan (Anda tidak peduli mempertahankan semua komitmen sementara).

Rebase

  • Skenario kedua adalah jika Anda mulai melakukan pengembangan dan kemudian pengembang lain membuat perubahan yang tidak terkait. Anda mungkin ingin menarik dan kemudian rebase untuk mendasarkan perubahan Anda dari versi saat ini dari repositori.
Rob Di Marco
sumber
105
@Rob disebutkan mempertahankan komit sementara ketika bergabung. Saya percaya secara default menggabungkan cabang B (cabang fitur yang telah Anda kerjakan) menjadi cabang M (cabang master) akan membuat satu komit di M untuk setiap komit yang dibuat di B sejak keduanya berbeda. Tetapi jika Anda bergabung menggunakan opsi - squash, semua komit yang dibuat pada cabang B akan "disatukan" dan digabung sebagai komit tunggal pada cabang M, menjaga agar log di cabang master Anda tetap bagus dan bersih. Squashing mungkin adalah yang Anda inginkan jika Anda memiliki banyak pengembang yang bekerja secara mandiri dan bergabung kembali menjadi master.
spaaarky21
19
Saya percaya anggapan @ spaaarky21 tentang penggabungan tidak benar. Jika Anda menggabungkan cabang B ke master M, hanya akan ada satu komit di M (bahkan jika B memiliki banyak komit), terlepas dari apakah Anda menggunakan gabungan polos atau - squash. Apa --squash akan lakukan adalah menghilangkan referensi ke B sebagai orangtua. Visualisasi yang baik ada di sini: syntevo.com/smartgithg/howtos.html?page=workflows.merge
jpeskin
14
@ jpeskin Bukan itu yang saya lihat. Saya baru saja melakukan tes cepat untuk memverifikasi. Buat direktori dengan file teks, initrepo baru, addfile dan commit. Periksa cabang fitur baru ( checkout -b feature.) Ubah file teks, komit dan ulangi sehingga ada dua komitmen baru pada cabang fitur. Lalu checkout masterdan merge feature. Di log, saya melihat komit awal saya pada master, diikuti oleh dua yang digabungkan dari fitur. Jika Anda merge --squash feature, fitur digabung menjadi master tetapi tidak berkomitmen, sehingga satu-satunya commit baru pada master akan menjadi yang Anda buat sendiri.
spaaarky21
21
@ spaaarky21 Sepertinya kami berdua setengah benar. Ketika penggabungan maju-cepat dimungkinkan (seperti pada contoh Anda), git akan secara default menyertakan semua komit di cabang fitur B (atau seperti yang Anda sarankan, Anda dapat menggunakan --sash untuk menggabungkan menjadi satu komit). Tetapi dalam kasus di mana ada dua cabang divergen M dan B yang Anda gabungkan, git tidak akan menyertakan semua komit individu dari cabang B jika digabung menjadi M (terlepas atau tidak Anda menggunakan - squash).
jpeskin
6
Mengapa "(Anda tidak peduli mempertahankan semua komitmen sementara)" di samping masih ada dalam jawaban ini? Itu tidak masuk akal di '09 dan tidak masuk akal sekarang. Selain itu, tentunya Anda hanya ingin melakukan rebase jika pengembang lain membuat perubahan terkait yang Anda butuhkan - jika mereka membuat perubahan yang tidak terkait, cabang fitur Anda harus bergabung dengan mudah tanpa konflik, dan sejarah Anda akan dipertahankan.
Mark Booth
372

Itu mudah. Dengan rebase Anda mengatakan untuk menggunakan cabang lain sebagai basis baru untuk pekerjaan Anda.

Jika Anda memiliki, misalnya, cabang master, Anda membuat cabang untuk mengimplementasikan fitur baru, dan mengatakan Anda beri nama cool-feature, tentu saja cabang master adalah basis untuk fitur baru Anda.

Sekarang pada titik tertentu Anda ingin menambahkan fitur baru yang Anda terapkan di mastercabang. Anda bisa beralih ke masterdan menggabungkan cool-featurecabang:

$ git checkout master
$ git merge cool-feature

Tapi dengan cara ini dummy commit baru ditambahkan. Jika Anda ingin menghindari sejarah spageti, Anda dapat rebase :

$ git checkout cool-feature
$ git rebase master

Dan kemudian menggabungkannya dalam master:

$ git checkout master
$ git merge cool-feature

Kali ini, karena cabang topik memiliki komit yang sama ditambah komit dengan fitur baru, penggabungan akan menjadi langkah maju.

Aldo 'xoen' Giambelluca
sumber
31
but this way a new dummy commit is added, if you want to avoid spaghetti-history- bagaimana kabar buruknya?
ア レ ッ ク ス
6
Juga, flag gabungan --no-ff sangat berguna.
Aldo 'xoen' Giambelluca
3
@ ア レ ッ ク ス seperti yang Sean Schofielddimasukkan pengguna dalam komentar: "Rebase juga bagus karena sekali Anda akhirnya menggabungkan barang-barang Anda kembali ke master (yang sepele seperti yang sudah dijelaskan) Anda memilikinya duduk di" atas "dari sejarah komit Anda. memproyeksikan di mana fitur dapat ditulis tetapi digabung beberapa minggu kemudian, Anda tidak ingin hanya menggabungkan mereka ke dalam master karena mereka "dimasukkan" ke dalam master jalan kembali dalam sejarah. Secara pribadi saya suka bisa melakukan git log dan melihat fitur baru-baru ini tepat di "atas." Perhatikan tanggal komit dipertahankan - rebase tidak mengubah informasi itu. "
Adrien Be
4
Saya pikir itu diulang di sini - ingat bahwa semua istilah-istilah ini ( merge, rebase, fast-forward, dll) mengacu pada manipulasi tertentu dari grafik asiklik diarahkan. Mereka menjadi lebih mudah untuk berpikir dengan model mental itu dalam pikiran.
Roy Tinker
10
@Aldo Tidak ada yang "bersih" atau "rapi" tentang riwayat rebased. Ini umumnya kotor dan IMHO mengerikan karena Anda tidak tahu apa yang sebenarnya terjadi. Sejarah Git "terbersih" adalah yang benar-benar terjadi. :)
Marnen Laibow-Koser
269

Untuk melengkapi jawaban saya sendiri yang disebutkan oleh TSamper ,

  • rebase seringkali merupakan ide yang baik untuk dilakukan sebelum penggabungan, karena idenya adalah bahwa Anda mengintegrasikan Ypekerjaan cabang Anda di cabang Byang akan Anda gabungkan.
    Tetapi sekali lagi, sebelum penggabungan, Anda menyelesaikan konflik apa pun di cabang Anda (yaitu: "rebase", seperti dalam "memutar ulang pekerjaan saya di cabang saya mulai dari titik baru-baru ini dari cabang B).
    Jika dilakukan dengan benar, penggabungan berikutnya dari cabang Anda ke cabang Bbisa maju cepat.

  • gabungan secara langsung berdampak pada cabang tujuan B, yang berarti gabungan sebaiknya sepele, jika tidak cabang itu Bbisa lama untuk kembali ke keadaan stabil (waktu untuk Anda menyelesaikan semua konflik)


titik penggabungan setelah rebase?

Dalam hal yang saya jelaskan, saya Bnaik ke cabang saya, hanya untuk memiliki kesempatan untuk memutar ulang pekerjaan saya dari titik yang lebih baru B, tetapi sambil tetap berada di cabang saya.
Dalam hal ini, penggabungan masih diperlukan untuk membawa pekerjaan "replayed" saya B.

Skenario lain ( dijelaskan dalam Git Ready misalnya), adalah membawa pekerjaan Anda langsung Bmelalui rebase (yang menghemat semua komitmen baik Anda, atau bahkan memberi Anda kesempatan untuk memesan ulang melalui rebase interaktif).
Dalam hal itu (di mana Anda rebase saat berada di cabang B), Anda benar: tidak diperlukan penggabungan lebih lanjut:

Sebuah pohon Git secara default ketika kami belum bergabung atau rebased

rebase1

kita dapatkan dengan rebasing:

rebase3

Skenario kedua adalah semua tentang: bagaimana cara mendapatkan fitur baru kembali menjadi master.

Maksud saya, dengan menggambarkan skenario rebase pertama, adalah untuk mengingatkan semua orang bahwa rebase juga dapat digunakan sebagai langkah awal untuk itu (yaitu "mendapatkan fitur baru kembali menjadi master").
Anda dapat menggunakan rebase untuk pertama kali membawa master "dalam" cabang fitur baru: rebase akan memutar ulang fitur baru yang dilakukan dari HEAD master, tetapi masih di cabang fitur baru, secara efektif memindahkan titik awal cabang Anda dari komit master lama HEAD-master.
Itu memungkinkan Anda untuk menyelesaikan konflik apa pun di cabang Anda (artinya, dalam isolasi, sambil memungkinkan master untuk terus berkembang secara paralel jika tahap resolusi konflik Anda terlalu lama).
Kemudian Anda dapat beralih ke master dan menggabungkan new-feature(atau rebase new-featureke masterjika Anda ingin mempertahankan komit dilakukan di Andanew-feature cabang).

Begitu:

  • "rebase vs. merge" dapat dilihat sebagai dua cara untuk mengimpor karya pada, katakanlah master,.
  • Tapi "rebase lalu gabung" bisa menjadi alur kerja yang valid untuk menyelesaikan konflik terlebih dahulu secara terpisah, lalu mengembalikan pekerjaan Anda.
VONC
sumber
17
menggabungkan setelah rebase adalah langkah cepat sepele tanpa harus menyelesaikan konflik.
obecalp
4
@obelcap: Memang, ini adalah semacam ide: Anda mengambil semua masalah-konflik di lingkungan Anda (rebase master dalam cabang fitur baru Anda), dan kemudian co master, menggabungkan fitur-baru: 1 pico-detik (cepat- maju) jika master tidak memiliki evolusi
VonC
27
Rebase juga bagus karena sekali Anda akhirnya menggabungkan barang-barang Anda menjadi master (yang sepele seperti yang sudah dijelaskan) Anda memilikinya duduk di "atas" dari sejarah komit Anda. Pada proyek yang lebih besar di mana fitur dapat ditulis tetapi digabungkan beberapa minggu kemudian, Anda tidak ingin hanya menggabungkannya ke master karena mereka "dimasukkan" ke dalam master jalan kembali dalam sejarah. Secara pribadi saya suka bisa melakukan git log dan melihat fitur terbaru tepat di "atas." Perhatikan tanggal komit dipertahankan - rebase tidak mengubah informasi itu.
Sean Schofield
3
@ Jo: mental, Anda mengatakan "memutar ulang setiap perubahan saya (dilakukan secara terpisah di cabang pribadi saya) di atas cabang lain, tetapi tinggalkan saya di cabang pribadi saya setelah rebase dilakukan". Itu adalah kesempatan yang baik untuk membersihkan sejarah lokal, menghindari "melakukan pos pemeriksaan", membagi dua dan hasil kesalahan yang salah. Lihat "Alur kerja Git": sandofsky.com/blog/git-workflow.html
VonC
4
@scoarescoadalah kuncinya adalah untuk melihat bagaimana perubahan lokal Anda kompatibel di atas cabang hulu terbaru. Jika salah satu komit Anda menimbulkan konflik, Anda akan segera melihatnya. Penggabungan memperkenalkan hanya satu (gabungan) komit, yang dapat memicu banyak konflik tanpa cara mudah untuk melihat mana, di antara komitmen lokal Anda sendiri, yang menambahkan konflik tersebut. Jadi selain riwayat bersih, Anda mendapatkan tampilan yang lebih tepat dari perubahan yang Anda perkenalkan, komit dengan komit (diulang oleh rebase), sebagai lawan dari semua perubahan yang diperkenalkan oleh cabang hulu (dibuang ke satu penggabungan tunggal).
VonC
229

TL; DR

Jika Anda ragu, gunakan gabungan.

Jawaban singkat

Satu-satunya perbedaan antara rebase dan gabungan adalah:

  • Struktur pohon yang dihasilkan dari sejarah (umumnya hanya terlihat ketika melihat grafik komit) berbeda (satu akan memiliki cabang, yang lain tidak akan).
  • Menggabungkan umumnya akan membuat komit tambahan (mis. Simpul di pohon).
  • Gabung dan rebase akan menangani konflik secara berbeda. Rebase akan menyajikan konflik satu komit pada saat penggabungan akan menyajikannya sekaligus.

Jadi jawaban singkatnya adalah memilih rebase atau gabung berdasarkan seperti apa Anda ingin sejarah Anda terlihat .

Jawaban panjang

Ada beberapa faktor yang harus Anda pertimbangkan ketika memilih operasi mana yang akan digunakan.

Apakah cabang tempat Anda mendapatkan perubahan dari dibagikan dengan pengembang lain di luar tim Anda (mis. Open source, publik)?

Jika demikian, jangan rebase. Rebase menghancurkan cabang dan pengembang-pengembang itu akan merusak / repositori yang tidak konsisten kecuali jika mereka gunakan git pull --rebase. Ini adalah cara yang baik untuk membuat marah pengembang lain dengan cepat.

Seberapa terampil tim pengembangan Anda?

Rebase adalah operasi yang merusak. Itu berarti, jika Anda tidak menerapkannya dengan benar, Anda dapat kehilangan pekerjaan yang dilakukan dan / atau merusak konsistensi repositori pengembang lainnya.

Saya telah bekerja pada tim-tim tempat semua pengembang berasal dari masa ketika perusahaan dapat membeli staf yang berdedikasi untuk menangani percabangan dan penggabungan. Pengembang itu tidak tahu banyak tentang Git dan tidak ingin tahu banyak. Dalam tim ini saya tidak akan mengambil risiko merekomendasikan rebiling dengan alasan apa pun.

Apakah cabang itu sendiri mewakili informasi yang bermanfaat

Beberapa tim menggunakan model branch-per-feature di mana setiap cabang mewakili fitur (atau perbaikan bug, atau sub-fitur, dll.) Dalam model ini cabang membantu mengidentifikasi set komit terkait. Sebagai contoh, seseorang dapat dengan cepat mengembalikan fitur dengan mengembalikan gabungan cabang itu (agar adil, ini adalah operasi yang jarang terjadi). Atau perbaiki fitur dengan membandingkan dua cabang (lebih umum). Rebase akan menghancurkan cabang dan ini tidak akan langsung.

Saya juga bekerja pada tim yang menggunakan model branch-per-developer (kita semua pernah ke sana). Dalam hal ini cabang itu sendiri tidak menyampaikan informasi tambahan (komit sudah memiliki penulis). Tidak akan ada salahnya rebasing.

Mungkinkah Anda ingin mengembalikan penggabungan dengan alasan apa pun?

Mengembalikan (seperti dalam kehancuran) suatu rebase adalah sangat sulit dan / atau tidak mungkin (jika rebase memiliki konflik) dibandingkan dengan mengembalikan suatu penggabungan. Jika Anda berpikir ada kemungkinan Anda ingin kembali, gunakan penggabungan.

Apakah Anda bekerja dalam tim? Jika demikian, apakah Anda bersedia mengambil pendekatan apa pun atau tidak sama sekali pada cabang ini?

Operasi rebase perlu ditarik dengan yang sesuai git pull --rebase. Jika Anda bekerja sendiri, Anda mungkin dapat mengingat mana yang harus Anda gunakan pada waktu yang tepat. Jika Anda bekerja dalam tim, ini akan sangat sulit untuk dikoordinasikan. Inilah sebabnya mengapa sebagian besar alur kerja rebase merekomendasikan penggunaan rebase untuk semua penggabungan (dan git pull --rebaseuntuk semua tarikan).

Mitos umum

Gabung menghancurkan sejarah (squash commit)

Dengan asumsi Anda memiliki gabungan berikut:

    B -- C
   /      \
  A--------D

Beberapa orang akan menyatakan bahwa penggabungan "menghancurkan" sejarah komit karena jika Anda melihat log hanya cabang master (A - D) Anda akan kehilangan pesan komit penting yang terkandung dalam B dan C.

Jika ini benar, kami tidak akan memiliki pertanyaan seperti ini . Pada dasarnya, Anda akan melihat B dan C kecuali jika Anda secara eksplisit meminta untuk tidak melihatnya (menggunakan --first-parent). Ini sangat mudah untuk dicoba sendiri.

Rebase memungkinkan penggabungan yang lebih aman / sederhana

Kedua pendekatan bergabung secara berbeda, tetapi tidak jelas bahwa satu selalu lebih baik daripada yang lain dan mungkin tergantung pada alur kerja pengembang. Misalnya, jika pengembang cenderung melakukan komitmen secara rutin (mis. Mungkin mereka berkomitmen dua kali sehari saat mereka beralih dari kantor ke rumah) maka mungkin ada banyak komitmen untuk cabang tertentu. Banyak dari komitmen tersebut mungkin tidak terlihat seperti produk akhir (saya cenderung memperbaiki pendekatan saya satu atau dua kali per fitur). Jika orang lain sedang mengerjakan bidang kode yang terkait dan mereka mencoba untuk mengubah perubahan saya itu bisa menjadi operasi yang cukup membosankan.

Rebase lebih keren / seksi / lebih profesional

Jika Anda ingin alias rmuntuk rm -rf"menghemat waktu" maka mungkin rebase adalah untuk Anda.

Dua Senanku

Saya selalu berpikir bahwa suatu hari nanti saya akan menemukan skenario di mana Git rebase adalah alat yang luar biasa yang memecahkan masalah. Sama seperti saya pikir saya akan menemukan skenario di mana Git reflog adalah alat yang luar biasa yang memecahkan masalah saya. Saya telah bekerja dengan Git selama lebih dari lima tahun sekarang. Itu belum terjadi.

Sejarah berantakan tidak pernah benar-benar menjadi masalah bagi saya. Saya tidak pernah hanya membaca sejarah komit saya seperti novel yang menarik. Sebagian besar waktu saya membutuhkan sejarah saya akan menggunakan menyalahkan Git atau membagi dua Git. Dalam hal itu, memiliki komit gabungan benar-benar berguna bagi saya, karena jika gabungan tersebut memperkenalkan masalah, itu adalah informasi yang berarti bagi saya.

Pembaruan (4/2017)

Saya merasa berkewajiban untuk menyebutkan bahwa saya secara pribadi telah melunak menggunakan rebase meskipun saran umum saya masih berlaku. Saya baru-baru ini banyak berinteraksi dengan proyek Angular 2 Material . Mereka telah menggunakan rebase untuk menjaga riwayat commit yang sangat bersih. Ini memungkinkan saya untuk dengan mudah melihat komit mana yang memperbaiki cacat yang diberikan dan apakah komit itu termasuk dalam rilis. Ini berfungsi sebagai contoh yang bagus untuk menggunakan rebase dengan benar.

Kecepatan
sumber
5
Seharusnya jawaban yang divalidasi.
Mik378
Ini tentu jawaban terbaik. Terutama dengan komentar yang diklarifikasi di dalam pembaruan terbaru. Ini membantu menjaga riwayat git tetap bersih dan jelas, tetapi gunakan dengan aman.
zquintana
5
Saya lebih suka jawaban ini. Tetapi: Rebase tidak membuat sejarah "bersih". Itu membuat sejarah yang lebih linier, tapi itu sama sekali bukan hal yang sama, karena siapa yang tahu sekarang banyak "kotoran" yang dilakukan oleh setiap commit? Sejarah Git yang paling bersih dan paling jelas adalah yang menjaga cabang dan melakukan integritas.
Marnen Laibow-Koser
3
"Mitos umum, Anda melihat komit B dan C": Tidak harus !! Anda sebenarnya hanya melihat B dan C jika penggabungan tersebut merupakan penggabungan maju cepat dan itu hanya mungkin, jika tidak ada konflik. Jika ada konflik, Anda mendapat satu komit! Namun: Anda dapat menggabungkan master ke dalam fitur terlebih dahulu dan menyelesaikan konflik di sana dan jika Anda kemudian menggabungkan fitur menjadi master, Anda mendapatkan komit B dan C, dan satu komit X dari gabungan (pertama) dari master menjadi fitur dalam riwayat Anda.
Jeremy Benks
penjelasan yang sangat bagus! Itu harus lebih ditingkatkan!
dimmits
185

Banyak jawaban di sini mengatakan bahwa menggabungkan mengubah semua komit Anda menjadi satu, dan karenanya menyarankan untuk menggunakan rebase untuk mempertahankan komit Anda. Ini salah. Dan ide yang buruk jika Anda sudah mendorong komitmen Anda .

Gabung tidak melenyapkan komitmen Anda. Gabung menjaga sejarah! (lihat saja gitk) Rebase riwayat penulisan ulang, yang merupakan Hal Buruk setelah Anda mendorongnya .

Gunakan penggabungan - jangan rebase kapan pun Anda sudah mendorong.

Ini Linus (penulis Git) mengambil alih (sekarang di-host di blog saya sendiri, seperti yang dipulihkan oleh Mesin Wayback ). Ini bacaan yang sangat bagus.

Atau Anda dapat membaca versi saya sendiri dari ide yang sama di bawah ini.

Rebasing cabang pada master:

  • memberikan gagasan yang salah tentang bagaimana komit dibuat
  • master polusi dengan sekelompok komitmen menengah yang mungkin belum diuji dengan baik
  • sebenarnya bisa memperkenalkan jeda build pada komitmen perantara ini karena perubahan yang dibuat untuk dikuasai antara ketika cabang topik asli dibuat dan ketika itu direstrukturisasi.
  • membuat sulit menemukan tempat yang bagus di master untuk checkout.
  • Menyebabkan stempel waktu pada komitmen untuk tidak sejajar dengan urutan kronologisnya di pohon. Jadi, Anda akan melihat bahwa komit mendahului komit B di master, tetapi komit B adalah yang ditulis terlebih dahulu. (Apa?!)
  • Menghasilkan lebih banyak konflik, karena komitmen individu dalam cabang topik dapat masing-masing melibatkan gabungan konflik yang harus diselesaikan secara individual (lebih jauh terletak pada sejarah tentang apa yang terjadi di setiap komit).
  • adalah penulisan ulang sejarah. Jika cabang yang diremajakan telah didorong ke mana saja (dibagikan dengan orang lain selain diri Anda) maka Anda telah mengacaukan semua orang yang memiliki cabang itu sejak Anda menulis ulang sejarah.

Sebaliknya, menggabungkan cabang topik menjadi master:

  • mempertahankan sejarah di mana cabang topik dibuat, termasuk penggabungan dari master ke cabang topik untuk membantu mempertahankannya saat ini. Anda benar-benar mendapatkan ide yang akurat tentang kode apa yang sedang dikerjakan pengembang ketika mereka membangun.
  • master adalah cabang yang sebagian besar terdiri dari gabungan, dan masing-masing dari gabungan tersebut biasanya merupakan 'poin bagus' dalam sejarah yang aman untuk diperiksa, karena di situlah cabang topik siap untuk diintegrasikan.
  • semua komitmen individu dari cabang topik dilestarikan, termasuk fakta bahwa mereka berada di cabang topik, sehingga mengisolasi perubahan-perubahan itu adalah alami dan Anda dapat menelusuri jika diperlukan.
  • menggabungkan konflik hanya harus diselesaikan sekali (pada titik penggabungan), sehingga perubahan komit menengah yang dibuat dalam cabang topik tidak harus diselesaikan secara independen.
  • dapat dilakukan beberapa kali dengan lancar. Jika Anda mengintegrasikan cabang topik Anda untuk dikuasai secara berkala, orang-orang dapat terus membangun di cabang topik, dan itu bisa terus digabung secara independen.
Andrew Arnott
sumber
3
Juga, git merge memiliki opsi "--no-ff" (tanpa fast-forward) yang memungkinkan Anda untuk mengembalikan semua perubahan yang diperkenalkan oleh gabungan tertentu dengan sangat mudah.
Tiago
3
Hanya membuatnya lebih jelas: Anda merujuk pada situasi 'setiap kali Anda sudah mendorong' - ini harus berani. Tautan ke posting Linus bagus, btw., Memperjelasnya.
honzajde
2
tetapi bukankah itu praktik terbaik untuk "memperbarui" dari master ke cabang topik Anda, sebelum Anda menggabungkan cabang topik menjadi master melalui PR (untuk menyelesaikan konflik di cabang Anda, bukan master)? Kami melakukannya seperti itu sehingga sebagian besar cabang topik memiliki komit terakhir "menggabungkan cabang master ke dalam topik -..." tetapi di sini ini terdaftar sebagai "fitur" rebasing dan tidak ada yang menyebutkannya untuk menggabungkan ...?
ProblemsOfSumit
2
@AndrewArnott "Sebagian besar cabang topik harus dapat bergabung tanpa konflik ke cabang target mereka" Bagaimana seharusnya itu mungkin ketika 20 dev sedang mengerjakan 30 cabang? Akan ada penggabungan saat Anda mengerjakan milik Anda - jadi tentu saja Anda harus memperbarui cabang topik dari target sebelum membuat PR ... tidak?
ProblemsOfSumit
3
Tidak biasanya, @Sumit. Git dapat menggabungkan kedua arah dengan baik meskipun perubahan telah dilakukan pada salah satu atau kedua cabang. Hanya ketika baris kode yang sama (atau sangat dekat) dimodifikasi di dua cabang yang akan Anda dapatkan konflik. Jika itu sering terjadi pada tim mana pun, tim harus memikirkan kembali bagaimana mereka mendistribusikan pekerjaan karena menyelesaikan konflik adalah pajak dan memperlambatnya.
Andrew Arnott
76

TLDR: Itu tergantung pada apa yang paling penting - sejarah yang rapi atau representasi sebenarnya dari urutan pembangunan

Jika riwayat rapi adalah yang paling penting, maka Anda akan rebase pertama dan kemudian menggabungkan perubahan Anda, jadi jelas apa kode baru itu. Jika Anda telah mendorong cabang Anda, jangan rebase kecuali Anda dapat mengatasi konsekuensinya.

Jika representasi urutan yang benar adalah yang paling penting, Anda akan bergabung tanpa rebasing.

Menggabungkan berarti: Membuat satu komit baru yang menggabungkan perubahan saya ke tujuan. Catatan: Komit baru ini akan memiliki dua orang tua - komit terbaru dari serangkaian komit Anda dan komit terbaru dari cabang lain yang Anda gabungkan.

Rebase berarti: Buat serangkaian komit baru, menggunakan set komit saya saat ini sebagai petunjuk. Dengan kata lain, hitung seperti apa perubahan saya seandainya saya mulai membuatnya dari saat saya melanjutkan. Karena itu, setelah rebase, Anda mungkin perlu menguji kembali perubahan Anda dan selama rebase, Anda mungkin akan mengalami beberapa konflik.

Mengingat ini, mengapa Anda akan rebase? Hanya untuk menjaga sejarah perkembangan tetap jelas. Katakanlah Anda sedang mengerjakan fitur X dan ketika Anda selesai, Anda menggabungkan perubahan Anda. Tujuan sekarang akan memiliki satu komit yang akan mengatakan sesuatu di sepanjang baris "Fitur tambahan X". Sekarang, alih-alih penggabungan, jika Anda diubah kembali dan kemudian digabungkan, riwayat pengembangan tujuan akan berisi semua komitmen individu dalam satu perkembangan logis tunggal. Ini membuat meninjau perubahan nanti lebih mudah. Bayangkan betapa sulitnya Anda untuk meninjau riwayat pengembangan jika 50 pengembang menggabungkan berbagai fitur setiap saat.

Yang mengatakan, jika Anda sudah mendorong cabang yang Anda kerjakan di hulu, Anda tidak harus rebase, tetapi gabungkan Untuk cabang yang belum didorong ke hulu, rebase, uji dan gabungkan.

Waktu lain Anda mungkin ingin melakukan rebase adalah ketika Anda ingin menghilangkan komitmen dari cabang Anda sebelum mendorong hulu. Sebagai contoh: Komit yang memperkenalkan beberapa kode debug sejak awal dan komit lainnya lebih lanjut pada yang membersihkan kode itu. Satu-satunya cara untuk melakukan ini adalah dengan melakukan rebase interaktif:git rebase -i <branch/commit/tag>

UPDATE: Anda juga ingin menggunakan rebase ketika Anda menggunakan Git untuk antarmuka ke sistem kontrol versi yang tidak mendukung sejarah non-linear ( Subversion misalnya). Saat menggunakan jembatan git-svn, sangat penting bahwa perubahan yang Anda gabungkan kembali ke Subversion adalah daftar perubahan yang berurutan di atas perubahan terbaru dalam trunk. Hanya ada dua cara untuk melakukan itu: (1) Membuat ulang perubahan secara manual dan (2) Menggunakan perintah rebase, yang jauh lebih cepat.

UPDATE 2: Salah satu cara tambahan untuk memikirkan rebase adalah bahwa hal itu memungkinkan semacam pemetaan dari gaya pengembangan Anda ke gaya yang diterima dalam repositori yang Anda komit. Katakanlah Anda suka melakukan dalam potongan kecil, kecil. Anda memiliki satu komit untuk memperbaiki kesalahan ketik, satu komit untuk menyingkirkan kode yang tidak digunakan dan sebagainya. Pada saat Anda menyelesaikan apa yang perlu Anda lakukan, Anda memiliki serangkaian komitmen yang panjang. Sekarang katakanlah repositori yang Anda komit untuk mendorong komit besar, jadi untuk pekerjaan yang Anda lakukan, orang akan mengharapkan satu atau mungkin dua komit. Bagaimana Anda mengambil komitmen Anda dan mengompres mereka ke apa yang diharapkan? Anda akan menggunakan rebase interaktif dan remas komit kecil Anda menjadi potongan yang lebih sedikit. Hal yang sama berlaku jika kebalikannya diperlukan - jika gaya Anda adalah beberapa komit besar, tetapi repositori menuntut serangkaian komitmen kecil. Anda akan menggunakan rebase untuk melakukannya juga. Jika Anda telah bergabung sebagai gantinya, Anda sekarang telah mencangkokkan gaya komit Anda ke repositori utama. Jika ada banyak pengembang, Anda bisa membayangkan betapa sulitnya mengikuti sejarah dengan beberapa gaya komit yang berbeda setelah beberapa waktu.

UPDATE 3: Does one still need to merge after a successful rebase?Ya, benar. Alasannya adalah bahwa rebase pada dasarnya melibatkan "pengalihan" komitmen. Seperti yang saya katakan di atas, komit-komit ini dihitung, tetapi jika Anda memiliki 14 komit dari titik percabangan, maka dengan asumsi tidak ada yang salah dengan rebase Anda, Anda akan 14 komit di depan (dari poin yang Anda rebound) setelah rebase dilakukan. Anda memiliki cabang sebelum rebase. Anda akan memiliki cabang dengan panjang yang sama setelahnya. Anda masih perlu menggabungkan sebelum mempublikasikan perubahan Anda. Dengan kata lain, rebase sebanyak yang Anda inginkan (sekali lagi, hanya jika Anda belum mendorong perubahan Anda ke atas). Gabungkan hanya setelah Anda rebase.

Carl
sumber
1
Penggabungan dengan master dapat menghasilkan fast forward. Di cabang fitur mungkin ada beberapa komit, yang memiliki bug kecil atau bahkan tidak dikompilasi. Jika Anda hanya melakukan pengujian unit di cabang fitur, beberapa kesalahan dalam integrasi saya lolos. Sebelum bergabung dengan master, diperlukan tes integrasi dan dapat menunjukkan beberapa bug. Jika ini diperbaiki, fitur dapat diintegrasikan. Karena Anda tidak ingin melakukan kode buggy untuk dikuasai, rebase tampaknya perlu untuk mencegah all-commit-fast-forward.
mbx
1
@ mbx git mergemendukung --no-ffopsi yang memaksanya untuk membuat komit gabungan.
Gavin S. Yancey
63

Meskipun penggabungan jelas merupakan cara termudah dan paling umum untuk mengintegrasikan perubahan, itu bukan satu-satunya: Rebase adalah cara alternatif integrasi.

Memahami Gabung Sedikit Lebih Baik

Ketika Git melakukan penggabungan, Git mencari tiga komit:

  • (1) Nenek moyang yang sama berkomitmen. Jika Anda mengikuti sejarah dua cabang dalam suatu proyek, mereka selalu memiliki setidaknya satu kesamaan: pada titik waktu ini, kedua cabang memiliki konten yang sama dan kemudian berkembang secara berbeda.
  • (2) + (3) Titik akhir dari setiap cabang. Tujuan dari integrasi adalah untuk menggabungkan keadaan saat ini dari dua cabang. Oleh karena itu, revisi terbaru masing-masing adalah yang menarik. Menggabungkan ketiga komitmen ini akan menghasilkan integrasi yang kami tuju.

Komitmen Maju Cepat atau Gabung

Dalam kasus yang sangat sederhana, salah satu dari dua cabang tidak memiliki komitmen baru sejak percabangan terjadi - komit terbaru masih merupakan leluhur bersama.

Masukkan deskripsi gambar di sini

Dalam hal ini, melakukan integrasi itu benar-benar sederhana: Git bisa menambahkan semua komit dari cabang lain di atas komit leluhur yang sama. Di Git, bentuk integrasi paling sederhana ini disebut gabungan "maju cepat". Kedua cabang kemudian berbagi sejarah yang sama persis.

Masukkan deskripsi gambar di sini

Namun, dalam banyak kasus, kedua cabang bergerak maju secara individual.

Masukkan deskripsi gambar di sini

Untuk membuat integrasi, Git harus membuat komit baru yang berisi perbedaan di antara mereka - komit gabungan.

Masukkan deskripsi gambar di sini

Komitmen Manusia & Komitmen Gabung

Biasanya, komit dibuat dengan hati-hati oleh manusia. Ini adalah unit yang bermakna yang hanya membungkus perubahan terkait dan membubuhi keterangan dengan komentar.

Komitmen gabungan sedikit berbeda: alih-alih dibuat oleh pengembang, itu dibuat secara otomatis oleh Git. Dan alih-alih membungkus satu set perubahan terkait, tujuannya adalah untuk menghubungkan dua cabang, seperti simpul. Jika Anda ingin memahami operasi gabungan nanti, Anda perlu melihat sejarah kedua cabang dan grafik komit yang sesuai.

Integrasi dengan Rebase

Beberapa orang memilih untuk pergi tanpa komitmen penggabungan otomatis tersebut. Sebaliknya, mereka ingin sejarah proyek terlihat seolah-olah telah berkembang dalam satu garis lurus. Tidak ada indikasi yang tersisa bahwa itu telah dipecah menjadi beberapa cabang di beberapa titik.

Masukkan deskripsi gambar di sini

Mari kita berjalan melalui operasi rebase langkah demi langkah. Skenarionya sama seperti pada contoh sebelumnya: kami ingin mengintegrasikan perubahan dari cabang-B ke cabang-A, tetapi sekarang dengan menggunakan rebase.

Masukkan deskripsi gambar di sini

Kami akan melakukan ini dalam tiga langkah

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

Pertama, Git akan "membatalkan" semua komit pada cabang-A yang terjadi setelah garis mulai bercabang (setelah leluhur bersama melakukan). Namun, tentu saja, itu tidak akan mengabaikan mereka: Anda dapat menganggap komit tersebut sebagai "disimpan sementara".

Masukkan deskripsi gambar di sini

Selanjutnya, ini menerapkan komit dari cabang-B yang ingin kita integrasikan. Pada titik ini, kedua cabang terlihat persis sama.

Masukkan deskripsi gambar di sini

Pada langkah terakhir, komit baru pada cabang-A sekarang diterapkan kembali - tetapi pada posisi baru, di atas komit terintegrasi dari cabang-B (didasarkan kembali).

Hasilnya terlihat seperti perkembangan yang terjadi dalam garis lurus. Alih-alih komit gabungan yang berisi semua perubahan gabungan, struktur komit asli dipertahankan.

Masukkan deskripsi gambar di sini

Akhirnya, Anda mendapatkan cabang cabang-A yang bersih tanpa komitmen yang tidak diinginkan dan dihasilkan secara otomatis.

Catatan: Diambil dari pos luar biasa oleh git-tower. The kelemahan dari rebaseini juga baik dibaca dalam posting yang sama.

Abdullah Khan
sumber
+1 untuk diagram yang sangat keren. Saya selalu ingin dapat menggambarkan contoh git flow dengan cara yang sama tanpa keberuntungan.
Mikayil Abdullayev
60

Sebelum bergabung / rebase:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

Setelah git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

Setelah git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E dan F adalah komitmen)

Contoh ini dan banyak informasi lainnya yang diilustrasikan dengan baik tentang Git dapat ditemukan di Git The Basics Tutorial .

sikat gigi
sumber
30

Kalimat ini menerimanya:

Secara umum, cara untuk mendapatkan yang terbaik dari kedua dunia adalah dengan rebase perubahan lokal yang telah Anda buat, tetapi belum dibagikan, sebelum Anda mendorongnya untuk membersihkan cerita Anda, tetapi tidak pernah rebase apa pun yang Anda mendorong di suatu tempat .

Sumber: 3.6 Git Branching - Rebasing, Rebase vs Merge

Joaquin Sargiotto
sumber
25

Jawaban ini berorientasi luas di sekitar Git Flow . Tabel-tabel telah dihasilkan dengan Generator Tabel ASCII yang bagus , dan pohon-pohon sejarah dengan perintah yang luar biasa ini ( alias sebagai git lg):

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

Tabel dalam urutan kronologis terbalik agar lebih konsisten dengan pohon sejarah. Lihat juga perbedaan antara git mergedan git merge --no-ffpertama (Anda biasanya ingin menggunakan git merge --no-ffkarena membuat sejarah Anda terlihat lebih dekat dengan kenyataan):

git merge

Perintah:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

Perintah:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge vs. git rebase

Poin pertama: selalu menggabungkan fitur ke dalam pengembangan, tidak pernah rebase mengembangkan dari fitur . Ini adalah konsekuensi dari Aturan Emas Rebasing :

Aturan emas git rebaseadalah untuk tidak pernah menggunakannya di cabang publik .

Dengan kata lain :

Jangan pernah rebase apapun yang Anda dorong ke suatu tempat.

Saya pribadi akan menambahkan: kecuali itu cabang fitur DAN Anda dan tim Anda sadar akan konsekuensinya .

Jadi pertanyaan git mergevs git rebaseberlaku hampir hanya untuk cabang fitur (dalam contoh berikut, --no-ffselalu digunakan saat penggabungan). Perhatikan bahwa karena saya tidak yakin ada satu solusi yang lebih baik ( ada perdebatan ), saya hanya akan memberikan bagaimana kedua perintah berperilaku. Dalam kasus saya, saya lebih suka menggunakan git rebasekarena menghasilkan pohon sejarah yang lebih bagus :)

Antara cabang fitur

git merge

Perintah:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Perintah:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Dari developke cabang fitur

git merge

Perintah:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Perintah:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Catatan samping

git cherry-pick

Ketika Anda hanya perlu satu komit tertentu, git cherry-pickadalah solusi yang bagus ( -xopsi menambahkan baris yang mengatakan " (cherry pick from komit ...) " ke badan pesan komit asli, jadi biasanya ide yang baik untuk menggunakannya - git log <commit_sha1>untuk melihat Itu):

Perintah:

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Hasil:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Saya tidak yakin saya bisa menjelaskannya lebih baik daripada Derek Gourlay ... Pada dasarnya, gunakan git pull --rebasealih-alih git pull:) Yang hilang dalam artikel ini adalah Anda dapat mengaktifkannya secara default :

git config --global pull.rebase true

git rerere

Sekali lagi, dijelaskan dengan baik di sini . Tapi sederhananya, jika Anda mengaktifkannya, Anda tidak perlu menyelesaikan konflik yang sama beberapa kali lagi.

sp00m
sumber
15

Buku Pro Git memiliki penjelasan yang sangat bagus di halaman rebasing .

Pada dasarnya penggabungan akan mengambil dua komit dan menggabungkannya.

Rebase akan diberikan kepada leluhur yang sama pada keduanya dan secara bertahap menerapkan perubahan di atas satu sama lain. Ini membuat 'bersih' dan sejarah yang lebih linier.

Tetapi ketika Anda rebase, Anda meninggalkan komit sebelumnya dan membuat yang baru. Jadi, Anda tidak boleh me-rebase repositori yang bersifat publik. Orang lain yang mengerjakan repositori akan membencimu.

Untuk alasan itu saja saya hampir secara eksklusif bergabung. 99% waktu cabang saya tidak jauh berbeda, jadi jika ada konflik hanya ada di satu atau dua tempat.

xero
sumber
1
Penggabungan tidak menggabungkan komit - itu akan menjadi penulisan ulang riwayat. Rebase melakukan itu.
kellyfj
4

Git rebase digunakan untuk membuat jalur percabangan dalam pembersih riwayat dan struktur repositori menjadi linier.

Ini juga digunakan untuk menjaga cabang yang dibuat oleh Anda pribadi, seperti setelah rebasing dan mendorong perubahan ke server, jika Anda menghapus cabang Anda, tidak akan ada bukti dari cabang yang Anda kerjakan. Jadi cabang Anda sekarang menjadi perhatian lokal Anda.

Setelah melakukan rebase, kami juga menyingkirkan komit ekstra yang biasa kami lihat jika kami melakukan penggabungan normal.

Dan ya, seseorang masih perlu melakukan penggabungan setelah rebase berhasil karena perintah rebase hanya menempatkan pekerjaan Anda di atas cabang yang Anda sebutkan selama rebase, katakanlah master, dan buat komit pertama dari cabang Anda sebagai keturunan langsung dari cabang master . Ini berarti kita sekarang dapat melakukan penggabungan maju untuk membawa perubahan dari cabang ini ke cabang master.

cvibha
sumber
4

Jika Anda hanya satu pengembang, Anda dapat menggunakan rebase alih-alih bergabung untuk memiliki riwayat yang jelas

masukkan deskripsi gambar di sini

yoAlex5
sumber
3

Beberapa contoh praktis, agak terkoneksi dengan pengembangan skala besar tempat Gerrit digunakan untuk meninjau dan mengintegrasikan pengiriman:

Saya menggabungkan ketika saya mengangkat cabang fitur saya ke master jarak jauh yang baru. Ini memberi kerja pengangkatan minimal dan mudah untuk mengikuti sejarah pengembangan fitur misalnya gitk .

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

Saya bergabung ketika saya menyiapkan komitmen pengiriman.

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

Saya rebase ketika komit pengiriman gagal integrasi dengan alasan apa pun, dan saya perlu memperbaruinya ke master jarak jauh yang baru.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
Martin G
sumber
3

Sudah berkali-kali dijelaskan apa itu rebase dan penggabungan apa, tetapi kapan Anda harus menggunakan apa?

Kapan Anda harus menggunakan rebase?

  • ketika Anda belum mendorong cabang / tidak ada orang lain yang mengerjakannya
  • Anda ingin riwayat lengkap
  • Anda ingin menghindari semua pesan komit "digabung .." yang dibuat secara otomatis

Saat Git rebase mengubah sejarah. Karena itu Anda tidak boleh menggunakannya saat orang lain mengerjakan cabang yang sama / jika Anda mendorongnya. Tetapi jika Anda memiliki cabang lokal, Anda bisa melakukan master rebase gabungan sebelum menggabungkan cabang Anda menjadi master untuk menjaga sejarah yang lebih bersih. Melakukan ini, setelah penggabungan ke cabang master tidak akan terlihat bahwa Anda menggunakan cabang di cabang master - sejarahnya "bersih" karena Anda tidak memiliki "gabungan" yang dibuat secara otomatis, tetapi masih memiliki riwayat lengkap di cabang master Anda tanpa melakukan "digabung .." yang dilakukan secara otomatis.

Namun pastikan, Anda menggunakan git merge feature-branch --ff-onlyuntuk memastikan tidak ada konflik membuat komit tunggal ketika Anda menggabungkan fitur Anda kembali ke utama.Ini menarik jika Anda menggunakan cabang fitur untuk setiap tugas yang Anda kerjakan saat Anda mendapatkan riwayat cabang fitur, tetapi bukan komit "digabung .."

Skenario kedua adalah, jika Anda bercabang dari cabang dan ingin tahu apa yang telah berubah di cabang utama. Rebase memberi Anda informasi karena mencakup setiap komit.

Kapan Anda harus menggunakan gabungan?

  • ketika Anda telah mendorong cabang / orang lain sedang mengerjakannya juga
  • Anda tidak perlu riwayat lengkap
  • cukup menggabungkan cukup baik untuk Anda

Ketika Anda tidak perlu atau ingin memiliki semua sejarah cabang fitur di cabang master Anda atau jika orang lain bekerja pada cabang yang sama / Anda telah mendorongnya. Jika Anda masih ingin memiliki riwayat, gabungkan saja master ke cabang fitur sebelum menggabungkan cabang fitur menjadi master. Ini akan menghasilkan penggabungan maju cepat di mana Anda memiliki riwayat cabang fitur di master Anda (termasuk komit gabungan yang ada di cabang fitur Anda karena Anda menggabungkan master ke dalamnya).

Jeremy Benks
sumber
-4

Kapan saya menggunakan git rebase? Hampir tidak pernah, karena menulis ulang sejarah. git mergehampir selalu merupakan pilihan yang lebih disukai, karena itu menghormati apa yang sebenarnya terjadi dalam proyek Anda.

Marnen Laibow-Koser
sumber
1
@benjaminhull Terima kasih! —kecuali saya berharap jawaban saya berdasarkan fakta. Pendapat IMHO memiliki sedikit tempat dalam hal semacam ini: fakta bahwa kehilangan riwayat Anda yang sebenarnya membuat hidup lebih sulit nantinya.
Marnen Laibow-Koser
1
Setuju. Penggabungan tidak akan pernah mengarah ke sejarah yang rusak, dll. (Ketika Anda mengubah komitmen yang didorong)
surfrider