webentwicklung-frage-antwort-db.com.de

Warum gibt es bei git-rebase Zusammenführungskonflikte, wenn ich nur Commits quetsche?

Wir haben ein Git-Repository mit über 400 Commits, von denen die ersten paar Dutzend viel ausprobiert wurden. Wir wollen 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 sind nicht einfach zu lösen. Ich verstehe nicht, warum es überhaupt Konflikte geben sollte, da ich nur Commits quetsche (nicht lösche oder neu anordnete). 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). nicht funktionieren. Außerdem sollte ich darauf hinweisen, dass ich mithilfe des auskommentierten Codes in repo_squash_helper.sh die Originaldatei, an der das sed-Skript arbeitet, gespeichert und das sed-Skript darauf ausgeführt habe, um sicherzustellen, dass es das tat, was ich wollte ( es war). Auch hier weiß ich nicht einmal, warum es würde einen Konflikt geben, daher scheint es nicht so wichtig zu sein, welche Strategie verwendet wird. Irgendwelche Ratschläge oder Einsichten wären hilfreich, aber meistens möchte ich nur, dass das Quetschen funktioniert.

Aktualisiert mit zusätzlichen Informationen aus der Diskussion mit Jefromi:

Bevor ich an unserem massiven "echten" Repository gearbeitet habe, habe ich ähnliche Skripte in einem Test-Repository verwendet. Es war ein sehr einfaches Repository und der Test funktionierte einwandfrei.

Die Meldung, die ich erhalte, wenn es fehlschlägt, lautet:

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. Laufen git status ergibt ein sauberes Arbeitsverzeichnis. Wenn ich dann ein git rebase --continue, Nach ein paar weiteren Commits erhalte ich eine sehr ähnliche Meldung. Wenn ich es dann erneut mache, erhalte ich nach ein paar Dutzend Commits eine weitere sehr ähnliche Meldung. Wenn ich es noch einmal mache, durchläuft es dieses Mal ungefähr hundert Commits und gibt die folgende Meldung aus:

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 git status, Ich bekomme:

# 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 "beides 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" ansehe, er sich auf eine einzelne Zeile mit einer Version zusammensetzt, die 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 drin. (Ich habe bemerkt, dass core.ignorecase auf true gesetzt ist, aber offensichtlich hat git-clone das automatisch gemacht. Das überrascht mich nicht, wenn man bedenkt, dass sich die ursprüngliche Quelle auf einem Windows-Computer befindet.)

Wenn ich die Datei file_X.cpp manuell behebe, schlägt sie kurz darauf mit einem anderen Konflikt fehl, diesmal zwischen einer Datei (CMakeLists.txt), von der eine Version denkt, dass sie existieren sollte, und einer Version denkt, dass sie nicht existieren sollte. Wenn ich diesen Konflikt behebe, indem ich sage, ich möchte diese Datei (was ich auch tue), erhalte ich ein paar Commits später einen weiteren Konflikt (in derselben Datei), in dem es jetzt einige 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 eine Lerche (beeinflusst von Jefromis Kommentaren) habe ich beschlossen, die Änderung in repo_squash.sh vorzunehmen:

rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

Und dann habe ich einfach die ursprünglichen Einträge so wie sie sind akzeptiert. Das heißt, die "Rebase" sollte nichts geändert haben. Es endete mit den gleichen Ergebnissen, die zuvor beschrieben wurden.

Update Nr. 3:

Alternativ, wenn ich die Strategie auslasse und den letzten Befehl durch Folgendes ersetze:

git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a

Ich bekomme nicht mehr die Rebase-Probleme "nichts zu begehen", aber ich bin immer noch mit den anderen Konflikten überlassen.

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 [email protected]{7}
#========================================================

#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================

test_squash_helper.sh (verwendet von test_sqash.sh):

# 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

P.S .: Ja, ich weiß, dass einige von Ihnen zusammenzucken, wenn Sie sehen, dass ich Emacs als Fallback-Editor verwende.

P.P.S .: Wir wissen, dass wir alle unsere Klone des vorhandenen Repositorys nach dem Rebase wegblasen müssen. (Nach dem Motto "Du sollst ein Repository nach seiner Veröffentlichung nicht neu gründen".)

P.P.P.S: Kann mir jemand sagen, wie ich dem eine Prämie hinzufügen kann? Ich sehe die Option nirgendwo auf diesem Bildschirm, egal ob ich mich im Bearbeitungsmodus oder im Ansichtsmodus befinde.

120
Ben Hocking

Okay, ich bin zuversichtlich, eine Antwort herauszugeben. Vielleicht muss ich es bearbeiten, aber ich glaube, ich weiß, was dein Problem ist.

