Git Cherry Pick gegen Rebase

119

Ich habe vor kurzem angefangen, mit Git zu arbeiten.

Als ich das Git-Buch online durchgesehen habe, habe ich im Abschnitt "Git Rebase" Folgendes gefunden:

Mit dem Befehl rebase können Sie alle Änderungen, die in einem Zweig festgeschrieben wurden, in einem anderen Zweig wiedergeben.

(Zitiert von: http://git-scm.com/book/en/Git-Branching-Rebasing )

Ich dachte, das ist die genaue Definition von Git cherry-pick (wende ein Commit oder eine Reihe von Commit-Objekten erneut auf den aktuell ausgecheckten Zweig an).

Was ist der Unterschied zwischen den beiden?

Lysergsäure
quelle

Antworten:

164

Seit die Zeit git cherry-pickgelernt hat, mehrere Commits anwenden zu können, wurde die Unterscheidung zwar etwas umstritten, aber dies ist etwas, das man als konvergente Evolution bezeichnen kann ;-)

Der wahre Unterschied liegt in der ursprünglichen Absicht, beide Werkzeuge zu erstellen:

  • git rebaseDie Aufgabe besteht darin, eine Reihe von Änderungen, die ein Entwickler in seinem privaten Repository vorgenommen hat und die gegen Version X eines Upstream-Zweigs erstellt wurden, auf Version Y desselben Zweigs (Y> X) weiterzuleiten. Dies ändert effektiv die Basis dieser Reihe von Commits und führt somit zu einer "Umbasierung".

    (Es ermöglicht dem Entwickler auch, eine Reihe von Commits auf ein beliebiges Commit zu übertragen, dies ist jedoch weniger offensichtlich.)

  • git cherry-pickdient dazu, ein interessantes Engagement von einer Entwicklungslinie in eine andere zu bringen. Ein klassisches Beispiel ist das Backportieren eines Sicherheitsupdates für einen instabilen Entwicklungszweig in einen stabilen (Wartungs-) Zweig, in dem a mergekeinen Sinn ergibt , da dies viele unerwünschte Änderungen mit sich bringen würde.

    Seit seinem ersten Erscheinen git cherry-pickkonnten mehrere Commits gleichzeitig ausgewählt werden.

Der wahrscheinlich auffälligste Unterschied zwischen diesen beiden Befehlen besteht darin, wie sie den Zweig behandeln, an dem sie arbeiten: git cherry-pickBringt normalerweise ein Commit von einem anderen Ort und wendet es auf Ihren aktuellen Zweig an, zeichnet ein neues Commit auf, git rebasenimmt Ihren aktuellen Zweig und schreibt ihn neu Eine Reihe eigener Tipps begeht auf die eine oder andere Weise. Ja, dies ist eine stark heruntergekommene Beschreibung dessen, was getan werden git rebasekann, aber es ist beabsichtigt, zu versuchen, die allgemeine Idee zum Eintauchen zu bringen.

Aktualisieren Sie , um ein Beispiel für die Verwendung git rebasezu erläutern.

In dieser Situation heißt es im
ein Zustand des Repos vor dem erneuten Basieren
Buch :

Es gibt jedoch noch einen anderen Weg: Sie können den Patch der in C3 eingeführten Änderung übernehmen und erneut auf C4 anwenden. In Git wird dies als Rebasing bezeichnet. Mit dem Befehl rebase können Sie alle Änderungen, die in einem Zweig festgeschrieben wurden, auf einen anderen übertragen.

In diesem Beispiel würden Sie Folgendes ausführen:

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

"Der Haken" hier ist, dass in diesem Beispiel der Zweig "Experiment" (das Thema für die Neubasierung) ursprünglich vom Zweig "Master" abgezweigt wurde und daher die Commits C0 bis C2 mit ihm teilt - effektiv ist "Experiment" " Master "bis einschließlich C2 plus Commit C3 darüber. (Dies ist der einfachste Fall. Natürlich kann "Experiment" mehrere Dutzend Commits zusätzlich zu seiner ursprünglichen Basis enthalten.)

