Frage Verschieben Sie die letzten Commits in einen neuen Zweig mit Git


Ich möchte die letzten paar Commits, die ich zu Master gemacht habe, in einen neuen Zweig verschieben und den Master zurückholen, bevor diese Commits gemacht wurden. Leider ist mein Git-Fu noch nicht stark genug, keine Hilfe?

I.e. Wie kann ich davon ausgehen?

master A - B - C - D - E

dazu?

newbranch     C - D - E
             /
master A - B 

3753
2017-10-27 03:07


Ursprung


Antworten:


Umzug in eine neue Filiale

WARNUNG: Diese Methode funktioniert, weil Sie mit dem ersten Befehl eine neue Verzweigung erstellen: git branch newbranch. Wenn Sie Commits zu einem verschieben möchten bestehende Filiale Sie müssen Ihre Änderungen vor der Ausführung in den vorhandenen Zweig zusammenführen git reset --hard HEAD~3 (sehen Zu einem bestehenden Zweig wechseln unten). Wenn Sie Ihre Änderungen nicht zuerst zusammenführen, gehen sie verloren.

Sofern keine anderen Umstände vorliegen, kann dies leicht durch Verzweigen und Zurückrollen erfolgen.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Aber stellen Sie sicher, wie viele Commits zurückgehen. Alternativ können Sie statt HEAD~3, geben Sie einfach den Hash des Commits (oder die Referenz wie Herkunft / Meister) Sie möchten "zurück zu" auf der Meister (/ aktueller) Zweig, z.B.

git reset --hard a1b2c3d4

* 1 wirst du nur "Verliere" Commits vom Master-Zweig, aber mach dir keine Sorgen, du wirst diese Commits in newbranch haben!

WARNUNG: Mit Git Version 2.0 und später, wenn Sie später git rebase der neue Zweig auf das Original (master) Zweig, Sie benötigen möglicherweise eine explizite --no-fork-point Option während der Rebase, um den Verlust der übertragenen Commits zu vermeiden. Haben branch.autosetuprebase always Set macht dies wahrscheinlicher. Sehen John Mellors Antwort für Details.

Zu einem bestehenden Zweig wechseln

Wenn Sie Ihre Commits zu einem verschieben möchten bestehende FilialeEs sieht so aus:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

4778
2017-07-22 22:37



Für diejenigen, die sich fragen, warum es funktioniert (wie ich es zuerst war):

Sie möchten zu C zurückkehren und D und E in den neuen Zweig verschieben. So sieht es zunächst aus:

A-B-C-D-E (HEAD)
        ↑
      master

Nach git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Nach git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Da ein Zweig nur ein Zeiger ist, Meister wies auf das letzte Commit hin. Als du gemacht hast Neue Abteilung, Sie haben einfach einen neuen Zeiger auf das letzte Commit gesetzt. Dann mit git reset du hast das bewegt Meister Zeiger zurück zwei Commits. Aber da du dich nicht bewegt hast Neue Abteilung, es zeigt immer noch auf das Commit, das es ursprünglich getan hat.


846
2018-02-07 16:58



Im Algemeinen...

Die von sykora zur Verfügung gestellte Methode ist in diesem Fall die beste Option. Aber manchmal ist nicht die einfachste und es ist keine allgemeine Methode. Für eine allgemeine Methode verwenden Git Kirschen-Pick:

Um zu erreichen, was OP will, ist es ein 2-Stufen-Prozess:

Schritt 1 - Beachten Sie, welche Commits von Master Sie auf einem möchten newbranch

Ausführen

git checkout master
git log

Beachten Sie die Hashes von (sagen wir 3) Commits, die Sie wollen newbranch. Hier werde ich verwenden:
C commit: 9aa1233
D begehen: 453ac3d
E verpflichten: 612ecb3 

Hinweis: Sie können die ersten sieben Zeichen oder verwenden   der ganze Commit Hash

Schritt 2 - Legen Sie sie auf die newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

ODER (auf Git 1.7.2+, verwenden Sie Bereiche)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

Git Kirschen-Pick wendet diese drei Commits auf newbranch an.


342
2018-03-26 08:13



Noch eine andere Möglichkeit, dies zu tun, mit nur 2 Befehlen. Hält auch Ihren aktuellen Arbeitsbaum intakt.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Alte Version - Bevor ich davon erfahren habe git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Fähig sein zu push zu . ist ein netter Trick zu wissen.


251
2018-04-06 22:38



Die meisten vorherigen Antworten sind gefährlich falsch!

Mach das nicht:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Beim nächsten Mal rennst du git rebase (oder git pull --rebase) Diese 3 Commits würden stillschweigend verworfen werden newbranch! (siehe Erklärung unten)

Tun Sie das stattdessen:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Zuerst verwirft es die 3 letzten Commits (--keep ist wie --hard, aber sicherer, als fehlschlägt, anstatt nicht festgeschriebene Änderungen wegzuwerfen).
  • Dann geht es weg newbranch.
  • Dann greift er diese 3 Commits zurück auf newbranch. Da sie nicht mehr von einer Verzweigung referenziert werden, geschieht dies mithilfe von Git reflog: HEAD@{2} ist das Commit das HEAD verwendet, um sich auf 2 Operationen zu beziehen, d. h. bevor wir 1. ausgecheckt haben newbranch und 2. verwendet git reset die 3 Commits verwerfen.

