2017-01-25 16 views
3

Angenommen, ich habe ein Feature unter branch1 entwickelt und es zur Code-Überprüfung mit einer GitHub-Pull-Anforderung gesendet. Während es überprüft wird, mache ich einige Folgearbeiten an branch2.Rebase-Zweig nach GitHub "Squash and merge" auf Master

branch2     -> D --> E --> F 
         / 
branch1 -> A --> B --> C 
     /
master M 

Mein Rezensent liebt meine Arbeit! Es sind keine Änderungen erforderlich. Ich füge die Pull-Anforderung für branch1 mit GitHub Squash and merge Feature zusammen.

Nach git pull auf master läuft und Löschen von branch1, ich bin mit dieser Situation gelassen:

branch2 -> A --> B --> C -> D --> E --> F 
     /
master M --> S 

Um branch2 eine sauber aussehende PR zu senden, würde Ich mag meine bekommen begehen Baum aussehen wie dies:

branch2  -> D' --> E' --> F' 
      /
master M --> S 

der Code bei S (die durch erzeugte commit "Squash und merge" für branch1) ist identisch mit C, da es nur die squa Schuppenversion von A --> B --> C.

Eine Möglichkeit, dies zu erreichen, wäre eine Sequenz wie diese auf branch2 auszuführen:

git reset --hard S 
git cherry-pick D E F 

Aber der all Commits aus so langweilig wird, und das ist wirklich fühlt wie ein Fütterungsmaterial. git rebase master wird natürlich nicht funktionieren, da die Commits A, B und C verschwinden müssen.

Was ist der beste Weg, eine Verzweigung von einer zerquetschten Version eines seiner Vorfahr-Commits zu rebasen?

+1

IIRC können Sie eine Auswahl von commits auswählen, z. 'Cherry-Pick D..F' – Whymarrh

+2

@Whymarrh: Sie können, aber dann müssen Sie' D^'nennen, um' D' selbst zu kopieren. Die Bereichsnotation ähnelt einem halboffenen Intervall, wobei der Anfang und das Ende ausgeschlossen sind. (Obwohl es tatsächlich eine Set-Subtraktion ist!) – torek

Antwort

7

Verwenden Sie git rebase mit --onto. Das ist immer noch ein bisschen schwierig, also machen Sie es früher leichter.

Ich denke, es ist übrigens besser, diese Graphen mit den Verzweigungsnamen unter der rechten Seite zu zeichnen, die auf einen bestimmten Commit zeigen. Dies ist, weil in Git, Commits sind auf mehrere Zweige, und die Namen der Filialen wirklich tun nur auf eine bestimmte Commit zeigen. Es lohnt sich auch, die internen Pfeile umzukehren (weil Git sie wirklich so speichert) oder einfach Verbindungslinien zu verwenden, um nicht die falsche Richtung zu implizieren.

Daraus folgt:

  D--E--F <-- branch2 
     / 
    A--B--C  <-- branch1 
/
M   <-- master 

Verpflichtet A durch C wirklich sind sowohl branch1undbranch2, während verpflichtet D durch F sind nur auf branch2. (Commits M und früher sind in allen drei Zweigen.

)

Was git rebase upstream tut, ist wählen Sie alle erreichbar von die Stromzweig verpflichtet, aber nicht erreichbar von die upstream Argument, kopieren Sie sie dann (mit git cherry-pick oder gleichwertig), so dass sie kommen direkt nach die upstream commit.

Nach dem Squash- „merge“ (nicht wirklich ein merge), wenn Sie git fetch laufen und dann vorspulen Ihre master, Sie die gleiche Sache haben Sie gezeichnet haben, aber ich lasse branch1 in und legen Sie die Etiketten auf der linken Seite und fügen Sie origin/master hier:

  D--E--F <-- branch2 
     / 
    A--B--C  <-- branch1 
/
M--S  <-- master, origin/master 

(Oder, wenn Sie nicht vorspulen Ihre master noch, nur origin/master Punkte zu begehen S).

Sie möchten nun Git sagen, dass Sie D-E-F mit cherry-pick kopieren und dann das Label branch2 verschieben, um auf das letzte kopierte Commit zu zeigen. Sie nicht möchten A-B-C kopieren, wie sie in S integriert sind. Sie möchten, dass die Kopien nach S gehen, auf die jetzt origin/master zeigt - unabhängig davon, ob Sie master aktualisiert haben oder nicht. Daraus folgt:

git checkout branch2 
git rebase --onto origin/master branch1 

Die upstream ist jetzt branch1 statt master, aber die --onto sagt Git, wo die Kopien platzieren: branch1 nur dient zu begrenzen, was nicht zu kopieren. So, jetzt Git Kopien D-E-F und ändert branch2 dort zeigen:

  D--E--F [abandoned] 
     / 
    A--B--C  <-- branch1 
/
M--S  <-- master?, origin/master 
    \ 
    D'-E'-F' <-- branch2 

und jetzt Sie den Namen branch1 löschen. (Und jetzt können Sie vorzuspulen master, wenn Sie noch nicht-es ist nicht wirklich wichtig, wenn Sie es tun, und in der Tat brauchen Sie nicht Ihre eigene master überhaupt.)


Genauer gesagt, wählt rebase Commits aus, die (a) keine Merge-Commits sind und (b) nicht dieselbe git patch-id als Commit in der ausgeschlossenen Menge haben, wobei eine symmetrische Differenz verwendet wird. Das heißt, statt upstream..HEAD läuft Git tatsächlich git rev-list auf upstream...HEAD, mit --cherry-mark oder ähnlichem, um Commits auszusuchen. Die Implementierungen variieren geringfügig in Abhängigkeit von der bestimmten Art von Rebase.

+0

Danke für die tollen Diagramme, ich hatte noch nie von '--onto' gehört und es macht genau das, was ich will. Ich bemerkte, dass ich verschiedene commit SHAs mit 'git rebase - auf Master Branch' 'als wenn ich' git reset --hard master && git cherry-pick branch1..branch2' verwendet habe. Ist das etwas grundlegend anders als eine Reihe von Cherry-Picks? – danvk

+1

Eine nicht interaktive 'git rebase' wird oft' git format-patch' zu 'git am' pipen, um die Commits zu machen, aber der Effekt ist meistens der gleiche. Befehle, die mindestens eine Sekunde auseinander liegen, erhalten unterschiedliche Hashwerte (sofern sie nicht sorgfältig gesteuert werden, wie z. B. "git filter-branch"), da sie den 1-Sekunden-Zeitstempel von "jetzt" erhalten und Teil der Daten sind Hashed. – torek

Verwandte Themen