Git cherry pick vs rebase

120

Saya baru saja mulai bekerja dengan Git.

Saat membaca buku Git online, saya menemukan yang berikut di bawah bagian "Git Rebase":

Dengan perintah rebase, Anda dapat mengambil semua perubahan yang dilakukan pada satu cabang dan mengulangnya di cabang lain.

(Dikutip dari: http://git-scm.com/book/en/Git-Branching-Rebasing )

Saya pikir ini adalah definisi yang tepat dari git cherry-pick (menerapkan kembali komit atau sekumpulan objek komit pada cabang yang saat ini diperiksa).

Apa perbedaan diantara keduanya?

asam lisergat
sumber

Jawaban:

166

Sejak saat git cherry-pickbelajar untuk dapat menerapkan banyak komit, perbedaannya memang menjadi agak diperdebatkan, tetapi ini adalah sesuatu yang disebut evolusi konvergen ;-)

Perbedaan sebenarnya terletak pada niat asli untuk membuat kedua alat tersebut:

  • git rebaseTugasnya adalah meneruskan serangkaian perubahan yang dimiliki pengembang dalam repositori pribadinya, yang dibuat terhadap versi X dari beberapa cabang hulu, ke versi Y dari cabang yang sama (Y> X). Ini secara efektif mengubah dasar rangkaian komit tersebut, karenanya "rebasing".

    (Ini juga memungkinkan pengembang untuk mentransplantasikan serangkaian komit ke komit sewenang-wenang, tetapi penggunaan ini kurang jelas.)

  • git cherry-pickadalah untuk membawa komitmen menarik dari satu lini pengembangan ke lini lain. Contoh klasik adalah mem-backport perbaikan keamanan yang dibuat pada cabang pengembangan yang tidak stabil ke cabang yang stabil (pemeliharaan), yang mergetidak masuk akal, karena akan membawa banyak perubahan yang tidak diinginkan.

    Sejak kemunculannya yang pertama, git cherry-picktelah dapat memilih beberapa komit sekaligus, satu per satu.

Oleh karena itu, mungkin perbedaan paling mencolok antara kedua perintah ini adalah bagaimana mereka memperlakukan cabang tempat mereka bekerja: git cherry-pickbiasanya membawa komit dari tempat lain dan menerapkannya di atas cabang Anda saat ini, merekam komit baru , sambil git rebasemengambil cabang Anda saat ini dan menulis ulang serangkaian tipnya sendiri berlaku dalam satu atau lain cara. Ya, ini adalah deskripsi yang sangat bodoh tentang apa yang git rebasebisa dilakukan, tetapi disengaja, untuk mencoba membuat gagasan umum meresap.

Perbarui untuk lebih menjelaskan contoh penggunaan yang git rebasesedang dibahas.

Mengingat situasi ini,
keadaan repo sebelum rebasing
Buku menyatakan:

Namun, ada cara lain: Anda dapat mengambil patch dari perubahan yang diperkenalkan di C3 dan menerapkannya kembali di atas C4. Di Git, ini disebut rebasing. Dengan perintah rebase, Anda dapat mengambil semua perubahan yang dilakukan pada satu cabang dan menerapkannya ke cabang lain.

Dalam contoh ini, Anda akan menjalankan yang berikut ini:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

"Tangkapan" di sini adalah bahwa dalam contoh ini, cabang "eksperimen" (subjek untuk rebasing) pada awalnya bercabang dari cabang "master", dan karenanya berbagi commit C0 hingga C2 dengannya - secara efektif, "eksperimen" adalah " master "hingga, dan termasuk, C2 plus commit C3 di atasnya. (Ini adalah kasus yang paling sederhana; tentu saja, "eksperimen" dapat berisi beberapa lusinan commit di atas basis aslinya.)

