Wir haben ein Git-Repository mit über 400 Commits, von denen das erste Dutzend viel Versuch und Irrtum war. Wir möchten diese Commits bereinigen, indem wir viele zu einem einzigen Commit zusammenfassen. Natürlich scheint Git-Rebase der richtige Weg zu sein. Mein Problem ist, dass es zu Zusammenführungskonflikten kommt und diese Konflikte nicht einfach zu lösen sind. Ich verstehe nicht, warum es überhaupt Konflikte geben sollte, da ich nur Commits quetsche (nicht lösche oder neu anordne). Sehr wahrscheinlich zeigt dies, dass ich nicht ganz verstehe, wie Git-Rebase seine Kürbisse macht.
Hier ist eine modifizierte Version der Skripte, die ich verwende:
repo_squash.sh (dies ist das Skript, das tatsächlich ausgeführt wird):
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
repo_squash_helper.sh (dieses Skript wird nur von repo_squash.sh verwendet):
if grep -q "pick " $1
then
# cp $1 ../repo_squash_history.txt
# emacs -nw $1
sed -f ../repo_squash_list.txt < $1 > $1.tmp
mv $1.tmp $1
else
if grep -q "initial import" $1
then
cp ../repo_squash_new_message1.txt $1
elif grep -q "fixing bad import" $1
then
cp ../repo_squash_new_message2.txt $1
else
emacs -nw $1
fi
fi
repo_squash_list.txt: (Diese Datei wird nur von repo_squash_helper.sh verwendet.)
# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g
Ich überlasse den Inhalt der "neuen Nachricht" Ihrer Fantasie. Anfangs habe ich dies ohne die Option "--strategy Theirs" getan (dh mit der Standardstrategie, die, wenn ich die Dokumentation richtig verstehe, rekursiv ist, aber ich bin nicht sicher, welche rekursive Strategie verwendet wird), und es hat auch nicht funktioniert. t arbeiten. Außerdem sollte ich darauf hinweisen, dass ich mit dem auskommentierten Code in repo_squash_helper.sh die Originaldatei, an der das sed-Skript arbeitet, gespeichert und das sed-Skript dagegen ausgeführt habe, um sicherzustellen, dass es das tut, was ich wollte ( es war). Auch hier weiß ich nicht einmal , warum es würde ein Konflikt sein, so wäre es nicht so viel Rolle zu spielen, welche Strategie verwendet wird. Jeder Rat oder jede Einsicht wäre hilfreich, aber meistens möchte ich nur, dass dieses Quetschen funktioniert.
Aktualisiert mit zusätzlichen Informationen aus der Diskussion mit Jefromi:
Bevor ich an unserem riesigen "echten" Repository gearbeitet habe, habe ich ähnliche Skripte für ein Test-Repository verwendet. Es war ein sehr einfaches Repository und der Test funktionierte sauber.
Die Nachricht, die ich bekomme, wenn es fehlschlägt, ist:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
Dies ist die erste Auswahl nach dem ersten Squash-Commit. Das Ausführen git status
ergibt ein sauberes Arbeitsverzeichnis. Wenn ich dann eine mache git rebase --continue
, erhalte ich nach ein paar weiteren Commits eine sehr ähnliche Nachricht. Wenn ich es dann noch einmal mache, erhalte ich nach ein paar Dutzend Commits eine weitere sehr ähnliche Nachricht. Wenn ich es noch einmal mache, durchläuft es diesmal ungefähr hundert Commits und liefert die folgende Meldung:
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental
Wenn ich dann renne git status
, bekomme ich:
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: repo/file_A.cpp
# modified: repo/file_B.cpp
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: repo/file_X.cpp
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: repo/file_Z.imp
Das "beide modifizierte" Bit klingt für mich komisch, da dies nur das Ergebnis einer Auswahl war. Es ist auch erwähnenswert, dass wenn ich mir den "Konflikt" anschaue, er sich auf eine einzelne Zeile beschränkt, wobei eine Version mit einem [Tab] -Zeichen beginnt und die andere mit vier Leerzeichen. Das klang, als wäre es ein Problem mit der Einrichtung meiner Konfigurationsdatei, aber es ist nichts dergleichen darin. (Ich habe bemerkt, dass core.ignorecase auf true gesetzt ist, aber offensichtlich hat git-clone dies automatisch getan. Ich bin nicht völlig überrascht, wenn man bedenkt, dass sich die ursprüngliche Quelle auf einem Windows-Computer befand.)
Wenn ich file_X.cpp manuell behebe, schlägt dies kurz darauf mit einem weiteren Konflikt fehl, diesmal zwischen einer Datei (CMakeLists.txt), von der eine Version glaubt, dass sie existieren sollte, und einer Version, die nicht denken sollte. Wenn ich diesen Konflikt behebe, indem ich sage, dass ich diese Datei möchte (was ich auch tue), erhalte ich einige Commits später einen weiteren Konflikt (in derselben Datei), bei dem es jetzt einige eher nicht triviale Änderungen gibt. Es sind immer noch nur etwa 25% des Weges durch die Konflikte.
Da dies sehr wichtig sein könnte, sollte ich auch darauf hinweisen, dass dieses Projekt in einem SVN-Repository gestartet wurde. Diese anfängliche Historie wurde sehr wahrscheinlich aus diesem SVN-Repository importiert.
Update Nr. 2:
Auf einer Lerche (beeinflusst von Jefromis Kommentaren) habe ich beschlossen, die Änderung meiner repo_squash.sh wie folgt vorzunehmen:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Und dann habe ich die ursprünglichen Einträge so wie sie sind akzeptiert. Das heißt, die "Rebase" hätte nichts ändern sollen. Es endete mit den gleichen Ergebnissen, die zuvor beschrieben wurden.
Update Nr. 3:
Alternativ, wenn ich die Strategie weglasse und den letzten Befehl durch Folgendes ersetze:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Ich bekomme nicht mehr die Rebase-Probleme "nichts zu begehen", aber ich habe immer noch die anderen Konflikte.
Update mit Spielzeug-Repository, das das Problem neu erstellt:
test_squash.sh (dies ist die Datei, die Sie tatsächlich ausführen):
#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================
#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..
#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================
#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================
#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================
test_squash_helper.sh (wird von test_sqash.sh verwendet):
# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" > $1
fi
PS: Ja, ich weiß, dass einige von Ihnen zusammenzucken, wenn Sie sehen, dass ich Emacs als Fallback-Editor verwende.
PPS: Wir wissen, dass wir nach der Rebase alle unsere Klone des vorhandenen Repositorys wegblasen müssen. (Nach dem Motto "Du sollst ein Repository nicht neu starten, nachdem es veröffentlicht wurde".)
PPPS: Kann mir jemand sagen, wie ich dem ein Kopfgeld hinzufügen kann? Ich sehe die Option nirgendwo auf diesem Bildschirm, egal ob ich mich im Bearbeitungsmodus oder im Ansichtsmodus befinde.
quelle
rebase -p
sowieso nicht verwenden )rebase --interactive
- das ist eine Art Liste von Aktionen, die Git versuchen soll. Ich hatte gehofft, Sie könnten dies auf einen einzigen Squash reduzieren, der Konflikte verursacht, und die zusätzliche Komplexität Ihrer Hilfsskripte vermeiden. Die andere fehlende Information ist, wann die Konflikte auftreten - wann Git die Patches anwendet, um den Squash zu bilden, oder wenn es versucht, über den Squash hinauszugehen und den nächsten Patch anzuwenden? (Und sind Sie sicher, dass mit Ihrem GIT_EDITOR-Kludge nichts Schlimmes passiert? Eine weitere Abstimmung für einen einfachen Testfall.)Antworten:
In Ordnung, ich bin zuversichtlich genug, eine Antwort herauszugeben. Vielleicht muss es bearbeitet werden, aber ich glaube, ich weiß, was Ihr Problem ist.
Ihr Toy Repo-Testfall enthält eine Zusammenführung - schlimmer noch, es gibt eine Zusammenführung mit Konflikten. Und Sie stützen sich auf die Fusion. Ohne
-p
(was nicht ganz funktioniert-i
) werden die Zusammenführungen ignoriert. Dies bedeutet, dass alles, was Sie in Ihrer Konfliktlösung getan haben, nicht vorhanden ist, wenn die Rebase versucht, das nächste Commit auszuwählen, sodass der Patch möglicherweise nicht angewendet wird. (Ich glaube, dies wird als Zusammenführungskonflikt angezeigt, dagit cherry-pick
der Patch angewendet werden kann, indem eine dreifache Zusammenführung zwischen dem ursprünglichen Commit, dem aktuellen Commit und dem gemeinsamen Vorfahren durchgeführt wird.)Leider kommen wir, wie wir in den Kommentaren bemerkt haben,
-i
und-p
(Zusammenführungen beibehalten) nicht sehr gut miteinander aus. Ich weiß, dass das Bearbeiten / Umformulieren funktioniert und das Umordnen nicht. Ich glaube jedoch, dass es gut mit Kürbissen funktioniert. Dies ist nicht dokumentiert, hat aber für die unten beschriebenen Testfälle funktioniert. Wenn Ihr Fall viel, viel komplexer ist, haben Sie möglicherweise große Probleme, das zu tun, was Sie wollen, obwohl dies immer noch möglich ist. (Moral der Geschichte: Aufräumenrebase -i
vor dem Zusammenführen.)Nehmen wir also an, wir haben einen sehr einfachen Fall, in dem wir A, B und C zusammenpressen wollen:
Nun, wie gesagt, wenn es in X keine Konflikte gab,
git rebase -i -p
funktioniert es wie erwartet.Wenn es Konflikte gibt, wird es etwas schwieriger. Es wird gut gequetscht, aber wenn es dann versucht, die Zusammenführung wiederherzustellen, werden die Konflikte erneut auftreten. Sie müssen sie erneut auflösen, zum Index hinzufügen und dann
git rebase --continue
zum Fortfahren verwenden. (Natürlich können Sie sie erneut beheben, indem Sie die Version aus dem ursprünglichen Zusammenführungs-Commit auschecken.)Wenn Sie haben , passieren
rerere
in Ihrem Repo (enabledrerere.enabled
auf true eingestellt), wird auf diese Weise einfacher sein - git Lage sein wird, wieder die Verwendung re Corded re Lösung aus , als Sie die Konflikte hatten, und alle müssen Sie tun , ist es inspizieren Um sicherzustellen, dass es richtig funktioniert, fügen Sie die Dateien zum Index hinzu und fahren Sie fort. (Sie können sogar einen Schritt weiter gehen und sich einschaltenrerere.autoupdate
, und es werden sie für Sie hinzugefügt, damit die Zusammenführung nicht einmal fehlschlägt.) Ich vermute jedoch, dass Sie Rerere nie aktiviert haben, sodass Sie die Konfliktlösung selbst durchführen müssen. ** Oder Sie können das
rerere-train.sh
Skript von git-contrib ausprobieren , das versucht, "die Datenbank aus vorhandenen Zusammenführungs-Commits neu zu sortieren" - im Grunde werden alle Zusammenführungs-Commits überprüft, versucht, sie zusammenzuführen, und wenn die Zusammenführung fehlschlägt, es erfasst die Ergebnisse und zeigt sie angit-rerere
. Dies kann zeitaufwändig sein und ich habe es nie benutzt, aber es könnte sehr hilfreich sein.quelle
-p
Option nicht angegeben haben.git commit -a -m"Some message"
und eingeben kanngit rebase --continue
und es weitergeht. Dies funktioniert auch ohne die-p
Option, aber es funktioniert noch besser mit der-p
Option (da ich keine Nachbestellung mache, scheint das-p
in Ordnung zu sein). Wie auch immer, ich werde dich auf dem Laufenden halten.git config --global rerere.enabled true
undgit config --global rerere.autoupdate true
vor dem Ausführen des Testbeispiels wird das Hauptproblem behoben. Interessanterweise werden die Zusammenführungen jedoch nicht beibehalten, selbst wenn sie angegeben werden--preserve-merges
. Wenn ich diese jedoch nicht festgelegt habegit commit -a -m"Meaningful commit"
undgit rebase --continue
während der Angabe tippe--preserve-merges
, bleiben die Zusammenführungen erhalten. Wie auch immer, danke, dass du mir geholfen hast, dieses Problem zu überwinden!Wenn es Ihnen nichts ausmacht, einen neuen Zweig zu erstellen, habe ich das Problem folgendermaßen behandelt:
Auf der Hauptstraße sein:
quelle
git diff new_clean_branch..original_messy_branch
sollten Sie gleich sein? Für mich sind sie anders.Ich suchte nach einer ähnlichen Anforderung, dh dem Verwerfen von Zwischenverpflichtungen meines Entwicklungszweigs. Ich habe festgestellt, dass dieses Verfahren für mich funktioniert.
auf meinem Arbeitszweig
Viola Wir haben das letzte Commit mit dem Start-Commit der Filiale verbunden! Keine Probleme beim Zusammenführen von Konflikten! In meiner Lernpraxis bin ich zu diesem Zeitpunkt zu dem Schluss gekommen, ob es einen besseren Ansatz für diesen Zweck gibt.
quelle
Aufbauend auf der obigen großartigen Antwort von @ hlidka , die manuelle Eingriffe minimiert, wollte ich eine Version hinzufügen, die alle neuen Commits auf dem Master beibehält , die nicht in der Verzweigung zum Squash sind.
Wie ich glaube, könnten diese in diesem
git reset
Schritt leicht verloren gehen .quelle
Beachten Sie, dass
-X
und Strategieoptionen bei Verwendung in einer interaktiven Rebase ignoriert wurden.Siehe Commit db2b3b820e2b28da268cc88adff076b396392dfe (Juli 2013, Git 1.8.4+),
Das bedeutet, dass
-X
Strategie und Strategie jetzt sowohl mit interaktiver als auch mit einfacher Rebase funktionieren und Ihr ursprüngliches Skript jetzt besser funktionieren könnte.quelle
Ich stieß auf ein einfacheres, aber ähnliches Problem, bei dem ich 1) einen Zusammenführungskonflikt in einem lokalen Zweig gelöst hatte, 2) weiter daran arbeitete, viel mehr kleine Commits hinzuzufügen, 3) Zusammenführungskonflikte neu zu definieren und zu treffen.
Für mich hat
git rebase -p -i master
gearbeitet. Es behielt die ursprüngliche Verpflichtung zur Konfliktlösung bei und erlaubte mir, die anderen an der Spitze zu zerquetschen.Hoffe das hilft jemandem!
quelle