Git: Wie kann ich auf ein bestimmtes Commit zurückgreifen?

154

Ich möchte auf ein bestimmtes Commit zurückgreifen, nicht auf einen 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?

Ondra Žižka
quelle
4
Hast du es git checkout Bvor dem Laufen versucht git rebase?
Peter-Paul van Gemerden
Nein, sollte das helfen? Ich denke, nur die Referenzen des rebaseBefehls sind wichtig.
Ondra Žižka

Antworten:

98

Sie können die Verwendung des Parameters --onto vermeiden, indem Sie einen temporären Zweig für das gewünschte Commit erstellen und dann rebase in seiner einfachen Form verwenden:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Adam Dymitruk
quelle
5
Ich mag diesen RISC-ähnlichen Ansatz mehr :) Ich werde es versuchen. Vielen Dank.
Ondra Žižka
10
Ich frage mich, warum es bei mir in einem etwas anderen Szenario nicht funktioniert . Ich möchte, dass die Gruppe pep8 überspringt und auf dem Master basiert . git rebase temp(wenn auf Gruppe ) gibt mit "Aktuelle Zweiggruppen sind auf dem neuesten Stand." auf.
Alois Mahdal
4
Diese Lösung funktioniert nicht für das Szenario, in dem das Thema bereits auf Master neu basiert wurde, Sie es jedoch auf einem Vorfahren neu erstellen möchten, der gemastert werden soll. In diesem Fall müssen Sie verwenden, git rebase --onto <target> <from> <to>damit Sie das Commit <from> angeben können.
Mirzmaster
Dies ist bei weitem die einfachste Option, wenn Sie auf dasselbe Commit zurückgreifen möchten, auf dem der Zweig basiert.
Ash
1
Ich scheine zu arbeiten, aber das GitLab sagt etwas anderes. Wenn ich 10 Commits hinter mir hatte, 5 Commits voraus, war meine Erwartung, 8 Commits hinter mir zu haben, 5 Commits voraus, nachdem ich 2 Commits erhalten hatte. Aber stattdessen fügt es mehr Commits zu diesen 5.
ROMANIA_engineer
67

Sie können sogar direkt vorgehen:

git checkout topic
git rebase <commitB>
r0hitsharma
quelle
6
Für mich macht das nicht das, was beabsichtigt ist. Soweit ich das beurteilen kann, versucht es, sich auf den "letzten gemeinsamen Vorfahren" von topicund zu stützen commitB.
Dan Lenski
2
@DanLenski, so funktioniert Rebase nicht. Als ich die Dokumente zitierte , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. versuchte ich es jetzt noch einmal und schien gut zu funktionieren.
r0hitsharma
1
Super einfach und funktioniert! Ich habe jetzt: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek
Bei mir hat es nicht funktioniert. Zuvor sagte GitLab "n Commits voraus". Und jetzt heißt es "m setzt voraus" wo m > n.
ROMANIA_engineer
48

Verwenden Sie die Option "Ein":

git rebase --onto master^ D^ D
Adam Dymitruk
quelle
2
Dund D^wäre Hash des letzten und vorletzten Commits von "Thema"?
Ondra Žižka
39
Die Syntax ist wie git rebase --onto <new-parent> <old-parent>. Siehe Setzen des übergeordneten Git-Zeigers auf ein anderes übergeordnetes Element . In Ihrem Fall ist <new-parent> B und <old-parent> A.
jsz
7
Ich benutze immer die 3 Argumente: Desitnation, Beginn und Ende von Commits, um neu zu gründen.
Adam Dymitruk
14
Dies funktionierte für mich:git rebase --onto <commit-ID> master
4
@ jsz 'Kommentar ist korrekt, im Gegensatz zu Simon Souths Kommentar ist es umgekehrt: git rebase --onto master <commit-ID-of-old-parent>und für OP git rebase --onto B A.
gaborous
19

Der Kommentar von jsz oben hat mir jede Menge Schmerzen erspart. Hier ist ein Schritt-für-Schritt-Rezept, das darauf basiert und das ich verwendet habe, um ein Commit über ein anderes Commit zu verschieben / zu verschieben:

  1. Suchen Sie einen vorherigen Verzweigungspunkt des Zweigs, der neu basiert (verschoben) werden soll - nennen Sie ihn altes übergeordnetes Element. Im obigen Beispiel ist das A.
  2. Suchen Sie ein Commit, auf das Sie den Zweig verschieben möchten - nennen Sie es neues übergeordnetes Element. In der Exampe ist das B.
  3. Sie müssen sich in Ihrem Zweig befinden (dem, den Sie bewegen):
  4. Wenden Sie Ihre Rebase an: git rebase --onto <new parent> <old parent>

Im obigen Beispiel ist das so einfach wie:

   git checkout topic
   git rebase --onto B A
Nestor Miljew
quelle
6
Dies sollte die richtige Antwort sein. Abgesehen von meiner Verwendung git rebase --onto B masterfinden Sie in meiner Antwort eine ausführlichere Erklärung.
Zack Morris
Es hat bei mir nicht gut funktioniert. Ich habe 2 aufeinanderfolgende Commits ausgewählt (das letzte vom Master, das sich im aktuellen Zweig befand, und das erste vom Master, das sich nicht im aktuellen Zweig befand). Ich habe mit 100 Rückstand - 10 Vorsprung angefangen und anstatt 99 Rückstand - 10 Vorsprung - habe ich jetzt 105 Rückstand - 13 Vorsprung.
ROMANIA_engineer
Tut mir leid zu hören, dass es nicht funktioniert hat. Klingt so, als ob Ihre Zweige ziemlich unterschiedlich sind - ich würde empfehlen, zuerst zu quetschen, bevor Sie versuchen, Zweige mit diesen vielen Unterschieden neu zu gründen.
Nestor Milyaev
11

