Git Cherry Pick vs Rebase


120

Yakın zamanda Git ile çalışmaya başladım.

Üzerinden geçiyor Git kitabında çevrimiçi ben "Git Rebase" bölümü altında aşağıdaki bulundu:

Rebase komutu ile, bir dalda yapılan tüm değişiklikleri alıp başka bir dalda tekrar oynatabilirsiniz.

(Alıntı: http://git-scm.com/book/en/Git-Branching-Rebasing )

Bunun, git cherry- pick'in tam tanımı olduğunu düşündüm (şu anda kontrol edilen dalda bir commit veya bir dizi commit nesnesini yeniden uygulayın).

İkisi arasındaki fark nedir?

Yanıtlar:


166

git cherry-pickBirden fazla işlem uygulayabilmeyi öğrendiğinizden beri, ayrım gerçekten biraz tartışmalı hale geldi, ancak bu yakınsak evrim olarak adlandırılacak bir şey ;-)

Gerçek ayrım, her iki aracı da yaratma amacında yatmaktadır:

  • git rebase'nin görevi, bir geliştiricinin kendi özel havuzunda sahip olduğu bir dizi değişikliği, bazı yukarı akış dallarının X sürümüne karşı oluşturulan bir dizi değişikliği aynı dalın Y sürümüne (Y> X) iletmektir. Bu , bu işlem serilerinin temelini etkili bir şekilde değiştirir , dolayısıyla "yeniden satış".

    (Ayrıca, geliştiricinin herhangi bir keyfi işleme üzerine bir dizi taahhüdü aktarmasına izin verir, ancak bu daha az açık bir kullanımdır.)

  • git cherry-pickbir gelişim çizgisinden diğerine ilginç bir taahhüt getirmek içindir. Klasik bir örnek, kararsız bir geliştirme dalında yapılan bir güvenlik düzeltmesinin, mergepek çok istenmeyen değişiklik getireceğinden, a'nın hiçbir anlam ifade etmediği bir kararlı (bakım) şubesine geri gönderilmesidir.

    İlk ortaya çıkışından bu yana git cherry-pick, tek seferde birden fazla işlem seçebildi.

Bu nedenle, muhtemelen bu iki komut arasındaki en çarpıcı fark, üzerinde çalıştıkları şubeye nasıl davrandıklarıdır: git cherry-pickgenellikle başka bir yerden bir taahhüt getirir ve bunu mevcut şubenizin üzerine uygular, yeni bir commit kaydederken git rebase, mevcut şubenizi alır ve yeniden yazar. bir dizi kendi bahşişi şu ya da bu şekilde taahhüt eder. Evet, bu neler git rebaseyapılabileceğine dair son derece basitleştirilmiş bir açıklama , ancak genel fikri içine kaptırmaya çalışmak kasıtlı.

git rebaseTartışılan kullanım örneğini daha fazla açıklamak için güncelleme yapın .

Bu durum göz önüne alındığında,
yeniden satış öncesi repo durumu
Kitap şöyle der:

Ancak, başka bir yol daha var: C3'te tanıtılan değişikliğin yamasını alıp C4'ün üzerine yeniden uygulayabilirsiniz. Git'te buna yeniden sıralama denir. Rebase komutu ile, bir şubede yapılan tüm değişiklikleri alıp başka bir şubeye uygulayabilirsiniz.

Bu örnekte, aşağıdakileri çalıştıracaksınız:

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

Buradaki "püf noktası", bu örnekte "deney" dalının (yeniden adlandırma konusu) başlangıçta "ana" daldan ayrılması ve dolayısıyla C0 ile C2 arasındaki taahhütleri paylaşmasıdır - etkili bir şekilde "deney" ana "kadar ve C2 dahil, artı bunun üzerine C3 işleyin. (Bu, mümkün olan en basit durumdur; tabii ki, "deney" orijinal tabanının üzerinde birkaç düzinelerce işlem içerebilir.)