Sekarang git rebasediperintahkan untuk rebase "percobaan" ke saat ujung "master", dan git rebaseberjalan seperti ini:

  1. Berjalan git merge-baseuntuk melihat komit terakhir yang dibagi oleh "eksperimen" dan "master" (apa tujuan pengalihan, dengan kata lain). Ini C2.
  2. Menghemat semua komitmen yang dibuat sejak titik pengalihan; dalam contoh mainan kami, hanya C3.
  3. Mundurkan HEAD (yang menunjuk ke ujung komit dari "eksperimen" sebelum operasi mulai dijalankan) untuk menunjuk ke ujung "master" - kami melakukan rebasing ke atasnya.
  4. Mencoba untuk menerapkan setiap komit yang disimpan (seolah-olah dengan git apply) secara berurutan. Dalam contoh mainan kami itu hanya satu komit, C3. Katakanlah aplikasinya akan menghasilkan komit C3 '.
  5. Jika semua berjalan dengan baik, referensi "percobaan" diperbarui untuk menunjuk ke komit yang dihasilkan dari penerapan komit tersimpan terakhir (C3 'dalam kasus kami).

Sekarang kembali ke pertanyaan Anda. Seperti yang Anda lihat, di sini secara teknis git rebase memang mentransplantasikan serangkaian komit dari "eksperimen" ke ujung "master", sehingga Anda berhak mengetahui bahwa memang ada "cabang lain" dalam proses tersebut. Tapi intinya adalah bahwa tip komit dari "eksperimen" akhirnya menjadi komit tip baru dalam "eksperimen", itu baru saja mengubah dasarnya:
keadaan setelah penggabungan

Sekali lagi, secara teknis Anda dapat mengatakan bahwa di git rebasesini menggabungkan komit tertentu dari "master", dan ini sepenuhnya benar.

kostix.dll
sumber
2
Terima kasih. Saya masih belum sepenuhnya memahami apa yang Anda maksud di sini. Dalam buku, contoh diberikan bahwa rebase menerapkan serangkaian komitmen tip dari cabang lain, sementara Anda mengatakan itu dari "cabang yang sama". Atau mungkin ada beberapa kasus bagaimana cara kerjanya?
lysergic-acid
1
Mencoba menjelaskan masalah tersebut dengan memperbarui jawaban saya.
kostix
98

Dengan cherry-pick, komit / cabang asli tetap ada dan komit baru dibuat. Dengan rebase, seluruh cabang dipindahkan dengan cabang menunjuk ke komit yang diputar ulang.

Katakanlah Anda memulai dengan:

      A---B---C topic
     /
D---E---F---G master

Rebase:

$ git rebase master topic

Anda mendapatkan:

              A'--B'--C' topic
             /
D---E---F---G master

Pilih ceri:

$ git checkout master -b topic_new
$ git cherry-pick A^..C

Anda mendapatkan:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new

untuk info lebih lanjut tentang git buku ini memiliki sebagian besar (http://git-scm.com/book)

Kenny Ho
sumber
3
Jawabannya bagus. Ini juga umum bahwa Anda mungkin ingin memilih ceri hanya A dan B komit tetapi membiarkan C menggantung dalam kasus-kasus itu Anda mungkin ingin mempertahankan cabang dan hanya memilih perubahan yang mungkin perlu dilihat rekan kerja. Git dibuat untuk bekerja dengan orang-orang jadi jika Anda tidak melihat manfaat dari sesuatu saat bekerja sendiri, ini sering kali lebih umum digunakan saat bekerja dalam kelompok yang lebih besar.
Pablo Jomer
Jika rebase interaktif dilakukan sebagai gantinya, meninggalkan satu atau lebih komit, cabang apa yang akan Anda miliki di akhir? jika hanya di- topicrebased di atas master, itu tidak berisi komit yang ditinggalkan jadi di cabang apa mereka akan menjadi bagian?
Anthony
Hanya satu hal lagi yang ingin saya tambahkan: jika Anda git checkout topicdan kemudian git reset --hard C'setelah pemetikan ceri, maka Anda memiliki hasil yang sama seperti setelah rebasing. Saya menyelamatkan diri dari banyak konflik penggabungan menggunakan cherry picking over rebasing, karena nenek moyang yang sama sudah jauh.
sorrymissjackson
@anthony - stackoverflow.com/questions/11835948/… : sejauh yang saya mengerti, mereka hilang. Saya no git-guru tapi ini rebase/ cherry-pickpada semua detail dengan gityang saya punya masalah pemahaman.
thoni56
1
Grafik Anda lebih merugikan daripada menguntungkan, karena secara fungsional identik. Satu-satunya perbedaan adalah cabang yang dibuat oleh git checkout -b, yang tidak ada hubungannya dengan git cherry-pick. Cara yang lebih baik untuk menjelaskan apa yang ingin Anda katakan adalah "Anda lari git rebasedi topiccabang dan menyebarkannya master; Anda menjalankan git cherry-pickdi mastercabang dan meneruskannya (komit dari) topic. "
Rory O'Kane
14

Memetik ceri bekerja untuk komitmen individu .

Saat Anda melakukan rebasing, ini berlaku semua komit dalam riwayat ke HEAD cabang yang hilang di sana.

iltempo
sumber
Terima kasih. Apakah Anda tahu jika ini beroperasi sama di bawah selimut? (simpan output perantara mereka ke file "patch", dll).
asam lisergat
Afaik ya. Ini menerapkan semua tambalan satu per satu. Itulah alasan mengapa terkadang Anda harus menyelesaikan konflik gabungan di tengah rebase sebelum melanjutkan.
iltempo
6
@iltempo, ini bekerja untuk komit individu hanya di versi Git yang lebih lama; saat ini Anda dapat melakukan sesuatu seperti git cherry-pick foo~3..foodan mendapatkan komitmen puncak pohon dari "foo" yang dipilih satu per satu.
kostix
1
git-rebase menggunakan api yang sama seperti yang dilakukan pemetikan ceri di basis kode, iirc
alternatif
Saya tidak berpikir mereka benar-benar bekerja sama di bawah selimut. Saya telah mencoba melakukan rebasing ribuan komit dan menurut saya git membuat file kotak surat yang sangat besar dan kemudian menjalankannya git am. Sedangkan cherry pick menerapkan commit by commit (mungkin dengan membuat kotak pesan pesan tunggal untuk setiap patch). Rebase saya gagal karena file kotak surat yang dibuatnya kehabisan ruang di drive, tetapi cherry-pick dengan rentang revisi yang sama berhasil (dan tampaknya berjalan lebih cepat).
hanya tidak ada
11

Jawaban singkatnya:

  • git cherry-pick lebih "level rendah"
  • Dengan demikian, ini dapat meniru git rebase

Jawaban yang diberikan di atas bagus, saya hanya ingin memberi contoh dalam upaya menunjukkan keterkaitannya.

Tidak disarankan untuk mengganti "git rebase" dengan urutan tindakan ini, ini hanya "bukti konsep" yang, saya harap, membantu untuk memahami cara kerja sesuatu.

Diberikan repositori mainan berikut:

$ git log --graph --decorate --all --oneline
* 558be99 (test_branch_1) Test commit #7
* 21883bb Test commit #6
| * 7254931 (HEAD -> master) Test commit #5
| * 79fd6cb Test commit #4
| * 48c9b78 Test commit #3
| * da8a50f Test commit #2
|/
* f2fa606 Test commit #1

Katakanlah, kami memiliki beberapa perubahan yang sangat penting (komit # 2 hingga # 5) di master yang ingin kami sertakan ke dalam test_branch_1 kami. Biasanya kita hanya berpindah ke cabang dan melakukan "git rebase master". Tapi karena kami berpura-pura kami hanya dilengkapi dengan "git cherry-pick", kami melakukan:

$ git checkout 7254931                # Switch to master (7254931 <-- master <-- HEAD)
$ git cherry-pick 21883bb^..558be99   # Apply a range of commits (first commit is included, hence "^")    

Setelah semua operasi ini, grafik komit kami akan terlihat seperti ini:

* dd0d3b4 (HEAD) Test commit #7
* 8ccc132 Test commit #6
* 7254931 (master) Test commit #5
* 79fd6cb Test commit #4
* 48c9b78 Test commit #3
* da8a50f Test commit #2
| * 558be99 (test_branch_1) Test commit #7
| * 21883bb Test commit #6
|/
* f2fa606 Test commit #1

Seperti yang bisa kita lihat, komit # 6 dan # 7 diterapkan terhadap 7254931 (komit tip dari master). HEAD dipindahkan dan menunjukkan komit yang, pada dasarnya, ujung dari cabang rebased. Sekarang yang perlu kita lakukan adalah menghapus pointer cabang lama dan membuat yang baru:

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4

test_branch_1 sekarang di-root dari posisi master terbaru. Selesai!

raiks
sumber
Tapi rebase juga bisa mensimulasikan git cherry-pick?
Nomor 945
Karena cherry-pickdapat menerapkan berbagai komitmen, saya rasa, ya. Meskipun ini adalah cara yang agak aneh dalam melakukan sesuatu, tidak ada yang menghalangi Anda untuk memilih semua komitmen di cabang fitur Anda di atasnya master, kemudian hapus cabang fitur dan buat kembali sedemikian rupa sehingga mengarah ke ujung master. Anda dapat menganggapnya git rebasesebagai urutan git cherry-pick feature_branch, git branch -d feature_branchdan git branch feature_branch master.
raik
7

Keduanya adalah perintah untuk menulis ulang komit dari satu cabang di atas yang lain: perbedaannya adalah di cabang mana - "milikmu" (yang saat ini diperiksa HEAD) atau "milik mereka" (cabang yang diteruskan sebagai argumen ke perintah) - adalah yang dasar untuk menulis ulang ini.

git rebasemengambil komit awal dan mengulang komit Anda setelah komit awal mereka (komit awal).

git cherry-pickmengambil satu set komit dan mengulang komit mereka setelah komit Anda ( milik Anda HEAD).

Dengan kata lain, kedua perintah tersebut, dalam perilaku intinya (mengabaikan karakteristik kinerja yang berbeda, konvensi pemanggilan, dan opsi peningkatan), simetris : memeriksa cabang bardan menjalankan git rebase fooset barcabang ke riwayat yang sama seperti memeriksa cabang foodan menjalankan git cherry-pick ..barakan ditetapkan foomenjadi (perubahan dari foo, diikuti oleh perubahan dari bar).

Dari segi penamaan, perbedaan antara dua perintah dapat diingat karena masing-masing menjelaskan apa yang dilakukannya pada cabang saat ini : rebasemenjadikan kepala yang lain sebagai basis baru untuk perubahan Anda, sedangkan cherry-pickmengambil perubahan dari cabang lain dan meletakkannya di atas AndaHEAD (seperti ceri di atas sundae).

Stuart P. Bentley
sumber
1
Saya tidak dapat memahami jawaban apa pun kecuali jawaban Anda! Ini ringkas dan masuk akal tanpa kata-kata yang berlebihan.
neoxic
4

Keduanya melakukan hal yang sangat mirip; Perbedaan konseptual utamanya adalah (dalam istilah yang disederhanakan) bahwa:

  • rebase memindahkan komit dari cabang saat ini ke cabang lain .

  • salinan cherry-pick melakukan dari cabang lain ke cabang saat ini .

Menggunakan diagram yang mirip dengan jawaban @Ken Ho :

Diberikan keadaan awal ini:

A---B---C---D master
     \
      E---F---G topic

... dan dengan asumsi bahwa Anda ingin komit dari topiccabang diputar ulang di atas mastercabang saat ini , Anda memiliki dua opsi:

  1. Menggunakan rebase: Pertama-tama Anda pergi ke topicby doing git checkout topic, dan kemudian pindahkan cabang dengan menjalankan git rebase master, menghasilkan:

    A---B---C---D master
                 \
                  E'---F'---G' topic
    

    Hasil: cabang Anda saat topicini di-rebased (dipindahkan) ke master.
    The topiccabang telah diupdate, sedangkan mastercabang tetap di tempat.

  2. Menggunakan cherry-pick : pertama-tama Anda akan pergi masterdengan melakukan git checkout master, dan kemudian menyalin cabang dengan menjalankan git cherry-pick topic~3..topic(atau, setara, git cherry-pick B..G), menghasilkan:

    A---B---C---D---E'---F'---G' master
         \
          E---F---G topic
    

    Hasil: komit dari topicyang disalin ke dalam master.
    The mastercabang telah diupdate, sedangkan topiccabang tetap di tempat.


Tentu saja, di sini Anda harus secara eksplisit memberi tahu cherry-pick untuk memilih urutan komit , menggunakan notasi rentang foo..bar . Jika Anda hanya memberikan nama cabang, seperti di git cherry-pick topic, itu hanya akan mengambil komit di ujung cabang, menghasilkan:

A---B---C---D---G' master
     \
      E---F---G topic
waldyrious
sumber