Einfaches Tool zum "Akzeptieren ihrer" oder "Akzeptieren meiner" für eine ganze Datei mit git

399

Ich möchte kein visuelles Zusammenführungswerkzeug, und ich möchte auch nicht die in Konflikt stehende Datei vi und manuell zwischen HEAD (meins) und der importierten Änderung (ihrer) auswählen müssen. Die meiste Zeit möchte ich entweder alle ihre Änderungen oder alle meine. In der Regel liegt dies daran, dass meine Änderung das Tempo erhöht hat und durch einen Zug zu mir zurückkehrt, aber an verschiedenen Stellen leicht modifiziert werden kann.

Gibt es ein Kommandozeilen-Tool, mit dem die Konfliktmarkierungen entfernt und je nach meiner Wahl auf die eine oder andere Weise ausgewählt werden können? Oder eine Reihe von Git-Befehlen, die ich selbst als Alias ​​verwenden kann, um jeden einzelnen auszuführen.

# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"

Das zu tun ist ziemlich nervig. Für 'akzeptiere meine' habe ich versucht:

randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.

randy@sabotage ~/linus $ git checkout Makefile 
error: path 'Makefile' is unmerged

andy@sabotage ~/linus $ git reset --hard HEAD Makefile 
fatal: Cannot do hard reset with paths.

Wie soll ich diese Änderungsmarker loswerden?

Ich kann:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Aber das scheint ziemlich rund zu sein, es muss einen besseren Weg geben. Und zu diesem Zeitpunkt bin ich mir nicht sicher, ob Git überhaupt glaubt, dass die Zusammenführung stattgefunden hat, also denke ich nicht, dass dies unbedingt überhaupt funktioniert.

In die andere Richtung zu gehen und ihre zu akzeptieren, ist ebenso chaotisch. Der einzige Weg, wie ich es herausfinden kann, ist:

git show test-branch:Makefile > Makefile; git add Makefile;

Dies gibt mir auch eine durcheinandergebrachte Commit-Nachricht, die zweimal Conflicts: Makefile enthält.

Kann jemand bitte darauf hinweisen, wie die beiden oben genannten Aktionen auf einfachere Weise ausgeführt werden können? Vielen Dank

nosatalian
quelle
4
Ich muss es Ihnen als dreijähriger + Git-Befehlszeilenbenutzer geben. Ich finde das lächerlich schwer aus dem Gedächtnis zu tun. Es sollte wirklich standardmäßig eingebaut sein.
Mauvis Ledford

Antworten:

602

Die Lösung ist sehr einfach. git checkout <filename>versucht, die Datei aus dem Index auszuchecken , und schlägt daher beim Zusammenführen fehl.

Was Sie tun müssen, ist (dh ein Commit auschecken ):

Zum Auschecken Ihrer eigenen Version können Sie eine der folgenden Optionen verwenden :

git checkout HEAD -- <filename>

oder

git checkout --ours -- <filename>

oder

git show :2:<filename> > <filename> # (stage 2 is ours)

Zum Auschecken der anderen Version können Sie eine der folgenden Optionen verwenden :

git checkout test-branch -- <filename>

oder

git checkout --theirs -- <filename>

oder

git show :3:<filename> > <filename> # (stage 3 is theirs)

Sie müssten auch 'add' ausführen, um es als gelöst zu markieren:

git add <filename>
Jakub Narębski
quelle
31
Ich fand es ein bisschen seltsam, dass --oursund --theirsgenau das Gegenteil von dem bedeutet, was ich intuitiv gedacht habe, als ich diesen Befehl ausprobiert habe ...
Joshua Muheim
6
Seien Sie vorsichtig bei der Verwendung git show- dies überspringt die Normalisierung von Zeilenumbrüchen.
Chronial
2
Dies ist für einige Dateien hilfreich, aber wenn Sie viele Dateien in Konflikt haben (weil ein Datum in einem Kommentar geändert wurde!), Wie machen Sie das?
JhovaniC
4
@Santhos: Das --wird von Git verwendet, um Revisionen ( Zweignamen usw.) von Pfadnamen (Dateinamen, Verzeichnisse) zu trennen. Es ist wichtig, wenn Git nicht entscheiden kann, ob ein Name der Name des Zweigs oder der Name der Datei ist. Dies folgt der POSIX- (oder GNU-) Konvention, doppelte Striche zu verwenden, um Optionen von Argumenten (Dateinamen) zu trennen.
Jakub Narębski
3
@Sammaron @Joshua Muheim; Das theirs/ ourskann ausgetauscht erscheinen, wenn Sie Konflikte im Rahmen einer Rebase-Operation lösen. Da die Rebase durch Auschecken des Zielzweigs funktioniert und dann das Cherry-Picking von "Ihrem" Zweig auf das Ziel übertragen wird, stammt die eingehende Änderung ("ihre") von "Ihrem" Zweig, und der aktuelle Zweig ist der Zielzweig ("unser"). ).
RJFalconer
93

Versuche dies:

Um ihre Änderungen zu akzeptieren: git merge --strategy-option theirs

Um deine zu akzeptieren: git merge --strategy-option ours

Siva Mandadi
quelle
5
Beachten Sie, dass Ihre Änderungen für ALLE in Konflikt stehenden Dateien beibehalten werden. Dies kann daher gefährlich sein, wenn ein unerwarteter Konflikt auftritt.
John
3
Und Sie können dies für andere Zusammenführungsbefehle wie Cherry-Pick und Rebase verwenden.
idbrii
50

