Die meisten bisherigen Antworten sind gefährlich falsch!
Tun Sie dies NICHT:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Wenn Sie das nächste Mal git rebase
又は git pull --rebase
) würden diese 3 Übertragungen stillschweigend aus 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 Übertragungen verworfen (
--keep
ist wie --hard
, aber sicherer, da es eher scheitert als unbestätigte Änderungen zu verwerfen).
- Dann gabelt sie sich
newbranch
.
- Dann wählt es diese 3 Commits zurück auf
newbranch
. Da sie nicht mehr durch einen Zweig referenziert werden, geschieht dies durch die Verwendung von git's reflog : HEAD@{2}
ist die Übergabe, die HEAD
vor 2 Operationen, d.h. bevor wir 1. ausgecheckt haben newbranch
und 2. verwendet git reset
um die 3 Commits zu verwerfen.
Warnung: Das Reflog ist standardmäßig aktiviert, aber wenn Sie es manuell deaktiviert haben (z.B. durch Verwendung eines "nackten" Git-Repositorys), werden Sie nicht in der Lage sein, die 3 Commits zurückzubekommen, nachdem Sie git reset --keep HEAD~3
.
Eine Alternative, die nicht auf das Reflog angewiesen ist, 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 schreiben @{-1}
- den zuvor ausgecheckten Zweig - anstelle von oldbranch
).
Technische Erklärung
Warum sollte git rebase
die 3 Übertragungen nach dem ersten Beispiel verwerfen? Es ist, weil git rebase
ohne Argumente ermöglicht die --fork-point
Option, die das lokale Reflog verwendet, um zu verhindern, dass der Upstream-Zweig gepusht wird.
Angenommen, Sie haben origin/master verzweigt, als es die Commits M1, M2 und M3 enthielt, und dann selbst drei Commits gemacht:
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
aber dann schreibt jemand die Geschichte um, indem er den Ursprung/Master zwingt, M2 zu entfernen:
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
Verwenden Sie Ihr lokales Reflog, git rebase
kann sehen, dass Sie sich von einer früheren Inkarnation des origin/master-Zweigs abgespalten haben, und daher die M2 und M3 Commits nicht wirklich Teil Ihres Themenzweigs sind. Daher wird vernünftigerweise davon ausgegangen, dass Sie M2 nicht mehr in Ihrem Themenzweig haben wollen, sobald der Themenzweig rebasiert ist, da er aus dem Upstream-Zweig entfernt wurde:
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
Dieses Verhalten ist sinnvoll und in der Regel auch richtig, wenn es um eine Umbasierung geht.
Das ist der Grund, warum die folgenden Befehle fehlschlagen:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
ist, weil sie das Reflog in einem falschen Zustand lassen. Git sieht newbranch
als vom Upstream-Zweig mit einer Revision abgezweigt, die die 3 Commits enthält, dann wird die reset --hard
schreibt die Upstream-Historie neu, um die Commits zu entfernen, so dass Sie beim nächsten Mal git rebase
verwirft er sie wie jeden anderen Commit, der aus dem Upstream entfernt wurde.
Aber in diesem speziellen Fall wollen wir, dass diese 3 Commits als Teil des Themenzweigs betrachtet werden. Um das zu erreichen, müssen wir den Upstream bei der früheren Revision abzweigen, die die 3 Commits nicht enthält. Das ist es, was meine vorgeschlagenen Lösungen tun, daher lassen sie beide das Reflog im richtigen Zustand.
Für weitere Einzelheiten siehe die Definition von --fork-point
im Git-Rebase et git merge-base docs.
161 Stimmen
Anmerkung: Ich habe die gegenteilige Frage gestellt aquí
6 Stimmen
eddmann.com/posts/ dieser funktioniert
22 Stimmen
Wurden die Kommentare hier gelöscht? Ich frage, weil ich bei meinem zweimonatlichen Besuch dieser Frage immer an diesem Kommentar vorbeilaufe.
5 Stimmen
Nebenbemerkung: In der Frage geht es um einen sehr einfachen Fall. Wenn ich die Antworten lese und all die "tu das nicht, weil..." und "eine bessere Lösung ist..." und "Warnung mit Version n+..." gleich nach den Antworten (möglicherweise wenn es zu spät ist), scheint es mir, dass selbst sehr einfache Operationen keine einfachen Lösungen in Git haben. Ein grafischer Versionsverwalter, bei dem man einfach einen Tag für den neuen Zweig hinzufügen könnte, ohne sich mit einer mir obskur und archaisch erscheinenden Syntax auseinandersetzen zu müssen, wäre eine große Erleichterung. Mein Königreich und meine goldenen Abzeichen für den ersten, der git "forkt" und einen neuen Ansatz startet ;-) es ist dringend.
0 Stimmen
@mins Vielleicht ist TortoiseGit ein Schritt näher an dem, was Sie wollen. Das Schöne an Git ist, dass man die volle Kontrolle hat, aber das erfordert ein tieferes Verständnis und die Notwendigkeit, dass der Benutzer mit einer Befehlszeilenschnittstelle vertraut ist. Ich verwende Git nun schon seit einigen Jahren, aber ich verlasse mich immer noch auf die Dokumentation und/oder Stackoverflow, wenn ich weniger häufige Operationen durchführen muss. Ich arbeite gerne mit der Git-Befehlszeilenschnittstelle (ich verwende GitBash), da ich damit Dinge schnell erledigen kann. Zur Lösung von Konflikten wende ich mich jedoch an TortoiseGit, da ich dies einfacher finde.
3 Stimmen
Lesen Sie sich die ersten zehn Antworten (oder so) durch, denn die besten sind nicht die, die am meisten hochgestuft wurden.