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.
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.)
rebase -p
sowieso nicht verwenden )