Wie verwende ich 'git rebase -i', um alle Änderungen in einem Zweig neu zu gründen?

70

Hier ist ein Beispiel:

>git status
# On branch master
nothing to commit (working directory clean)
>git checkout -b test-branch
>vi test.c
>git add test.c
>git commit -m "modified test.c"
>vi README
>git add README
>git commit -m "modified README"

Jetzt möchte ich ein ' git rebase -i' machen, mit dem ich alle Commits für diesen Zweig neu festlegen kann. Gibt es so etwas wie ' git rebase -i HEAD~MASTER' oder ähnliches? Ich denke, ich könnte es tun git rebase -i HEAD~2, aber ich möchte wirklich nicht zählen müssen, wie viele Commits gemacht wurden. Ich könnte es auch tun, git rebase -i sha1aber ich möchte das Git-Protokoll nicht durchkämmen, um das erste Commit sha1 zu finden. Irgendwelche Ideen?

semmons99
quelle
1
Bitte benennen Sie Ihre Frage etwas besser. Erwähnen Sie vielleicht, dass Sie eine interaktive Basis für alle Änderungen in einem Zweig erstellen möchten. Am besten in Form einer Frage (wenn auch nicht immer möglich).
Dustin
Möchten Sie auf eine modifizierte Basis zurückgreifen masteroder nur die Commits bearbeiten, die Sie gerade vorgenommen haben test-branch?
DylanYoung

Antworten:

49

Haben Sie versucht : git rebase -i master?

ididak
quelle
20
Dies schlägt fehl, wenn der Master der aktuellen Zusammenführungsbasis in Ihrer Zweigstelle voraus ist.
Alex Brown
4
Das Problem dabei git rebase -i masterist, dass Sie möglicherweise Zusammenführungskonflikte haben, mit denen Sie sich im Moment nicht unbedingt befassen möchten, oder dass Sie einen Konflikt in einem Commit beheben, um ihn im Verlauf des Rebases erneut in einem anderen Commit zu beheben. Ich habe eine Antwort hinzugefügt, die eine Alternative dazu bietet, und eine Alternative zur Angabe des genauen Commits oder der Anzahl der Commits, von denen Sie zurückgreifen möchten.
Seth Flowers
rerereist dein Freund, wann immer du umbasierst.
DylanYoung
Funktioniert bei mir nicht, führt zu seltsamen Zusammenführungskonflikten, nachdem ich… buchstäblich nichts getan habe! Ich meine, alles was ich getan habe git rebase master (nicht -inur für Tests) , und es führt bereits zu Konflikten.
Hi-Angel
72

Ok, ich gehe davon aus, dass der Zweig "Feature" heißt und von "Master" verzweigt wurde.

Es gibt diesen kleinen Git-Befehl namens Merge-Base. Es dauert zwei Commits und gibt Ihnen den ersten gemeinsamen Vorfahren von beiden. Damit...

git merge-base feature master

... gibt Ihnen den ersten gemeinsamen Vorfahren dieser beiden Commits. Ratet mal, was passiert, wenn Sie dieses Commit für git rebase -i bestehen, wie ...

git rebase -i `git merge-base feature master`

Interaktive Rebase vom ersten gemeinsamen Vorfahren von Master und Feature-Zweig. Profitieren! ;)

DanNetwalker
quelle
2
Es ist allerdings hässlich - gibt es keinen syntaktischen Zucker?
Alex Brown
14
Das ist ziemlich im Vergleich zu vielen Git-Lösungen :).
Studgeek
6
Ich würde vorschlagen, zu verwenden, git merge-base master HEADwas immer für den aktuellen Zweig funktionieren sollte, ohne den aktuellen Zweignamen einzugeben. Alias ​​dieser Befehl und Sie haben Ihren netten kurzen Git-Befehl.
Jayeff
3
git rebase -i $(git merge-base @{u} HEAD) - Dies setzt voraus, dass Ihr aktueller Zweig so eingestellt ist, dass er den Basiszweig verfolgt. Beispiel: git branch feature1 origin/masterwürde Herkunft / Master verfolgen. Jetzt müssen Sie das nicht einmal mehr eingeben.
Alexander Bird
63

