Git Cherry-Pick sagt: "... 38c74d ist eine Zusammenführung, aber es wurde keine -m-Option angegeben."

518

Ich habe einige Änderungen in meiner Hauptniederlassung vorgenommen und möchte diese in den Upstream bringen. Wenn ich die folgenden Commits auswähle, stecke ich jedoch auf fd9f578 fest, wo git sagt:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

Was will git mir sagen und ist Cherry-Pick das Richtige, um es hier zu verwenden? Der Hauptzweig enthält Änderungen an Dateien, die im Upstream-Zweig geändert wurden. Ich bin mir also sicher, dass es einige Zusammenführungskonflikte geben wird, aber diese sind nicht schlecht, um sie zu korrigieren. Ich weiß, welche Änderungen wo benötigt werden.

Dies sind die Commits, die ich in den Upstream bringen möchte.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
Wufoo
quelle

Antworten:

606

Die Art und Weise, wie ein Kirschpflücker funktioniert, besteht darin, den Diff, den ein Änderungssatz darstellt (den Unterschied zwischen dem Arbeitsbaum an diesem Punkt und dem Arbeitsbaum seines übergeordneten Baums), auf Ihren aktuellen Zweig anzuwenden.

Wenn ein Commit zwei oder mehr Eltern hat, stellt es auch zwei oder mehr Unterschiede dar - welches sollte angewendet werden?

Sie versuchen, Kirschen zu pflücken fd9f578, was eine Fusion mit zwei Eltern war. Sie müssen dem Cherry-Pick-Befehl also mit der -mOption mitteilen, mit welchem ​​Befehl der Diff berechnet werden soll . git cherry-pick -m 1 fd9f578Verwenden Sie beispielsweise übergeordnetes Element 1 als Basis.

Ich kann nicht sicher sagen , für Ihre spezielle Situation, wobei jedoch git mergeanstelle der git cherry-pickist in der Regel ratsam. Wenn Sie ein Zusammenführungs-Commit auswählen, werden alle Änderungen, die in dem übergeordneten Element vorgenommen wurden, das Sie nicht angegeben haben, -min diesem einen Commit zusammengefasst . Sie verlieren all ihre Geschichte und bringen all ihre Unterschiede zusammen. Ihr Anruf.

Borealid
quelle
3
@wufoo Du solltest wahrscheinlich auch etwas darüber lernen git rebase- es ist wie eine Zusammenführung, aber anstatt zwei Zweige zu integrieren, wird einer transplantiert, um auf dem anderen zu sitzen.
Borealid
91
Woher kennst du die Elternnummer?
Anentropic
66
@Anentropic 1 ist das "erste Elternteil", 2 ist das "zweite Elternteil" und so weiter. Die Reihenfolge ist die, in der sie im Commit aufgeführt sind (wie von git showund dergleichen angezeigt ).
Borealid
2
@lkraav Du hättest auch einfach eine machen können git reset --hard HEAD@{1}, um dein fehlendes Commit zurückzubekommen. git resetist nicht darauf beschränkt, sich in der Geschichte "rückwärts" zu bewegen. git checkout -b mybranch HEAD@{1}würde auch funktionieren.
Borealid
4
WARNUNG: git mergeKann unbeabsichtigte Folgen haben. Dieser Befehl fügt alle anderen (älteren) Commits hinzu, die im übergeordneten Zweig vorhanden sind. Normalerweise entscheiden sich die Leute für die Auswahl von Kirschen, weil sie die anderen Commits nicht wollen. Stellen Sie sicher, dass Sie nur überprüfen, ob Sie nur die gewünschten Änderungen implementieren!
Kay V
52

-m bedeutet die übergeordnete Nummer.

Aus dem Git-Dokument:

Normalerweise können Sie keine Zusammenführung auswählen, da Sie nicht wissen, welche Seite der Zusammenführung als Hauptlinie betrachtet werden soll. Diese Option gibt die übergeordnete Nummer (beginnend mit 1) der Hauptzeile an und ermöglicht Cherry-Pick, die Änderung relativ zum angegebenen übergeordneten Element wiederzugeben.

Wenn Ihr Commit-Baum beispielsweise wie folgt lautet:

- A - D - E - F -   master
   \     /
    B - C           branch one

Dann git cherry-pick Ewird das Problem angezeigt, mit dem Sie konfrontiert sind.

git cherry-pick E -m 1bedeutet verwenden D-E, während git cherry-pick E -m 2bedeutet verwenden B-C-E.

Gavincook
quelle
32

Die Antwort von @ Borealid ist richtig, aber nehmen wir an, dass Sie sich nicht darum kümmern, den genauen Zusammenführungsverlauf eines Zweigs beizubehalten, sondern nur eine linearisierte Version davon auswählen möchten. Hier ist eine einfache und sichere Möglichkeit, dies zu tun:

Startzustand: Sie befinden sich in einer Filiale Xund möchten die Commits auswählen Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (Optional) git branch -D tempZ

Dies bedeutet, dass ein Zweig erstellt wird, tempZder auf Zdem Verlauf basiert , aber von dort Yan linearisiert ist, und dieser dann auf einer Kopie von Xaufgerufen ausgewählt wird newX. (Es ist sicherer, dies in einem neuen Zweig zu tun, als zu mutieren X.) Natürlich kann es in Schritt 4 zu Konflikten kommen, die Sie auf die übliche Weise lösen müssen ( cherry-pickfunktioniert rebasein dieser Hinsicht sehr ähnlich ). Schließlich wird der temporäre tempZZweig gelöscht .

