stackoverflow - git merge strategy




Wie kann ich git anweisen, immer meine lokale Version für Konfliktkonstellationen in einer bestimmten Datei auszuwählen? (2)

Angenommen, ich arbeite mit jemandem über ein Git-Repository zusammen, und es gibt eine bestimmte Datei, die ich niemals akzeptieren möchte.

Gibt es irgendeine Möglichkeit für mich, mein lokales Repo einzurichten, um nicht jedes Mal, wenn ich losziehe, über eine Konfliktverschmelzung zu klagen? Ich möchte beim Zusammenführen dieser Datei immer meine lokale Version auswählen.


In der spezifischen Instanz einer Konfigurationsdatei würde ich Rons Antwort zustimmen:
Eine Konfiguration sollte für Ihren Arbeitsbereich "privat" sein (daher "ignoriert", wie in "deklariert in einer .gitignore Datei").
Sie haben möglicherweise eine Konfigurationsdateivorlage mit darin enthaltenen config.template und ein Skript, das config.template Datei config.template in eine private (und ignorierte) Konfigurationsdatei config.template .

Diese spezifische Bemerkung beantwortet jedoch nicht die allgemeinere Frage, dh Ihre Frage (!):

Wie kann ich git anweisen, immer meine lokale Version für Konfliktkonstellationen in einer bestimmten Datei auszuwählen? (für jede Datei oder Dateigruppe)

Bei dieser Art der Zusammenführung handelt es sich um eine "Kopierzusammenführung", bei der Sie bei Konflikten immer "unsere" oder "ihre" Version einer Datei kopieren.

(Wie Brian Vandenberg in den Kommentaren bemerkt , werden " ours " und " theirs " hier für eine Zusammenführung verwendet .
Sie werden für eine Rebase umgedreht : siehe " Why is the meaning of “ours” and “theirs” reversed with git-svn ", die eine Rebase verwendet, " git rebase , die" local "und" remote "verfolgt)

Für "eine Datei" (eine Datei im Allgemeinen, die nicht von einer "config" -Datei spricht, da es ein schlechtes Beispiel ist), würden Sie dies mit einem benutzerdefinierten Skript erreichen, das durch Zusammenführungen aufgerufen wird.
Git wird dieses Skript aufrufen, da Sie einen gitattributes- Wert definiert haben , der einen benutzerdefinierten Merge-Treiber definiert .

Der "custom merge driver" ist in diesem Fall ein sehr einfaches Skript, das die aktuelle Version grundsätzlich unverändert belässt, so dass Sie immer Ihre lokale Version auswählen können.

Lassen Sie uns das in einem einfachen Szenario mit einem msysgit 1.6.3 unter Windows in einer DOS-Sitzung testen:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Nun, machen wir zwei Dateien, die beide Konflikte haben werden, die aber unterschiedlich zusammengeführt werden.

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

Wir werden einen "Konflikt" im Inhalt dieser beiden Dateien in zwei verschiedenen Git-Zweigen einführen:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Versuchen wir nun, "hisBranch" auf "myBranch" zu verschmelzen mit:

  • manuelle Auflösung für widersprüchliche Zusammenführungen
  • außer dirWithCopyMerge\b.txt wo ich immer meine Version von b.txt behalten b.txt .

Da die Zusammenführung in ' MyBranch ' erfolgt, werden wir darauf zurückschalten und die Anweisungen ' gitattributes ' hinzufügen, die das Zusammenführungsverhalten anpassen.

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

Wir haben eine .gitattributes Datei, die im dirWithCopyMerge Verzeichnis definiert ist (nur in dem Zweig definiert, in dem die Zusammenführung stattfinden wird: myBranch ), und wir haben eine .git\config Datei, die jetzt einen Zusammenführungs-Treiber enthält.

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

Wenn Sie keepMine.sh noch nicht definieren und die Zusammenführung trotzdem starten, erhalten Sie Folgendes:

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

Das ist gut:

  • a.txt ist bereit, zusammengeführt zu werden und hat einen Konflikt darin
  • b.txt ist immer noch unberührt, da der Merge-Treiber dafür sorgen soll (aufgrund der Anweisung in der .gitattributes Datei in seinem Verzeichnis).

Definieren Sie eine keepMine.sh irgendwo in Ihrem %PATH% (oder $PATH für unseren Unix-Freund. Ich mache natürlich beides: Ich habe eine Ubuntu-Sitzung in einer VirtualBox-Sitzung)

Wie von lrkwz commented und im Abschnitt " Merge-Strategien " von Git-Git-Attribute im Customizing beschrieben , können Sie das Shell-Skript durch den Shell-Befehl true ersetzen.

git config merge.keepMine.driver true

Aber im allgemeinen Fall können Sie eine Skriptdatei definieren:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(das war ein einfacher Merge-Treiber;) (Noch einfacher in diesem Fall, true )
(Wenn Sie die andere Version beibehalten möchten, fügen Sie einfach vor der Zeile exit 0 hinzu:
cp -f $3 $2 .
Das ist es. Ihr Merge-Treiber würde die Version immer von der anderen Verzweigung halten und jede lokale Änderung außer Kraft setzen.

Jetzt versuchen wir die Zusammenführung von Anfang an:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

Die Zusammenführung schlägt fehl ... nur für a.txt .
Bearbeiten Sie a.txt und belassen Sie die Zeile von 'hisBranch', dann:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Überprüfen wir, ob b.txt während dieser Zusammenführung erhalten geblieben ist

type dirWithCopyMerge\b.txt
b
myLineForB

Das letzte Commit repräsentiert die vollständige Zusammenführung:

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(Die mit Merge beginnende Zeile beweist das)

Beachten Sie, dass Sie den Merge-Treiber definieren, kombinieren und / oder überschreiben können, da Git

  • Überprüfen Sie <dir>/.gitattributes (das sich im selben Verzeichnis wie der fragliche Pfad befindet): wird Vorrang vor den anderen .gitattributes in Verzeichnissen haben
  • Dann überprüft es .gitattributes (das im übergeordneten Verzeichnis ist), wird nur Anweisungen setzen, wenn sie nicht bereits gesetzt sind
  • Schließlich untersucht es $GIT_DIR/info/attributes . Diese Datei wird zum Überschreiben der bauminternen Einstellungen verwendet. Es überschreibt <dir>/.gitattributes Direktiven.

Mit "kombinieren" meine ich "aggregieren" mehrere Merge-Treiber.
Nick Green versucht in den Kommentaren , Merge-Treiber zu kombinieren: siehe " Merge poms via python git driver ".
Wie jedoch in seiner anderen Frage erwähnt , funktioniert es nur im Falle von Konflikten (gleichzeitige Änderung in beiden Zweigen).


Wir haben mehrere Konfigurationsdateien, die wir niemals überschreiben wollen. Allerdings .gitignore und .gitattributes hat in unserer Situation nicht funktioniert. Unsere Lösung bestand darin, die Konfigurationsdateien in einem Konfigurationszweig zu speichern. Lassen Sie dann zu, dass die Dateien während der Git-Zusammenführung geändert werden, aber unmittelbar nach dem Zusammenführen den "git checkout branch -." um unsere Konfigurationsdateien nach jedem Zusammenführen aus dem configs-Zweig zu kopieren. Detaillierte -Antwort hier





git-merge