Warnung: Der Reflog ist standardmäßig aktiviert, aber wenn Sie ihn manuell deaktiviert haben (z. B. indem Sie ein "blankes" Git-Repository verwenden), können Sie die 3 Commits nach dem Ausführen nicht wiederherstellen git reset --keep HEAD~3.

Eine Alternative, die nicht auf dem Reflog beruht, ist:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(Wenn Sie möchten, können Sie schreiben @{-1} - der zuvor ausgecheckte Zweig - anstelle von oldbranch).


Technische Erklärung

Warum würde git rebase Verwerfen Sie die 3 Commits nach dem ersten Beispiel? Es ist, weil git rebase ohne Argumente ermöglicht das --fork-point Diese Option verwendet standardmäßig den lokalen Reflog, um zu versuchen, robust gegen den Force-Push des Upstream-Zweigs zu sein.

Angenommen, Sie haben den Ursprung / Master abgezweigt, wenn er die Commits M1, M2, M3 enthielt, und dann selbst drei Commits gemacht:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

aber dann schreibt jemand die Geschichte neu, indem er den Ursprung / Meister dazu zwingt, M2 zu entfernen:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Mit Ihrem lokalen Reflog git rebase kann sehen, dass Sie sich von einer früheren Inkarnation des Ursprungs / Master-Zweiges gespalten haben, und daher sind die M2- und M3-Commits nicht wirklich Teil Ihres Themenzweiges. Daher wird vernünftigerweise davon ausgegangen, dass M2 seit dem Entfernen aus dem Upstream-Zweig nicht mehr in Ihrem Zweigzweig sein soll, nachdem der Zweig des Zweigzweigs neugestoßen wurde:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Dieses Verhalten ist sinnvoll und im Allgemeinen das richtige Vorgehen beim Rebasieren.

Also der Grund, warum die folgenden Befehle fehlschlagen:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

liegt daran, dass sie den Reflog im falschen Zustand belassen. Git sieht newbranch so wie man den Upstream - Zweig bei einer Revision, die die 3 Commits beinhaltet, abgespalten hat, dann wird der reset --hard schreibt den Verlauf des Upstreams neu, um die Commits zu entfernen, und so beim nächsten Mal git rebase Es verwirft sie wie jedes andere Commit, das aus dem Upstream entfernt wurde.

Aber in diesem speziellen Fall wollen wir, dass diese 3 Commits als Teil des Themenzweiges betrachtet werden. Um dies zu erreichen, müssen wir den Upstream bei der früheren Revision, die die 3 Commits nicht enthält, abzweigen. Das ist, was meine vorgeschlagenen Lösungen tun, daher lassen beide den Reflog im richtigen Zustand.

Weitere Informationen finden Sie in der Definition von --fork-point in dem Git Rebase und Git Merge-BasisDokumente


209
2017-10-19 14:12



Dies "bewegt" sie nicht im technischen Sinn, aber es hat denselben Effekt:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

27
2018-01-21 16:10



Um dies zu tun, ohne den Verlauf neu zu schreiben (d. H. Wenn Sie die Commits bereits gedrückt haben):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Beide Zweige können dann ohne Kraftaufwand geschoben werden!


19
2017-09-20 10:17



Hatte genau diese Situation:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

Ich führte auf:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Ich habe damit gerechnet, dass ich der HEAD wäre, aber begib L es jetzt ...

Um sicher zu sein, an der richtigen Stelle in der Geschichte zu landen, ist es einfacher mit dem Hash des Commits zu arbeiten

git branch newbranch 
git reset --hard #########
git checkout newbranch

12
2018-05-01 07:50



Viel einfachere Lösung

Ob:

  1. Ihr Hauptzweck ist ein Rollback master, und
  2. Sie möchten Änderungen beibehalten, aber kümmern sich nicht besonders um die individuelle Commits, und
  3. Du hast noch nicht gedrängt,

Dann ist das Folgende viel einfacher:

git reset HEAD~3
git stash
git checkout otherbranch
git stash pop

Was das bedeutet:

  1. Macht die letzten drei Commits (und ihre Nachrichten) rückgängig, lässt aber alle Arbeitsdateien intakt
  2. Stashes alle Änderungen der Arbeitsdatei, wodurch die master Arbeitsbaum gleich dem HEAD ~ 3 Zustand.
  3. Zu einem bestehenden Zweig wechseln otherbranch
  4. Zieht die geänderten Dateien aus dem Stash und in Ihr Arbeitsverzeichnis.

Sie können jetzt verwenden git add und git commit wie du normalerweise würdest. Alle Änderungen werden übernommen otherbranch.

Dadurch werden die fehlerhaften Commits und Commit-Nachrichten nicht beibehalten. Sie müssen diesem neuen Commit eine neue Commit-Nachricht hinzufügen und Ihre früheren Commit-Nachrichten erneut erstellen. Das OP erklärte jedoch, das Ziel bestehe darin, "den Master zurück zu nehmen, bevor diese Commits gemacht wurden", ohne Änderungen zu verlieren, und diese Lösung macht das auch.

Ich mache das mindestens einmal pro Woche, wenn ich versehentlich neue Commits mache master Anstatt von develop. Normalerweise habe ich nur einen Commit zum Rollback in diesem Fall git reset HEAD^ funktioniert, um nur einen Commit zurückzusetzen.


6
2018-05-25 14:14