Das Problem bei allen bereitgestellten Lösungen ist, dass Sie nicht vom ersten Commit an neu starten können. Wenn der erste Commit-Hash XYZ ist und Sie Folgendes tun:

git rebase -i XYZ

Sie werden erst ab dem 2. Commit neu gestartet.

Wenn Sie vom ersten Commit zurücksetzen möchten, gehen Sie wie folgt vor:

git rebase -i --root
Joost den Boer
quelle
16
Aber "--root" basiert auf dem ersten Commit überhaupt und nicht auf dem ersten Commit in der Branche, was das OP verlangt
Chris B
21

Verwenden Sie gitk (* nix) oder gitx (OS X) oder ähnliches auf anderen Plattformen und sehen Sie sich an, welches Commit das Stammverzeichnis Ihres Zweigs war. Dann renne:

git rebase -i <the SHA hash of the root commit>

Zum Beispiel habe ich ein Repository, das ich mit gitx überprüft habe:

Gitx Screencap

Jetzt, wo ich den Root-Hash kenne, kann ich Folgendes ausführen:

git rebase -i 38965ed29d89a4136e47b688ca10b522b6bc335f

Und mein Editor erscheint damit und ich kann / squash / was auch immer nach Belieben neu anordnen.

pick 50b2cff File 1 changes.
pick 345df08 File 2 changes.
pick 9894931 File 3 changes.
pick 9a62b92 File 4 changes.
pick 640b1f8 File 5 changes.
pick 1c437f7 File 6 changes.
pick b014597 File 7 changes.
pick b1f52bc File 8 changes.
pick 40ae0fc File 9 changes.

# Rebase 38965ed..40ae0fc onto 38965ed
#
# Commands:
#  pick = use commit
#  edit = use commit, but stop for amending
#  squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Ich bin mir sicher, dass es eine magische Möglichkeit gibt, Git davon zu überzeugen, die Wurzel des Baumes automatisch herauszufinden, aber ich weiß nicht, was es ist.

EDIT: Diese Magie ist dies:

git log master..other_feature | cat

Dadurch werden alle Commits in diesem Zweig angezeigt, und durch Weiterleiten an cat wird der Pager deaktiviert, sodass das erste Commit sofort angezeigt wird.

EDIT: Die Kombination der oben genannten ergibt eine vollautomatische Lösung:

git rebase -i  `git log master..other_feature --pretty=format:"%h" | tail -n 1`~
Otto
quelle
1
| catscheint wie eine nutzlose Verwendung von Katze, wenn Sie verwenden können git --no-pager.
Matthieu Moy
@MatthieuMoy --no-pager war wahrscheinlich nicht in der Version, die ich im Dezember 2008 verwendet habe. Diese Option war erst im Juli 2008 in der got-Codebasis gelandet. Github.com/git/git/commit/…
Otto
@Otto Ah, ich habe das Datum Ihres Beitrags nicht bemerkt. Sie wollten eigentlich auf github.com/git/git/commit/… verweisen .
Matthieu Moy
6

Das Problem beim Rebasieren aus einem anderen Zweig

Das Problem dabei git rebase -i masterist, dass Sie möglicherweise Zusammenführungskonflikte haben, mit denen Sie sich im Moment nicht unbedingt befassen möchten, oder dass Sie einen Konflikt in einem Commit beheben, um ihn im Verlauf des Rebases erneut in einem anderen Commit zu beheben.

Das Problem beim erneuten Basieren von einem bekannten Commit

Das ganze Problem hierbei ist, dass Sie wissen müssen, auf welches Commit Sie sich beziehen müssen, entweder durch seine SHA oder HEAD ~ x usw. Dies ist nur ein kleiner Ärger, aber es ist ein Ärger.

Der bessere Weg

Wenn Sie stattdessen alle Commits in Ihrem aktuellen Zweig neu festlegen möchten, können Sie .gitconfig den folgenden Alias ​​hinzufügen, da der letzte Commit für den übergeordneten Zweig freigegeben wurde:

