Ich möchte auf ein bestimmtes Commit zurückgreifen, nicht auf ein HEAD des anderen Zweigs:
A --- B --- C master
\
\-- D topic
zu
A --- B --- C master
\
\-- D topic
anstatt
A --- B --- C master
\
\-- D topic
Wie kann ich das erreichen?
Sie können die Verwendung des Parameters --onto vermeiden, indem Sie einen temporären Zweig für das Commit erstellen, das Sie mögen, und dann rebase in seiner einfachen Form verwenden:
git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Sie können sogar direkt vorgehen:
git checkout topic
git rebase <commitB>
Verwenden Sie die Option "Auf":
git rebase --onto master^ D^ D
Der Kommentar von jsz hat mir eine Menge Schmerzen erspart, daher folgt hier ein Schritt-für-Schritt-Rezept, das ich dazu benutze, um ein Commit zusätzlich zu jedem anderen Commit zurückzuweisen oder zu verschieben:
git rebase --onto <new parent> <old parent>
Im obigen Beispiel ist das so einfach wie:
git checkout topic
git rebase --onto B A
Ich habe eine Mischung von oben beschriebenen Lösungen verwendet:
$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp
Ich fand es viel einfacher zu lesen und zu verstehen. Die akzeptierte Lösung führte mich zu einem Verschmelzungskonflikt (zu faul, um sie von Hand zu reparieren):
$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
<some code>
.git/rebase-apply/patch:17: trailing whitespace.
<some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Der richtige Befehl zum Beantworten der gestellten Frage kann einer der folgenden sein (vorausgesetzt, der Zweig topic
ist bereits ausgecheckt):
git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B
Wenn topic
nicht ausgecheckt ist, hängen Sie einfach topic
an den Befehl an (mit Ausnahme des letzten):
git rebase --onto B master topic
Alternativ können Sie zuerst die Filiale überprüfen mit:
git checkout topic
Die Grundform des von uns benötigten Befehls, die aus der Dokumentation hervorgeht, lautet:
git rebase --onto <Target> [<Upstream> [<Branch>]]
<Branch>
ist optional und checkt lediglich den angegebenen Zweig aus, bevor der Rest des Befehls ausgeführt wird. Wenn Sie den Zweig, den Sie zurücksetzen möchten, bereits ausgecheckt haben, benötigen Sie diesen nicht. Beachten Sie, dass Sie <Upstream>
angegeben haben müssen, um <Branch>
anzugeben. Andernfalls wird git annehmen, dass Sie <Upstream>
angeben.
<Target>
ist das Commit, an das wir unsere Commit-Zeichenfolge anhängen. Wenn Sie einen Zweignamen angeben, geben Sie einfach das Head-Commit dieses Zweigs an. <Target>
kann jedes Commit sein, das nicht in der Zeichenfolge der zu verschiebenden Commits enthalten ist. Zum Beispiel:
A --- B --- C --- D master
\
\-- X --- Y --- Z feature
Um den gesamten Feature-Zweig zu verschieben, können Sie nicht X
, Y
, Z
oder feature
als <Target>
auswählen, da dies alles Festschreibungen innerhalb der zu verschiebenden Gruppe sind.
<Upstream>
ist etwas Besonderes, weil es zwei verschiedene Dinge bedeuten kann. Wenn es sich um ein Commit handelt, das Vorläufer des ausgecheckten Zweigs ist, dient es als Schnittpunkt. In dem von mir angegebenen Beispiel ist dies alles, was nicht C
, D
oder master
ist. Alle Commits nach <Upstream>
, bis der Kopf des ausgecheckten Zweigs verschoben wird.
Wenn <Upstream>
jedoch kein Vorfahr ist, sichert git die Kette ab dem angegebenen Commit, bis ein gemeinsamer Vorfahr mit dem ausgecheckten Zweig gefunden wird (und bricht ab, wenn er keinen findet). In unserem Fall führt ein <Upstream>
von B
, C
, D
oder master
dazu, dass B
festgeschrieben wird und als Schnittpunkt dient. <Upstream>
ist selbst ein optionaler Befehl, und wenn er nicht angegeben ist, überprüft git das übergeordnete Element des ausgecheckten Zweigs, was der Eingabe von master
entspricht.
Nachdem git die Commits ausgewählt hat, die es ausschneidet und verschiebt, wendet es sie auf <Target>
an und überspringt alle bereits auf das Ziel angewendeten.
Unter Verwendung dieses Ausgangspunkts:
A --- B --- C --- D --- E master
\
\-- X --- Y --- Z feature
git rebase --onto D A feature
Wendet Festschreibungen für B
, C
, X
, Y
, Z
an, um D
festzuschreiben, und überspringt B
und C
, weil sie bereits angewendet wurden.
git rebase --onto C X feature
Wendet Commits Y
und Z
auf Commit C
an und löscht effektiv Commit X
.
Eine einfachere Lösung ist git rebase <SHA1 of B> topic
. Dies funktioniert unabhängig davon, wo sich Ihr HEAD
befindet.
Wir können dieses Verhalten von git rebase doc bestätigen.
<upstream>
Upstream-Zweig zum Vergleich. Kann beliebig sein commit, nicht nur ein vorhandener Zweigname. Der Standardwert ist das konfigurierte Upstream für die aktuelle Branche.
topic
erwähne?git rebase <SHA1 of B> <SHA1 of topic>
Dies funktioniert auch, aber rebase führt nicht dazu, dass Topic
auf den so erstellten neuen Zweig verweist, und HEAD
befindet sich im getrennten Zustand. Von hier aus müssen Sie also alte Topic
manuell löschen und eine neue Verzweigungsreferenz für die von rebase erstellte neue Verzweigung erstellen.
Es gibt eine andere Möglichkeit, dies zu tun oder wenn Sie zu mehr als einem Commit zurückkehren möchten.
Hier ist ein Beispiel, um zu n
Anzahl der Commits zurückzukehren:
git branch topic master~n
Für diese Frage kann dies auch gemacht werden:
git branch topic master~1
Der Befehl funktioniert perfekt für git version 2.7.4
. Ich habe es noch nicht in einer anderen Version getestet.
Da Umbasierung so grundlegend ist, ist hier eine Erweiterung der Antwort von Nestor Milyaev . Die Kombination von jsz's und Simon Souths Kommentaren aus Adam Dymitruks Antwort ergibt diesen Befehl, der auf den Zweig topic
angewendet werden kann, unabhängig davon, ob er von der master
-Verzweigung A
oder C
verzweigt:
git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>
Beachten Sie, dass das letzte Argument erforderlich ist (andernfalls wird Ihr Zweig zurückgespult, um B
zu übergeben).
Beispiele:
# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master
Der letzte Befehl ist der, den ich normalerweise verwende.