Wenn in Schritt 2 die Meldung "Aktuelle VerzweigungstempZ ist aktuell" angezeigt wird, Y..Zwar diese bereits linear. Ignorieren Sie diese Meldung einfach und fahren Sie mit Schritt 3 fort.

Überprüfen newXSie dann, ob dies das getan hat, was Sie wollten.

(Hinweis: Dies ist nicht dasselbe wie ein einfacher git rebase XZweig Z, da er in keiner Weise von der Beziehung zwischen Xund abhängt. YMöglicherweise gibt es Commits zwischen dem gemeinsamen Vorfahren und dem Y, den Sie nicht wollten.)

Daira Hopwood
quelle
1
git rebase YsagtCurrent branch tempZ is up to date
Basilevs
Ich denke das bedeutet, dass Y..Zdas schon linear war. Sie können diese Nachricht also ignorieren und mit den Schritten 3 und 4 fortfahren.
Daira Hopwood
1
Interessante Idee, ich musste sie auf Papier zeichnen, um zu verstehen, was los war = D
Chris
2
Brillant. Git Cherry-Pick für einen ganzen Bereich beklagte sich entweder darüber, dass die Option -m fehlte oder dass sie bereitgestellt wurde. Ihre Lösung war golden. (Ein Vorschlag: TempZ-Zweig nach löschen)
Otheus
1
das ist großartig! Ich habe mit Cherry Pick gekämpft und nur das machte Sinn. Ich hatte ein bisschen Probleme mit den ausgewählten Buchstaben (einige Zweige und Commits sind Großbuchstaben und einige Zweige sind Kleinbuchstaben)
pcarvalho
19

Vereinfachen. Wählen Sie die Commits aus. Wählen Sie die Zusammenführung nicht aus.

Hier ist eine Neufassung der akzeptierten Antwort, die die Vorteile / Risiken möglicher Ansätze idealerweise verdeutlicht:

Sie versuchen, fd9f578 auszuwählen, was eine Fusion mit zwei Eltern war.

Anstatt eine Zusammenführung auszuwählen, ist es am einfachsten, die gewünschten Commits aus jedem Zweig der Zusammenführung auszuwählen.

Da Sie bereits zusammengeführt haben, befinden sich wahrscheinlich alle gewünschten Commits in Ihrer Liste. Wählen Sie sie direkt aus, und Sie müssen sich nicht mit dem Zusammenführungs-Commit herumschlagen.

Erläuterung

Die Art und Weise, wie ein Cherry-Pick funktioniert, besteht darin, den Diff, den ein Änderungssatz darstellt (den Unterschied zwischen dem Arbeitsbaum an diesem Punkt und dem Arbeitsbaum seines übergeordneten Elements), zu nehmen und den Änderungssatz auf Ihren aktuellen Zweig anzuwenden.

Wenn ein Commit zwei oder mehr Eltern hat, wie dies bei einer Zusammenführung der Fall ist, repräsentiert dieses Commit auch zwei oder mehr Unterschiede. Der Fehler tritt aufgrund der Unsicherheit auf, für die diff gelten soll.

Alternativen

Wenn Sie feststellen, dass Sie die Zusammenführung und die Auswahl der zugehörigen Commits einbeziehen müssen, haben Sie zwei Möglichkeiten:

  1. (Komplizierter und undurchsichtiger; verwirft auch den Verlauf) Sie können angeben, welcher Elternteil sich bewerben soll.

    • Verwenden Sie dazu die -mOption. Zum Beispiel git cherry-pick -m 1 fd9f578wird die erste Mutter in der Zusammenführung als Basis aufgeführt ist .

    • Beachten Sie auch, dass beim Auswählen eines Zusammenführungs-Commits alle Änderungen, die an dem übergeordneten Commit vorgenommen wurden, das Sie nicht angegeben haben, -min diesem einen Commit zusammengefasst werden . Sie verlieren all ihre Geschichte und bringen all ihre Unterschiede zusammen. Ihr Anruf.

  2. (Einfacher und vertrauter; bewahrt die Geschichte) können Sie git mergeanstelle von verwenden git cherry-pick.

    • Wie üblich git mergewird versucht, alle Commits anzuwenden, die in dem Zweig vorhanden sind, den Sie zusammenführen, und sie einzeln in Ihrem Git-Protokoll aufzulisten.
Kay V.
quelle
2

Vereinfachung der @aira Hopwood-Methode für die Auswahl eines einzelnen Commits. Benötigen Sie keine temporären Zweige.

Im Falle des Autors:

  • Z soll festgeschrieben werden (fd9f578)
  • Y ist Commit davor
  • X aktueller Arbeitszweig

dann mach:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
Ephemerr
quelle
2
Dadurch gehen natürlich die Metadaten verloren, die mit dem ursprünglichen Commit verknüpft sind. Ich denke, es ist Ansichtssache, ob es einfacher ist. Ich benutze es manchmal, wenn ich die Metadaten verlieren und nur die gesamten Codeänderungen behalten möchte . Beachten Sie, dass dies auch dann funktioniert, wenn Y kein unmittelbares übergeordnetes Element von Z ist (in diesem Fall werden die Änderungen gequetscht).
Daira Hopwood