rbi = !sh -c \"git rebase -i `git merge-base $1 HEAD`\" -

Verwendung

git rbi parentBranch

Wie es funktioniert

Dieser Alias ​​ist nur ein Shell-Skript, das ein Argument verwendet, das auf den übergeordneten Zweig verweist. Dieses Argument wird übergeben git merge-base, um das letzte gemeinsame Commit zwischen diesem Zweig und dem aktuellen Zweig zu bestimmen.

Seth Blumen
quelle
Ich bin mir nicht sicher, ob diese Antwort bei der gegebenen Topologie überhaupt Sinn macht. Die Zusammenführungsbasis wird hier masterzum Zeitpunkt test-branchder Erstellung sein (sagen wir 3hgn45). Diese Rebase macht also eigentlich nichts. Es heißt, diesen Zweig von (aber nicht einschließlich) 3hgn45nach (und einschließlich) HEADauf neu zu gründen 3hgn45. Aber vielleicht
verstehe
EDIT: Ich verstehe; Die Frage des OP war mir einfach nicht klar. Sie können eine Warnung hinzufügen, die Änderungen nicht beibehält master(dh nicht wirklich neu basiert, da Sie dieselbe Basis beibehalten und nur einige Commits für den aktuellen Zweig bearbeiten).
DylanYoung
1
@DylanYoung - ja, ich habe die Frage des OP verstanden, dass sie eigentlich nicht darauf aufbauen wollten master, sondern einfach alle Commits zu einem zusammenfassen, da sie davon abweichen master.
Seth Flowers
Hier ist ein ähnlicher Alias ​​"Rebase Common Ancest", der zusätzliche Argumente zulässt:rbca = "!git rebase $(git merge-base HEAD \"$1\") ${@:2} #"
Cambunctious
4

Seit Git v1.7.10 können Sie nur git rebaseohne Argument ausführen , und es wird den Verzweigungspunkt finden und Ihre lokalen Änderungen im Upstream-Zweig neu begründen.

Sie müssen den Upstream-Zweig konfiguriert haben, damit dies funktioniert (dh git pullohne Argument sollte funktionieren).

Weitere Informationen finden Sie in den Dokumenten zur Git-Rebase :

Wenn nicht angegeben, werden die in den Optionen branch..remote und branch..merge konfigurierten Upstreams verwendet (Details siehe git-config [1] für Details) und die Option --fork-point wird angenommen. Wenn Sie sich derzeit nicht in einem Zweig befinden oder wenn der aktuelle Zweig keinen konfigurierten Upstream hat, wird die Rebase abgebrochen.

Matthieu Moy
quelle
2

Eine allgemeine Lösung (wenn Sie den Namen des Upstream-Zweigs nicht kennen) lautet:

git rebase -i @{upstream}

Beachten Sie, dass Sie neue Commits aus dem Upstream abrufen, wenn Ihr Upstream (wahrscheinlich ein Tracking-Zweig) seit Ihrer letzten Neugründung aktualisiert wurde. Wenn Sie keine neuen Commits einfügen möchten, verwenden Sie

git rebase -i `git merge-base --all HEAD @{upstream}`

aber das ist ein bisschen mundvoll.

Alex Brown
quelle
Ich habe Vorschläge gesehen, dass der symmetrische Differenz-HEAD ... -Master (der die Zusammenführungsbasis als negiertes drittes sha ergibt) in der Git-Rebase verwendet werden kann, aber ich kann nicht herausfinden, wie.
Alex Brown
1
git rebase -i --onto @{u}... @{u}

Interaktives Rebase ausgehend vom einzelnen Zusammenführungspunkt von HEAD und seinem Upstream, einschließlich aller Commits in HEAD, die sich nicht in seinem Upstream befinden.

Mit anderen Worten genau das, was Sie wollen.

bbex
quelle
Aber wenn @{u}konfiguriert, dann können Sie argumentless verwenden git rebase, siehe meine Antwort.
Matthieu Moy