Şimdi git rebase"deney" i "usta" nın şu anki ipucu üzerine yeniden düzenlemesi söyleniyor ve git rebaseşöyle devam ediyor:

  1. git merge-baseHem "deney" hem de "usta" tarafından paylaşılan son işlemin ne olduğunu görmek için çalışır (başka bir deyişle, saptırmanın amacı nedir). Bu C2.
  2. Sapma noktasından bu yana yapılan tüm taahhütleri saklar; oyuncak örneğimizde sadece C3'tür.
  3. HEAD'i (işlem çalışmaya başlamadan önce "deney" in ipucu kaydına işaret eder) "ana" ifadesinin ucuna işaret edecek şekilde geri sarar - biz ona geri dönüyoruz.
  4. Kaydedilen taahhütlerin her birini git applysırayla (olduğu gibi ) uygulamaya çalışır . Oyuncak örneğimizde bu sadece bir commit, C3. Diyelim ki uygulaması bir commit C3 'üretecek.
  5. Her şey yolunda giderse, "deneme" referansı, son kaydedilen kaydetme uygulamasından (bizim durumumuzda C3 ') sonuçlanan kaydetmeyi gösterecek şekilde güncellenir.

Şimdi sorunuza geri dönün. Gördüğünüz gibi, burada teknik olarak git rebase gerçekten de bir dizi taahhüdü "deney" ten "usta" nın ucuna naklediyor, böylece süreçte gerçekten "başka bir dal" olduğunu haklı olarak söyleyebilirsiniz. Ancak işin özü, "deney" deki ipucu taahhüdünün "deney" deki yeni ipucu taahhüdüne dönüştüğü, sadece temelini değiştirdi:
birleştikten sonra durumu

Yine, teknik olarak git rebaseburada "usta" dan bazı taahhütleri birleştirdiğini söyleyebilirsiniz ve bu kesinlikle doğrudur.


2
Teşekkürler. Hala burada ne demek istediğini tam olarak anlayamadım. Kitapta, "aynı şubeden" olduğunu söylerken, yeniden ödemenin başka bir şubeden bir dizi bahşiş taahhüdü uyguladığı örnek verilmiştir. Ya da belki nasıl çalıştığına dair birkaç durum vardır?
lysergic-acid

1
Cevabımı güncelleyerek konuyu açıklamaya çalıştım.
kostix

98

Cherry-pick ile, orijinal taahhütler / dallar yapışır ve yeni taahhütler oluşturulur. Rebase ile, şube tekrarlanan taahhütleri gösterecek şekilde tüm şube taşınır.

Diyelim ki şununla başladınız:

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

rebase:

$ git rebase master topic

Sen alırsın:

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

Kiraz-almak:

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

Sen alırsın:

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

git hakkında daha fazla bilgi için bu kitapta çoğu var (http://git-scm.com/book)


3
İyi cevapladı. Ayrıca, sadece A ve B taahhütlerini kiraz seçmek isteyebilirsiniz, ancak bu durumlarda C'yi asılı bırakarak dalı tutmak isteyebilirsiniz ve sadece meslektaşların görmesi gerekebilecek kiraz toplama değişiklikleri yapabilirsiniz. Git insanlarla çalışmak için yapılmıştır, bu nedenle tek başına çalışırken bir şeyin faydalarını görmezseniz, genellikle daha büyük gruplar halinde çalışırken daha yaygın olarak kullanılır.
Pablo Jomer

Bunun yerine etkileşimli bir geri ödeme yapılsaydı, bir veya daha fazla taahhüt atılsaydı, sonunda hangi şubelere sahip olurdunuz? sadece topicüstüne yeniden yayılsaydı master, bırakılan taahhütleri içermez, peki bunlar hangi dalın parçası olacak?
Anthony

Eklemek istediğim bir şey daha var: eğer sen git checkout topicve sonra git reset --hard C'kiraz toplamadan sonra, o zaman yeniden satıştan sonraki ile aynı sonucu elde edersin. Ortak atamız çok eski olduğu için, kendimi yeniden satış yerine kiraz toplama yöntemini kullanarak birçok birleştirme çatışmasından kurtardım.
sorrymissjackson

@anthony - stackoverflow.com/questions/11835948/… : anladığım kadarıyla kaybolmuşlar. Ben git-guru değilim ama bu rebase/ cherry-pickile ilgili tüm detaylarda gitbir sorunla karşılaştım.
thoni56

1
Grafikleriniz faydadan çok zarar verir, çünkü bunlar işlevsel olarak aynıdır. Tek fark, git checkout -bhiçbir ilgisi olmayan tarafından yaratılan daldır git cherry-pick. Daha iyi bir yolu çalıştırmak”olacağını söylemeye çalıştığını açıklamak için git rebaseüzerinde topicşube ve geçmek master; Çalıştırmak git cherry-picküzerinde masterşube ve (gelen kaydedilmesini) geçmek topic.”
Rory O'Kane

14

Bireysel taahhütler için kiraz toplama çalışır .

Yeniden finansman yaptığınızda , geçmişteki tüm taahhütleri orada eksik olan şubenin HEAD'ine uygular .


Teşekkürler. Bunların örtülerin altında aynı şekilde çalışıp çalışmadığını biliyor musunuz? (ara çıktılarını "yama" dosyalarına vb. saklayın).
lysergic-acid

Afaik evet. Tüm yamaları tek tek uygular. Devam etmeden önce bir geri ödemenin ortasında birleştirme çatışmalarını çözmek zorunda kalmanızın nedeni budur.
iltempo

6
@iltempo, yalnızca Git'in eski sürümlerinde tek tek işlemeler için çalıştı; şu anda gibi bir şey yapabilir git cherry-pick foo~3..foove ağaç tepesini "foo" dan birer birer seçebilirsin .
kostix

1
git-rebase, kiraz toplama kod tabanında yaptığı gibi aynı API'yi kullanıyor, iirc
alternatif

Örtülerin altında aslında aynı şekilde çalıştıklarını sanmıyorum. Binlerce taahhüdü yeniden oluşturmayı denedim ve git'in büyük bir posta kutusu dosyası oluşturduğunu ve git ambunun üzerinde çalıştığını düşünüyorum. Oysa bir kiraz çekme, kaydetme yoluyla kesinleştirme uygular (muhtemelen her yama için tek mesajlı bir posta kutusu oluşturarak). Rebase'im başarısız oldu çünkü oluşturduğu posta kutusu dosyası sürücüde yer kalmadı, ancak aynı revizyon aralığına sahip kiraz çekme başarılı oldu (ve daha hızlı çalışıyor gibi görünüyor).
onlynone

11

Kısa bir cevap:

  • git kiraz-toplama daha "düşük seviyeli"
  • Gibi, git rebase taklit edebilir

Yukarıda verilen cevaplar iyidir, sadece aralarındaki ilişkiyi gösterme çabasıyla bir örnek vermek istedim.

"Git rebase" i bu eylem dizisiyle değiştirmek tavsiye edilmez, bu sadece "kavramın bir kanıtıdır" ve umarım, işlerin nasıl yürüdüğünü anlamaya yardımcı olur.

Aşağıdaki oyuncak deposu verildiğinde:

$ 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

Diyelim ki, master'da test_branch_1'imize dahil etmek istediğimiz çok önemli değişikliklerimiz var (# 2'den # 5'e kadar). Genellikle bir şubeye geçeriz ve "git rebase master" yaparız. Ama biz sadece "git kiraz-çekme" ile donatıldığımızı iddia ettiğimiz için:

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

Tüm bu işlemlerden sonra commit grafiğimiz şu şekilde görünecektir:

* 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

Gördüğümüz gibi, # 6 ve # 7 taahhütleri 7254931'e uygulandı (usta bir ipucu kaydı). HEAD taşındı ve esasen yeniden yapılan bir şubenin ipucu olan bir taahhüdü işaret ediyor. Şimdi tek yapmamız gereken eski bir dal işaretçisini silmek ve yeni bir tane oluşturmak:

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

test_branch_1 artık en son ana konumdan köklenmiştir. Bitti!


Ancak yeniden taban, git kiraz çekme simülasyonunu da yapabilir mi?
Number945

Yana cherry-pickkaydedilmesini bir dizi uygulayabilir, ben evet düşünüyorum. Bu, işleri yapmanın biraz garip bir yolu olsa da, hiçbir şey sizi özellik dalınızdaki tüm commit'leri en üstte seçmekten alıkoyamaz master, ardından özellik dalını silin ve ucunu gösterecek şekilde yeniden oluşturun master. Aklınıza gelebilecek git rebasebir dizinin olarak git cherry-pick feature_branch, git branch -d feature_branchve git branch feature_branch master.
18'de raiks

7

Her ikisi de bir dalın işlemlerini diğerinin üzerine yeniden yazmak için komutlardır: fark, hangi dalda - "sizin" (şu anda teslim alınmış olan HEAD) veya "onların" (dalın komuta bir argüman olarak iletildiği) - baz bu yeniden yazımın.

git rebaseBir sürer başlangıç işlemek ve tekrar oynatacak senin sonra gelmiş olarak kaydedilmesini onlarınki (başlangıç taahhüt).

git cherry-pickBir sürer kaydedilmesini kümesini ve tekrar oynatacak onların peşine olarak kaydedilmesini sizinki (senin HEAD).

Bir başka deyişle, iki komut, kendi çekirdek davranışlarında, (onların ıraksak performans özelliklerini göz ardı kuralları çağıran ve donanım seçenekleri) olan simetrik : şube kontrol barve koşu git rebase foosetleri barşube kontrol ile aynı tarihe şube foove çalışan git cherry-pick ..barkuracak footo (gelen değişiklikler foo, ardından gelen değişiklikler bar).

Adlandırma açısından, iki komut arasındaki fark, her birinin mevcut dala ne yaptığını tanımlamasıyla hatırlanabilir : rebasediğer baş, yaptığınız değişiklikler için yeni bir temel oluşturur, oysa cherry-pickdiğer daldan değişiklikleri seçer ve bunları en üstüneHEAD koyar . senin (bir dondurma üstüne kirazlar gibi).


1
Seninki dışında hiçbir cevabı anlayamadım! Özlü ve gereksiz ifadeler olmadan mükemmel bir anlam ifade ediyor.
neoxic

4

Her ikisi de çok benzer şeyler yapar; temel kavramsal fark (basitleştirilmiş terimlerle) şudur:

  • Rebase gelen hamle kaydedilmesini geçerli dalı için başka dalı .

  • kiraz-almak kopyalayan kaydedilmesini başka dal için geçerli dalı .

@ Kenny Ho'nun cevabına benzer diyagramlar kullanarak :

Bu başlangıç ​​durumu göz önüne alındığında:

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

... ve topicmevcut masterşubenin en üstüne tekrar oynatılan şubeden taahhütleri almak istediğinizi varsayarsak, iki seçeneğiniz vardır:

  1. Rebase kullanma: Önce topicyaparak gidersiniz git checkout topic, sonra şubeyi çalıştırarak hareket ettirirsiniz git rebase master:

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

    Sonuç: mevcut şubeniz topicyeniden düzenlendi (taşındı) master. İse şube, güncellendi şube yerinde kalmıştır.
    topicmaster

  2. Cherry-pick kullanarak : önce masteryaparak gidersiniz git checkout masterve ardından şunu çalıştırarak git cherry-pick topic~3..topic(veya eşdeğer olarak git cherry-pick B..G) dalı kopyalarsınız :

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

    Sonuç: kaydedilmesini gelen topicedildi kopyalanan içine master. İse şube, güncellendi şube yerinde kalmıştır.
    mastertopic


Tabii ki, burada, açık bir şekilde cherry-pick'e aralık gösterimini kullanarak bir dizi işlem seçmesini söylemeniz gerekiyordu foo..bar. Şube adını, olduğu gibi basitçe geçtiyseniz, git cherry-pick topicyalnızca şubenin ucundaki taahhüdü alır ve sonuçta:

A---B---C---D---G' master
     \
      E---F---G topic
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.