Git Cherry Pick vs Rebase

120

Niedawno zacząłem pracować z Gitem.

Przeglądając książkę Git w trybie online, w sekcji „Git Rebase” znalazłem:

Za pomocą polecenia rebase możesz pobrać wszystkie zmiany, które zostały wprowadzone w jednej gałęzi i odtworzyć je w innej.

(Cytat z: http://git-scm.com/book/en/Git-Branching-Rebasing )

Pomyślałem, że to jest dokładna definicja git cherry-pick (ponownie zastosuj zatwierdzenie lub zestaw obiektów zatwierdzonych do aktualnie pobranej gałęzi).

Jaka jest różnica między nimi?

kwas lizergowy
źródło

Odpowiedzi:

166

Odkąd git cherry-picknauczyliśmy się stosować wiele zatwierdzeń, to rozróżnienie rzeczywiście stało się nieco dyskusyjne, ale jest to coś, co można nazwać ewolucją zbieżną ;-)

Prawdziwa różnica polega na pierwotnym zamiarze stworzenia obu narzędzi:

  • git rebaseZadaniem jest przekierowanie serii zmian, które programista ma w swoim prywatnym repozytorium, utworzonych dla wersji X jakiejś gałęzi nadrzędnej, do wersji Y tej samej gałęzi (Y> X). To skutecznie zmienia podstawę tej serii zatwierdzeń, stąd „ponowne podstawy”.

    (Pozwala również programistom na przeszczepienie serii zatwierdzeń do dowolnego arbitralnego zatwierdzenia, ale ma to mniej oczywiste zastosowanie).

  • git cherry-picksłuży przeniesieniu interesującego zatwierdzenia z jednej linii rozwoju do drugiej. Klasycznym przykładem jest przeniesienie poprawki bezpieczeństwa dokonanej na niestabilnej gałęzi programistycznej do gałęzi stabilnej (konserwacyjnej), gdzie mergenie ma sensu, ponieważ przyniosłaby całą masę niechcianych zmian.

    Od pierwszego pojawienia się, git cherry-pickbył w stanie wybrać kilka zatwierdzeń naraz, jeden po drugim.

Dlatego prawdopodobnie najbardziej uderzającą różnicą między tymi dwoma poleceniami jest sposób, w jaki traktują gałąź, na której pracują: git cherry-pickzwykle przenosi zatwierdzenie z innego miejsca i stosuje je na bieżącej gałęzi, nagrywając nowe zatwierdzenie, podczas gdy git rebasebierze bieżącą gałąź i przepisuje seria własnych wskazówek zatwierdza się w taki czy inny sposób. Tak, jest to mocno głupi opis tego, co git rebasemożna zrobić, ale jest celowe, aby spróbować zagłębić się w ogólny pomysł.

Zaktualizuj, aby dokładniej wyjaśnić przykład git rebaseomawianego użycia .

W tej sytuacji
stan repozytorium przed zmianą bazy
Księga stwierdza:

Jest jednak inny sposób: możesz wziąć poprawkę zmiany, która została wprowadzona w C3 i ponownie zastosować ją na C4. W Git nazywa się to rebasingiem. Za pomocą polecenia rebase możesz wziąć wszystkie zmiany, które zostały zatwierdzone w jednej gałęzi i zastosować je w innej.

W tym przykładzie uruchomisz następujące polecenie:

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

W tym przykładzie „haczyk” polega na tym, że w tym przykładzie gałąź „eksperymentu” (temat do zmiany bazy) została pierwotnie oddzielona od gałęzi „nadrzędnej”, a zatem dzieli z nią zatwierdzenia od C0 do C2 - w rzeczywistości „eksperyment” to master ”aż do C2 włącznie, plus zatwierdzenie C3. (To najprostszy możliwy przypadek; oczywiście „eksperyment” może zawierać kilkadziesiąt zatwierdzeń na podstawie oryginalnej bazy).

