Die meisten vorherigen Antworten sind gefährlich falsch!
Mach das nicht:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Wenn Sie das nächste Mal git rebase
(oder git pull --rebase
) diese 3 Commits ausführen, werden diese stillschweigend verworfen newbranch
! (siehe Erklärung unten)
Tun Sie stattdessen Folgendes:
git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
- Zuerst werden die 3 letzten Commits verworfen (
--keep
ist wie --hard
, aber sicherer, da es fehlschlägt, anstatt nicht festgeschriebene Änderungen wegzuwerfen).
- Dann gabelt es sich
newbranch
.
- Dann werden diese 3 Commits wieder ausgewählt
newbranch
. Da sie nicht durch einen Zweig mehr referenziert wird, tut es , daß durch Gits mit reflog : HEAD@{2}
ist der Commit , dass HEAD
vor 2 Operationen Bezug zu nehmen, das heißt , bevor wir 1 ausgecheckt newbranch
und 2 verwendet git reset
die 3 Commits zu verwerfen.
Warnung: Das Reflog ist standardmäßig aktiviert. Wenn Sie es jedoch manuell deaktiviert haben (z. B. mithilfe eines "nackten" Git-Repositorys), können Sie die 3 Commits nach dem Ausführen nicht zurückerhalten git reset --keep HEAD~3
.
Eine Alternative, die sich nicht auf das Reflog stützt, 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 es vorziehen, können Sie @{-1}
anstelle des zuvor ausgecheckten Zweigs schreiben oldbranch
).
Technische Erklärung
Warum sollten git rebase
die 3 Commits nach dem ersten Beispiel verworfen werden? Es ist , weil git rebase
ohne Argumente , die ermöglicht --fork-point
Option standardmäßig, die die lokale reflog verwendet , um zu versuchen , gegen den Upstream - Zweig den Zwang geschoben robust.
Angenommen, Sie haben Origin / Master abgezweigt, als er Commits M1, M2, M3 enthielt, und dann selbst drei Commits durchgeführt:
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
aber dann schreibt jemand die Geschichte neu, indem er origin / master zwangsweise drückt, um M2 zu entfernen:
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
Wenn Sie Ihr lokales Reflog verwenden, git rebase
können Sie feststellen, dass Sie sich von einer früheren Inkarnation des Ursprungs- / Hauptzweigs getrennt haben und daher die Commits M2 und M3 nicht wirklich Teil Ihres Themenzweigs sind. Daher wird davon ausgegangen, dass Sie M2, da es aus dem Upstream-Zweig entfernt wurde, auch nicht mehr in Ihrem Themenzweig haben möchten, sobald der Themenzweig neu basiert:
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
Dieses Verhalten ist sinnvoll und im Allgemeinen beim erneuten Basieren richtig.
Der Grund, warum die folgenden Befehle fehlschlagen:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
liegt daran, dass sie das Reflog im falschen Zustand belassen. Git sieht aus, newbranch
als hätte er den Upstream-Zweig bei einer Revision, die die 3 Commits enthält, abgebrochen. Anschließend wird der reset --hard
Upstream-Verlauf neu geschrieben, um die Commits zu entfernen. Wenn Sie git rebase
ihn das nächste Mal ausführen , werden sie wie alle anderen Commits verworfen, die aus dem Upstream entfernt wurden.
In diesem speziellen Fall möchten wir jedoch, dass diese drei Commits als Teil des Themenzweigs betrachtet werden. Um dies zu erreichen, müssen wir den Upstream bei der früheren Revision abzweigen, die die 3 Commits nicht enthält. Das ist, was meine vorgeschlagenen Lösungen tun, daher lassen beide das Reflog im richtigen Zustand.
Weitere Informationen finden Sie in der Definition von --fork-point
in den Dokumenten git rebase und git merge-base .