Basierend auf Jakubs Antwort können Sie der Einfachheit halber die folgenden Git-Aliase konfigurieren:

accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"

Sie können optional einen oder mehrere Dateipfade zum Auflösen verwenden und standardmäßig alles im aktuellen Verzeichnis auflösen, wenn keine angegeben sind.

Fügen Sie sie dem [alias]Abschnitt Ihres ~/.gitconfigoder Ihres Laufs hinzu

git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'
kynan
quelle
1
Funktioniert nicht für mich ... Sind diese für Bash oder eine andere Shell?
user456584
Dies sind Git-Aliase. Fügen Sie sie dem [alias]Abschnitt in Ihrer ~.gitconfigoder Ihrer Verwendung hinzu git config --global accept-ours "...". Habe meine Antwort bearbeitet.
Kynan
2
Sie haben keine Ahnung, wie viel Zeit dieser Alias ​​mir gespart hat. Daumen hoch!
Adam Parkin
1
@hakre Stellen Sie sicher, dass Sie den Alias ​​zitieren, da Ihre Shell sonst versucht, ihn zu interpretieren. Oder bearbeiten Sie Ihre einfach manuell ~/.gitconfig.
Kynan
1
Shell-Syntax für Standardwerte:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill
17

Basierend auf der Antwort von Kynan sind hier dieselben Aliase, die so geändert wurden, dass sie Leerzeichen und Anfangsstriche in Dateinamen verarbeiten können:

accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"
Dar
quelle
0

Die ideale Situation für die Lösung von Konflikten besteht darin, dass Sie im Voraus wissen, auf welche Weise Sie sie lösen möchten, und die Optionen für die -Xoursoder die -Xtheirsrekursive Zusammenführungsstrategie übergeben können. Draußen sehe ich drei szenische:

  1. Sie möchten nur eine einzelne Version der Datei behalten (dies sollte wahrscheinlich nur für nicht zusammenführbare Binärdateien verwendet werden, da ansonsten möglicherweise in Konflikt stehende und nicht in Konflikt stehende Dateien möglicherweise nicht mehr miteinander synchronisiert sind).
  2. Sie möchten einfach alle Konflikte in eine bestimmte Richtung entscheiden.
  3. Sie müssen einige Konflikte manuell lösen und dann den Rest in eine bestimmte Richtung lösen.

Um diese drei Szenarien anzugehen, können Sie Ihrer .gitconfigDatei (oder einer gleichwertigen Zeile) die folgenden Zeilen hinzufügen :

[merge]
  conflictstyle = diff3
[mergetool.getours]
  cmd = git-checkout --ours ${MERGED}
  trustExitCode = true
[mergetool.mergeours]
  cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keepours]
  cmd = sed -I '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
  trustExitCode = true
[mergetool.gettheirs]
  cmd = git-checkout --theirs ${MERGED}
  trustExitCode = true
[mergetool.mergetheirs]
  cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keeptheirs]
  cmd = sed -I '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
  trustExitCode = true

Das get(ours|theirs) Tool behält nur die jeweilige Version der Datei bei und wirft alle Änderungen der anderen Version weg (es erfolgt also keine Zusammenführung).

Das merge(ours|theirs) Tool führt die Drei-Wege-Zusammenführung aus der lokalen, der Basis- und der Remote-Version der Datei erneut durch und wählt Konflikte in der angegebenen Richtung aus. Dies hat einige Einschränkungen, insbesondere: Es ignoriert die Diff-Optionen, die an den Merge-Befehl übergeben wurden (z. B. Algorithmus- und Whitespace-Behandlung). führt die Zusammenführung sauber aus den Originaldateien durch (daher werden alle manuellen Änderungen an der Datei verworfen, die gut oder schlecht sein können); und hat den Vorteil, dass es nicht durch Diff-Marker verwechselt werden kann, die in der Datei enthalten sein sollen.

Das keep(ours|theirs) Tool bearbeitet einfach die Diff-Markierungen und eingeschlossenen Abschnitte und erkennt sie durch regulären Ausdruck. Dies hat den Vorteil, dass die Diff-Optionen aus dem Zusammenführungsbefehl beibehalten werden und Sie einige Konflikte von Hand lösen und den Rest automatisch lösen können. Es hat den Nachteil, dass andere Konfliktmarkierungen in der Datei verwirrt werden können.

Diese werden alle verwendet, indem ausgeführt wird, git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]wo, wenn <filename>nicht angegeben, alle in Konflikt stehenden Dateien verarbeitet werden.

Unter der Annahme, dass Sie wissen, dass es keine Diff-Marker gibt, die den regulären Ausdruck verwirren könnten, sind die keep*Varianten des Befehls im Allgemeinen die mächtigsten. Wenn Sie die mergetool.keepBackupOption nicht gesetzt oder wahr lassen, können Sie die *.origDatei nach dem Zusammenführen anhand des Ergebnisses der Zusammenführung unterscheiden, um zu überprüfen, ob sie sinnvoll ist. Als Beispiel führe ich Folgendes aus mergetool, um die Änderungen vor dem Festschreiben zu überprüfen:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

Hinweis : Wenn dies merge.conflictstylenicht diff3der Fall ist, muss stattdessen das /^|||||||/Muster in der sedRegel verwendet werden /^=======/.

Parakleta
quelle