Ihr Spielzeug-Repo-Testfall weist eine Verschmelzung auf - schlimmer noch, es ist eine Verschmelzung mit Konflikten. Und Sie stürzen sich in die Fusion. Ohne -p (Was mit -i Nicht vollständig funktioniert) 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 zu wählen, sodass der Patch möglicherweise nicht angewendet wird. (Ich glaube, dies wird als Zusammenführungskonflikt angezeigt, da git cherry-pick Den Patch anwenden kann, indem eine Drei-Wege-Zusammenführung zwischen dem ursprünglichen Commit, dem aktuellen Commit und dem gemeinsamen Vorfahren durchgeführt wird.)

Wie wir in den Kommentaren bemerkt haben, kommen -i Und -p (Zusammenführungen beibehalten) leider 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 sehr viel komplexer ist, haben Sie möglicherweise große Probleme damit, das zu tun, was Sie wollen, obwohl dies immer noch möglich ist. (Moral der Geschichte: Räumen Sie mit rebase -i auf, bevor Sie verschmelzen.)

Nehmen wir also an, wir haben einen sehr einfachen Fall, in dem wir A, B und C zusammenpressen wollen:

- o - A - B - C - X - D - E - F (master)
   \             /
    Z -----------

Nun, wie gesagt, wenn es in X keine Konflikte gibt, funktioniert git rebase -i -p Wie erwartet.

Bei Konflikten wird es etwas kniffliger. 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, dem Index hinzufügen und dann mit git rebase --continue Weitermachen. (Natürlich können Sie sie erneut beheben, indem Sie die Version aus dem ursprünglichen Merge-Commit auschecken.)

Wenn Sie rerere in Ihrem Repo aktiviert haben (rerere.enabled Auf true gesetzt), ist dies viel einfacher - git kann re benutze die re schnurgebundene re Lösung von dem Zeitpunkt an, an dem du die Konflikte ursprünglich hattest und alles, was du hast Überprüfen Sie es, um sicherzustellen, dass es ordnungsgemäß funktioniert, fügen Sie die Dateien zum Index hinzu, und fahren Sie fort. (Sie können sogar einen Schritt weiter gehen, indem Sie rerere.autoupdate Aktivieren. Dadurch werden sie für Sie hinzugefügt, sodass 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. *

* Sie können auch das Skript rerere-train.sh von git-contrib verwenden, mit dem versucht wird, "die erneute Datenbank aus vorhandenen Zusammenführungs-Commits zu entschlüsseln" - im Grunde wird die gesamte Zusammenführung überprüft schreibt fest, versucht, sie zusammenzuführen, und schlägt die Zusammenführung fehl, werden die Ergebnisse erfasst und git-rerere angezeigt. Dies kann zeitaufwändig sein und ich habe es noch nie benutzt, aber es kann sehr hilfreich sein.

62
Cascabel

Wenn es Ihnen nichts ausmacht, einen neuen Zweig zu erstellen, habe ich das Problem folgendermaßen gelöst:

Meister sein:

# create a new branch
git checkout -b new_clean_branch

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
git reset --soft master        

git commit -m "Squashed changes from original_messy_branch"
50
hlidka

Ich habe nach einer ähnlichen Anforderung gesucht, d. H. Nach dem Verwerfen von Intermeiate-Commits meines Entwicklungszweigs. Ich habe festgestellt, dass dieses Verfahren bei mir funktioniert hat.
in meinem Arbeitszweig

git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”

viola wir haben das neueste commit mit dem start commit der branch verbunden! Keine Probleme mit Zusammenführungskonflikten! In meiner Lernpraxis bin ich zu diesem Schluss gekommen: Gibt es einen besseren Ansatz für diesen Zweck?.

4
user28186

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 lösen.

Für mich, git rebase -p -i master hat funktioniert. Es behielt das ursprüngliche Konfliktlösungs-Commit bei und erlaubte mir, die anderen an der Spitze zu quetschen.

Hoffe das hilft jemandem!

0
abaldwinhunter

Beachten Sie, dass -X Und Strategieoptionen bei der Verwendung in einer interaktiven Datenbank ignoriert wurden.

Siehe commit db2b3b820e2b28da268cc88adff076b396392dfe (Juli 2013, git 1.8.4+),

Ignorieren Sie die Zusammenführungsoptionen in der interaktiven Datenbank nicht

Die Zusammenführungsstrategie und ihre Optionen können in git rebase Angegeben werden, aber mit -- interactive Wurden sie vollständig ignoriert.

Unterzeichnet von: Arnaud Fontaine

Das bedeutet, dass -X Und Strategie jetzt sowohl mit interaktiver als auch mit einfacher Rebase funktionieren. Ihr anfängliches Skript könnte nun besser funktionieren.

0
VonC