Teraz git rebasemówi się, aby zmienić bazę „eksperymentu” na aktualną końcówkę „mistrza” i git rebasewygląda tak:

  1. Uruchamia, git merge-baseaby zobaczyć, jakie jest ostatnie zatwierdzenie wspólne zarówno dla „eksperymentu”, jak i dla „mistrza” (innymi słowy, jaki jest cel przekierowania). To jest C2.
  2. Oszczędza wszystkie zatwierdzenia dokonane od momentu przekierowania; w naszym przykładzie z zabawką jest to po prostu C3.
  3. Przewija HEAD (co wskazuje na końcówkę zatwierdzenia „eksperymentu” przed rozpoczęciem operacji), aby wskazać końcówkę „master” - ponownie bazujemy na niej.
  4. Próbuje zastosować po kolei każdy z zapisanych zatwierdzeń (jak gdyby git apply). W naszym przykładzie z zabawką to tylko jedno zatwierdzenie, C3. Powiedzmy, że jego aplikacja wygeneruje zatwierdzenie C3 '.
  5. Jeśli wszystko poszło dobrze, odniesienie do „eksperymentu” jest aktualizowane tak, aby wskazywało na zatwierdzenie wynikające z zastosowania ostatniego zapisanego zatwierdzenia (w naszym przypadku C3 ').

Wróćmy teraz do twojego pytania. Jak widać, technicznie rzecz biorąc, git rebase przeszczepia tutaj serię zatwierdzeń od „eksperymentu” do końcówki „mistrza”, więc można słusznie stwierdzić, że w tym procesie rzeczywiście istnieje „inna gałąź”. Ale sedno jest takie, że zatwierdzenie wskazówki z „eksperymentu” okazało się nowym zatwierdzeniem wskazówki w „eksperymencie”, po prostu zmieniło swoją podstawę:
stan po scaleniu

Ponownie, technicznie można powiedzieć, że git rebasetutaj włączono pewne zatwierdzenia z „mastera” i jest to absolutnie poprawne.

kostix
źródło
2
Dzięki. Nadal nie pojąłem w pełni, co masz na myśli. W książce podano przykład, że rebase stosuje serię zatwierdzeń napiwków z innej gałęzi, podczas gdy mówisz, że pochodzi ona z „tej samej gałęzi”. A może jest kilka przypadków, jak to działa?
kwas lizergowy,
1
Próbowałem wyjaśnić sprawę, aktualizując moją odpowiedź.
kostix
98

Dzięki cherry-pick oryginalne zatwierdzenia / rozgałęzienia pozostają w pobliżu i tworzone są nowe zatwierdzenia. W przypadku rebase cała gałąź jest przenoszona z gałęzią wskazującą na powtórzone zatwierdzenia.

Powiedzmy, że zacząłeś od:

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

Rebase:

$ git rebase master topic

Dostajesz:

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

Zbiór wiśni:

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

Dostajesz:

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

aby uzyskać więcej informacji o git, ta książka zawiera większość tego (http://git-scm.com/book)

Kenny Ho
źródło
3
Dobrze odpowiedział. Często zdarza się, że możesz chcieć zaszyfrować tylko zatwierdzenia A i B, ale pozostawić C wiszące w tych przypadkach, możesz chcieć zachować gałąź i po prostu wybrać najlepsze zmiany, które mogą potrzebować współpracownicy. Git jest stworzony do pracy z ludźmi, więc jeśli nie widzisz korzyści płynących z pracy w pojedynkę, jest on częściej używany podczas pracy w większych grupach.
Pablo Jomer
Gdyby zamiast tego wykonano interaktywną rebase, z pominięciem jednego lub więcej zatwierdzeń, jakie gałęzie miałbyś na końcu? gdyby był tylko topicrebased na wierzchu master, nie zawiera pominiętych zatwierdzeń, więc do której gałęzi będą one należeć?
Anthony
Jeszcze jedna rzecz, którą chcę dodać: jeśli ty, git checkout topica potem git reset --hard C'po zerwaniu wiśni, masz taki sam wynik, jak po ponownym bazowaniu. Uratowałem się przed wieloma konfliktami dotyczącymi scalania, używając wyboru wiśni zamiast ponownego bazowania, ponieważ wspólny przodek był dawno temu.
sorrymissjackson
@anthony - stackoverflow.com/questions/11835948/… : o ile rozumiem, zginęli. Nie jestem git-guru, ale to rebase/ cherry-pickjest jeden ze wszystkich szczegółów, z gitktórymi miałem problem ze zrozumieniem.
thoni56
1
Twoje wykresy wyrządzają więcej szkody niż pożytku, ponieważ są funkcjonalnie identyczne. Jedyną różnicą jest gałąź utworzona przez git checkout -b, z którą nie ma nic wspólnego git cherry-pick. Lepszym sposobem na wyjaśnienie tego, co próbujesz powiedzieć, byłoby: „biegniesz git rebasepo topicgałęzi i mijasz ją master; biegasz git cherry-pickna mastergałęzi i przekazujesz ją (zatwierdza) topic. ”
Rory O'Kane
14

Zbieranie wiśni działa dla indywidualnych zatwierdzeń .

Kiedy dokonujesz zmiany bazy, wszystkie zmiany w historii zostaną zastosowane do HEAD gałęzi, których tam brakuje.

iltempo
źródło
Dzięki. Czy wiesz, czy te same działają pod osłonami? (przechowuj ich pośrednie wyjścia w plikach "łatek" itp.).
kwas lizergowy
Afaik tak. Nakłada wszystkie poprawki jeden po drugim. To jest powód, dla którego czasami trzeba rozwiązać konflikty scalania w trakcie ponownego bazowania, zanim przejdziemy dalej.
iltempo
6
@iltempo, działało dla indywidualnych zatwierdzeń tylko w starszych wersjach Gita; w chwili obecnej możesz zrobić coś podobnego git cherry-pick foo~3..fooi pobrać zmiany z wierzchołka drzewa z "foo" wybieranego jeden po drugim.
kostix
1
git-rebase używa tego samego interfejsu API, co narzędzie typu cherry-picking w bazie kodu, iirc
alternatywa
Nie sądzę, żeby faktycznie działały tak samo pod kołdrą. Próbowałem zmienić bazę tysięcy zatwierdzeń i myślę, że git tworzy ogromny plik skrzynki pocztowej, a następnie uruchamia się git amna nim. Podczas gdy najlepszy wybór stosuje zatwierdzanie po zatwierdzeniu (prawdopodobnie tworząc skrzynkę pocztową z pojedynczą wiadomością dla każdej łatki). Moja rebase nie powiodła się, ponieważ plik skrzynki pocztowej, który tworzył, zabrakło miejsca na dysku, ale najlepszy wybór z tym samym zakresem wersji powiódł się (i wydaje się działać szybciej).
onlynone
11

Krótka odpowiedź:

  • git cherry-pick jest bardziej „niski”
  • W związku z tym może emulować git rebase

Odpowiedzi podane powyżej są dobre, chciałem tylko podać przykład, próbując zademonstrować ich wzajemne powiązania.

Nie zaleca się zastępowania „git rebase” tą sekwencją działań, jest to po prostu „dowód słuszności koncepcji”, który, mam nadzieję, pomaga zrozumieć, jak to działa.

Biorąc pod uwagę następujące repozytorium zabawek:

$ 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

Powiedzmy, że mamy kilka bardzo ważnych zmian (zatwierdzenia od # 2 do # 5) w pliku master, które chcemy uwzględnić w naszej gałęzi test_branch_1. Zwykle po prostu przełączamy się na gałąź i robimy „git rebase master”. Ale ponieważ udajemy, że jesteśmy wyposażeni tylko w „git cherry-pick”, robimy:

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

Po tych wszystkich operacjach nasz wykres zatwierdzenia będzie wyglądał następująco:

* 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

Jak widać, zatwierdzenia # 6 i # 7 zostały zastosowane przeciwko 7254931 (zatwierdzenie napiwku mastera). HEAD został przeniesiony i wskazuje zatwierdzenie, które jest w istocie końcówką nowej gałęzi. Teraz wszystko, co musimy zrobić, to usunąć stary wskaźnik gałęzi i utworzyć nowy:

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

test_branch_1 jest teraz zakorzeniony z ostatniej pozycji głównej. Gotowe!

raiks
źródło
Ale rebase może również symulować git cherry-pick?
Number945
Ponieważ cherry-pickjest w stanie zastosować szereg zatwierdzeń, myślę, że tak. Chociaż jest to trochę dziwny sposób robienia rzeczy, nic nie stoi na przeszkodzie, aby masterwybrać wszystkie zatwierdzenia w gałęzi feature , a następnie usunąć gałąź feature i ponownie utworzyć ją tak, aby wskazywała na koniec master. Można myśleć git rebasejak o sekwencji git cherry-pick feature_branch, git branch -d feature_branchi git branch feature_branch master.
raiks
7

Oba są poleceniami do przepisywania zatwierdzeń jednej gałęzi na drugiej: różnica polega na tym, która gałąź - „twoja” (aktualnie wyewidencjonowana HEAD) lub „ich” (gałąź przekazana jako argument do polecenia) jest podstawa do tego przepisać.

git rebaseprzyjmuje początkowe zatwierdzenie i odtwarza twoje zatwierdzenia jako następujące po ich (początkowym zatwierdzeniu).

git cherry-pickprzyjmuje zestaw zatwierdzeń i odtwarza ich zatwierdzenia jako nadchodzące po twoim (twoim HEAD).

Innymi słowy, te dwa polecenia są, w swojej podstawowej zachowania (ignorując ich rozbieżne charakterystyk, nazywając konwencje i opcje poprawy), symetryczne : sprawdzeniu oddział bari działa git rebase fooustawia się bargałąź do tej samej historii jak sprawdzanie oddział fooi działa git cherry-pick ..barnie stawiał foodo (zmiany z foo, a następnie zmiany z bar).

Jeśli chodzi o nazwy, różnicę między tymi dwoma poleceniami można zapamiętać, ponieważ każde z nich opisuje, co robi z bieżącą gałęzią: rebasesprawia, że ​​druga kieruje nową bazą zmian, podczas gdy cherry-pickwybiera zmiany z drugiej gałęzi i umieszcza je na wierzchu TwójHEAD (jak wiśnie na lodzie).

Stuart P. Bentley
źródło
1
Nie mogłem zrozumieć żadnej odpowiedzi poza twoją! Jest zwięzły i ma doskonały sens bez zbędnych sformułowań.
neoxic
4

Obie robią bardzo podobne rzeczy; główna różnica koncepcyjna polega na (w uproszczeniu), że:

  • rebase przenosi zatwierdzenia z bieżącej gałęzi do innej .

  • wybrane kopie zatwierdzeń z innej gałęzi do bieżącej gałęzi .

Korzystanie z diagramów podobnych do odpowiedzi @Kenny Ho :

Biorąc pod uwagę ten stan początkowy:

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

... i zakładając, że chcesz uzyskać zmiany z topicgałęzi odtwarzane na wierzchu bieżącej mastergałęzi, masz dwie możliwości:

  1. Korzystanie z rebase: najpierw udałoby się topiczrobić git checkout topic, a następnie przenieść gałąź, uruchamiając git rebase master, produkując:

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

    Wynik: Twoja obecna gałąź topiczostała przeniesiona (przeniesiona) na master. Gałąź została zaktualizowana, podczas gdy oddział pozostał na swoim miejscu.
    topicmaster

  2. Używając `` cherry-pick '' : najpierw przejdź masterprzez wykonanie git checkout master, a następnie skopiuj gałąź, uruchamiając git cherry-pick topic~3..topic(lub, równoważnie, git cherry-pick B..G), wytwarzając:

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

    Wynik: zatwierdzenia z topiczostały skopiowane do master. Gałąź została zaktualizowana, podczas gdy oddział pozostał na swoim miejscu.
    mastertopic


Oczywiście tutaj trzeba było wyraźnie powiedzieć cherry-pick, aby wybrał sekwencję zatwierdzeń , używając notacji zakresu foo..bar . Gdybyś po prostu przekazał nazwę gałęzi, tak jak w git cherry-pick topic, odebrałby on tylko zmianę na końcu gałęzi, co dałoby:

A---B---C---D---G' master
     \
      E---F---G topic
waldyrious
źródło