Themenlösung

Der richtige Befehl zur Beantwortung der gestellten Frage kann einer der folgenden sein (vorausgesetzt, der Zweig topicist 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 topicnicht ausgecheckt, fügen Sie einfach topicden Befehl (mit Ausnahme des letzten) wie folgt hinzu :

git rebase --onto B master topic

Alternativ können Sie zuerst die Filiale überprüfen mit:

git checkout topic

Stellen Sie eine beliebige Zeichenfolge von Commits auf ein Ziel-Commit um

Die Grundform des Befehls, den wir benötigen und der aus der Dokumentation hervorgeht, lautet:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>ist optional und überprüft lediglich den angegebenen Zweig, bevor der Rest des Befehls ausgeführt wird. Wenn Sie den Zweig, den Sie neu aufbauen möchten, bereits ausgecheckt haben, benötigen Sie diesen nicht. Beachten Sie, dass Sie angegeben haben müssen, <Upstream>um anzugeben, <Branch>oder git denkt, dass Sie angeben<Upstream> .

<Target>ist das Commit, an das wir unsere Reihe von Commits anhängen werden. Wenn Sie einen Zweigstellennamen angeben, geben Sie einfach das Head Commit dieses Zweigs an. <Target>kann ein beliebiges Commit sein, das nicht in der Zeichenfolge der zu verschiebenden Commits enthalten ist. Beispielsweise:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Um den gesamten Funktionszweig zu verschieben, können Sie nicht wählen X, Y, Zoder featurewie die<Target> da alle sind verpflichtet innerhalb der Gruppe bewegt werden.

<Upstream>ist etwas Besonderes, weil es zwei verschiedene Dinge bedeuten kann. Wenn es sich um ein Commit handelt, das ein Vorfahr des ausgecheckten Zweigs ist, dient es als Schnittpunkt. In dem Beispiel, das ich bereitgestellt habe, wäre dies alles, was nicht Cist D, oder master. Alle Commits danach, <Upstream>bis der Kopf des ausgecheckten Zweigs verschoben wird.

Wenn <Upstream>es sich jedoch nicht um einen Vorfahren handelt, sichert git die Kette vom angegebenen Commit, bis ein gemeinsamer Vorfahr mit dem ausgecheckten Zweig gefunden wird (und bricht ab, wenn er keinen findet). In unserem Fall ein <Upstream>von B, C, Doder masterwerden alle begehen Bals der Schnittpunkt dient. <Upstream>ist selbst ein optionaler Befehl, und wenn er nicht angegeben ist, betrachtet git das übergeordnete Element des ausgecheckten Zweigs, was der Eingabe entspricht master.

Nachdem git die Commits ausgewählt hat, die geschnitten und verschoben werden sollen, werden sie angewendet <Target>, um alle Commits zu überspringen, die bereits auf das Ziel angewendet wurden.

Interessante Beispiele und Ergebnisse

Verwenden Sie diesen Ausgangspunkt:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Gelten Commits B, C, X, Y, Zzu begehen Dund das Überspringen am Ende Bund Cweil sie bereits angewendet wurden.

  • git rebase --onto C X feature
    Wendet Commits an Yund Zlegt fest C, wobei Commit effektiv gelöscht wirdX

Isaac Brown
quelle
4

Eine einfachere Lösung ist git rebase <SHA1 of B> topic. Dies funktioniert unabhängig davon, wo Sie sich HEADbefinden.

Wir können dieses Verhalten anhand des Git-Rebase-Dokuments bestätigen

<upstream>Upstream-Zweig zum Vergleichen. Kann ein gültiges Commit sein , nicht nur ein vorhandener Filialname. Der Standardwert ist der konfigurierte Upstream für den aktuellen Zweig.


Sie denken vielleicht, was passieren wird, wenn ich topicim obigen Befehl auch SHA1 erwähne ?

git rebase <SHA1 of B> <SHA1 of topic>

Dies funktioniert auch, aber eine Neueinstellung macht dann keinen TopicSinn für einen so erstellten neuen Zweig und HEADbefindet sich im getrennten Zustand. Von hier aus müssen Sie also die alte manuell löschen Topicund eine neue Zweigreferenz für einen neuen Zweig erstellen, der durch Rebase erstellt wurde.

Nummer 945
quelle
3

Ich habe eine Mischung der 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 Zusammenführungskonflikt (zu faul, um von Hand behoben zu werden):

$ 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".
malat
quelle
1
Gleich hier hatten mehrere Dateien Konflikte, als ich die 2 beliebtesten Antworten verwendete (dh von r0hitsharma und Dymitruk)
Oliver
3

Da die Umbasierung so grundlegend ist, wird hier die Antwort von Nestor Milyaev erweitert . Die Kombination der Kommentare von jsz und Simon South aus der Antwort von Adam Dymitruk ergibt diesen Befehl, der für den topicZweig funktioniert, unabhängig davon, ob er vom masterCommit des Zweigs abzweigt Aoder C:

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 zum Festschreiben zurückgespult B ).

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 also der, den ich normalerweise benutze.

Zack Morris
quelle
-2

Es gibt eine andere Möglichkeit, dies zu tun, oder wenn Sie zu mehr als nur einem Commit zurückkehren möchten.

Hier ist ein Beispiel, um zur nAnzahl der Commits zurückzukehren:

git branch topic master~n

Um dieser Frage willen kann dies auch getan werden:

git branch topic master~1

Der Befehl funktioniert einwandfrei git version 2.7.4. Ich habe es auf keiner anderen Version getestet.

Talha Ashraf
quelle
Haben Sie dies als Frage zur Verzweigung falsch interpretiert? Dies ist eigentlich eine Frage zur Neugründung.
NetherGranite