Jetzt git rebasewird gesagt, dass "Experiment" auf die aktuelle Spitze von "Master" zurückgesetzt werden soll, und es git rebasegeht so:

  1. Läuft, um git merge-basezu sehen, was das letzte Commit ist, das sowohl von "Experiment" als auch von "Master" geteilt wird (was ist der Sinn der Ablenkung, mit anderen Worten). Das ist C2.
  2. Spart alle seit dem Umleitungspunkt getätigten Commits; In unserem Spielzeugbeispiel ist es nur C3.
  3. Spult den KOPF zurück (der auf das Spitzen-Commit von "Experiment" zeigt, bevor die Operation ausgeführt wird), um auf die Spitze von "Master" zu zeigen - wir stützen uns darauf.
  4. Versucht, alle gespeicherten Commits (wie mit git apply) der Reihe nach anzuwenden . In unserem Spielzeugbeispiel ist es nur ein Commit, C3. Angenommen, seine Anwendung erzeugt ein Commit C3 '.
  5. Wenn alles gut gegangen ist, wird die Referenz "Experiment" aktualisiert, um auf das Commit zu verweisen, das sich aus dem Anwenden des zuletzt gespeicherten Commits ergibt (in unserem Fall C3 ').

Nun zurück zu Ihrer Frage. Wie Sie sehen können, wird hier technisch gesehen git rebase eine Reihe von Commits von "Experiment" bis zur Spitze von "Master" transplantiert, sodass Sie zu Recht feststellen können, dass tatsächlich "ein anderer Zweig" im Prozess ist. Aber das Wesentliche ist, dass das Tip-Commit aus "Experiment" das neue Tip-Commit in "Experiment" war. Es hat nur seine Basis geändert:
Zustand nach dem Zusammenführen

Auch hier kann man technisch gesehen feststellen, dass git rebasehier bestimmte Commits von "master" enthalten sind, und dies ist absolut korrekt.

kostix
quelle
2
Vielen Dank. Ich habe immer noch nicht ganz verstanden, was du hier meinst. In dem Buch wird das Beispiel gegeben, dass Rebase eine Reihe von Tipp-Commits von einem anderen Zweig anwendet, während Sie sagen, dass es von "demselben Zweig" stammt. Oder gibt es vielleicht ein paar Fälle, wie es funktioniert?
Lysergsäure
1
Versucht, die Angelegenheit durch Aktualisierung meiner Antwort zu erklären.
Kostix
98

Mit Cherry-Pick bleiben die ursprünglichen Commits / Zweige erhalten und es werden neue Commits erstellt. Bei der erneuten Basis wird der gesamte Zweig verschoben, wobei der Zweig auf die wiedergegebenen Commits zeigt.

Angenommen, Sie haben begonnen mit:

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

Rebase:

$ git rebase master topic

Du erhältst:

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

Kirschpickel:

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

Du erhältst:

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

Für weitere Informationen über Git hat dieses Buch das meiste davon (http://git-scm.com/book)

Kenny Ho
quelle
3
Gut beantwortet. Es ist auch üblich, dass Sie nur A- und B-Commits auswählen möchten, C jedoch in den Fällen hängen lassen möchten, in denen Sie den Zweig beibehalten und nur Änderungen auswählen möchten, die Kollegen möglicherweise sehen müssen. Git wurde für die Arbeit mit Menschen entwickelt. Wenn Sie also die Vorteile von etwas nicht sehen, wenn Sie alleine arbeiten, wird es häufig häufiger verwendet, wenn Sie in größeren Gruppen arbeiten.
Pablo Jomer
Wenn stattdessen eine interaktive Rebase durchgeführt würde, bei der ein oder mehrere Commits weggelassen würden, welche Zweige hätten Sie am Ende? Wenn es nur darüber hinaus neu topicbasiert master, enthält es nicht die ausgelassenen Commits. In welchem ​​Zweig werden sie also sein?
Anthony
Nur noch eine Sache möchte ich hinzufügen: Wenn Sie git checkout topicund dann git reset --hard C'nach der Kirschernte, dann haben Sie das gleiche Ergebnis wie nach der Umbasierung. Ich habe mich vor vielen Zusammenführungskonflikten bewahrt, indem ich Kirschen gepflückt habe, weil der gemeinsame Vorfahr schon lange zurückliegt.
Sorrymissjackson
@anthony - stackoverflow.com/questions/11835948/… : Soweit ich weiß, sind sie verloren. Ich bin kein git-guru aber die rebase/ cherry-pickist auf alle Details mit , gitdass ich ein Problem hatte Verständnis.
Thoni56
1
Ihre Grafiken schaden mehr als sie nützen, da sie funktional identisch sind. Der einzige Unterschied ist der von erstellte Zweig git checkout -b, der nichts damit zu tun hat git cherry-pick. Eine bessere Möglichkeit zu erklären, was Sie sagen möchten, wäre: „Sie rennen git rebaseauf dem topicAst und passieren ihn master. Sie laufengit cherry-pick auf dem masterAst und übergibst ihn (Commits von) topic. “
Rory O'Kane
14

Das Pflücken von Kirschen funktioniert für einzelne Commits .

Wenn Sie die Basis neu festlegen, werden alle Commits im Verlauf auf den HEAD des Zweigs angewendet , die dort fehlen.

iltempo
quelle
Vielen Dank. Wissen Sie, ob diese unter der Decke gleich funktionieren? (Speichern Sie ihre Zwischenausgaben in "Patch" -Dateien usw.).
Lysergsäure
Afaik ja. Es werden alle Patches einzeln angewendet. Dies ist der Grund, warum Sie manchmal Zusammenführungskonflikte mitten in einer Rebase lösen müssen, bevor Sie fortfahren können.
Iltempo
6
@iltempo, es funktionierte für einzelne Commits nur in älteren Versionen von Git; Gegenwärtig können Sie so etwas tun git cherry-pick foo~3..foound die Tree-Top-Commits von "foo" einzeln auswählen lassen.
Kostix
1
Git-Rebase verwendet die gleiche API wie das Cherry-Picking in der Codebasis, iirc
Alternative
Ich glaube nicht, dass sie unter der Decke tatsächlich gleich funktionieren. Ich habe versucht, Tausende von Commits neu zu gründen, und ich denke, Git erstellt eine riesige Postfachdatei und läuft dann git amdarauf. Während ein Cherry Pick Commit für Commit anwendet (möglicherweise durch Erstellen eines Postfachs mit einer Nachricht für jeden Patch). Meine Rebase schlug fehl, weil die von ihr erstellte Postfachdatei nicht mehr über genügend Speicherplatz auf dem Laufwerk verfügte, aber Cherry-Pick mit demselben Revisionsbereich erfolgreich war (und anscheinend schneller ausgeführt wird).
Nur am
11

Eine kurze Antwort:

  • Git Cherry-Pick ist eher "Low Level"
  • Als solches kann es Git Rebase emulieren

Die oben gegebenen Antworten sind gut. Ich wollte nur ein Beispiel geben, um ihre Wechselbeziehung zu demonstrieren.

Es wird nicht empfohlen, "git rebase" durch diese Abfolge von Aktionen zu ersetzen. Es handelt sich lediglich um einen "Proof of Concept", der hoffentlich dazu beiträgt, die Funktionsweise der Dinge zu verstehen.

Angesichts des folgenden Spielzeug-Repositorys:

$ 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

Angenommen, wir haben einige sehr wichtige Änderungen (Commits Nr. 2 bis Nr. 5) im Master, die wir in unsere test_branch_1 aufnehmen möchten. Normalerweise wechseln wir einfach zu einem Zweig und machen "Git Rebase Master". Aber da wir so tun, als wären wir nur mit "git cherry-pick" ausgestattet, tun wir Folgendes:

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

Nach all diesen Operationen sieht unser Commit-Diagramm folgendermaßen aus:

* 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

Wie wir sehen können, wurden die Commits Nr. 6 und Nr. 7 gegen 7254931 angewendet (ein Tipp-Commit des Masters). HEAD wurde verschoben und zeigt auf ein Commit, das im Wesentlichen eine Spitze eines neu basierten Zweigs ist. Jetzt müssen wir nur noch einen alten Verzweigungszeiger löschen und einen neuen erstellen:

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

test_branch_1 ist jetzt von der letzten Master-Position aus gerootet . Getan!

Raiks
quelle
Aber Rebase kann auch Git Cherry-Pick simulieren?
Nummer 945
Da cherry-pickin der Lage ist, eine Reihe von Commits anzuwenden, denke ich, ja. Obwohl dies eine etwas seltsame Art ist, hindert Sie nichts daran, alle Commits in Ihrem Feature-Zweig darüber masterauszuwählen, den Feature-Zweig zu löschen und neu zu erstellen, sodass er auf die Spitze von zeigt master. Sie können denken, git rebaseder als eine Sequenz git cherry-pick feature_branch, git branch -d feature_branchund git branch feature_branch master.
Raiks vor
7

Sie sind beide Befehle zum Umschreiben der Commits eines Zweigs über einen anderen: Der Unterschied besteht darin, welcher Zweig - "Ihr" (der aktuell ausgecheckte HEAD) oder "ihr" (der Zweig, der als Argument an den Befehl übergeben wurde) - ist die Basis für dieses Umschreiben.

git rebasenimmt ein Ausgang zu begehen und Replays Ihre Commits als nach kommen sie (der Start begehen).

git cherry-picknimmt eine Reihe von Commits und Replays ihre Commits wie nach kommen Sie (Ihre HEAD).

Mit anderen Worten, die beiden Befehle in ihrem Kern Verhalten (ohne Berücksichtigung ihrer unterschiedlichen Leistungsmerkmalen, Aufrufkonventionen und Erweiterungsoptionen), symmetrisch : Zweig Check - out barund Lauf git rebase foosetzt den barZweig in die gleiche Geschichte wie Check - out Zweig foound läuft git cherry-pick ..bargesetzt würde foobis (die Änderungen von foo, gefolgt von den Änderungen von bar).

Naming weise kann der Unterschied zwischen den beiden Befehlen in Erinnerung bleiben , dass jeder beschreibt , was es tut , um den aktuellen Zweig: rebasemacht den anderen Kopf die neue Basis für die Änderungen, während cherry-pickPicks aus dem anderen Zweig ändern und legt sie oben auf IhreHEAD (wie Kirschen auf einem Eisbecher).

Stuart P. Bentley
quelle
1
Ich konnte keine der Antworten außer Ihrer verstehen! Es ist prägnant und macht ohne überflüssige Formulierung vollkommen Sinn.
neoxisch
4

Beide machen sehr ähnliche Dinge; Der hauptsächliche konzeptionelle Unterschied besteht (vereinfacht ausgedrückt) darin, dass:

  • rebase verschiebt Commits vom aktuellen Zweig in einen anderen Zweig .

  • Cherry-Pick- Kopien werden von einem anderen Zweig in den aktuellen Zweig übernommen .

Verwenden von Diagrammen ähnlich der Antwort von @Kenny Ho :

Angesichts dieses Ausgangszustands:

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

... und vorausgesetzt, Sie möchten, dass die Commits aus dem topicZweig über dem aktuellen masterZweig wiedergegeben werden, haben Sie zwei Möglichkeiten:

  1. Verwenden von rebase: Sie gehen zuerst zu, topicindem Sie Folgendes tun git checkout topic, und verschieben dann den Zweig, indem Sie Folgendes ausführengit rebase master :

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

    Ergebnis: Ihr aktueller Zweig topicwurde neu basiert (verschoben)master .
    Die topicNiederlassung wurde aktualisiert, während die masterNiederlassung bestehen blieb.

  2. Mit Kirsche-Pick : Sie zuerst gehen würde , masterindem Sie git checkout masterund dann den Zweig kopieren , indem Siegit cherry-pick topic~3..topic (oder äquivalent git cherry-pick B..G), die Herstellung von :

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

    Ergebnis: die Commits aus topicwurden kopiert in master.
    Dasmaster Niederlassung wurde aktualisiert, während die topicNiederlassung bestehen blieb.


Natürlich musste man Cherry-Pick hier explizit anweisen, eine Folge von Commits unter Verwendung der Bereichsnotation auszuwählenfoo..bar . Wenn Sie einfach den Filialnamen wie in übergeben hätten git cherry-pick topic, hätte er nur das Commit an der Spitze der Filiale erfasst, was zu Folgendem geführt hätte:

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