Semplice strumento per "accettare il loro" o "accettare il mio" su un intero file usando git


399

Non voglio uno strumento di unione visiva e non voglio nemmeno dover vi il file in conflitto e scegliere manualmente tra HEAD (mio) e il resto importato (loro). Il più delle volte voglio o tutti i loro cambiamenti o tutti i miei. Comunemente ciò è dovuto al fatto che il mio cambiamento mi ha consentito di risalire e mi sta tornando in mente, ma può essere leggermente modificato in vari punti.

Esiste uno strumento da riga di comando che eliminerà gli indicatori di conflitto e sceglierà in un modo o nell'altro in base alla mia scelta? O una serie di comandi git che posso fare alias per fare ognuno di essi.

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

Fare questo è piuttosto fastidioso. Per "accetta il mio" ho provato:

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.

Come dovrei sbarazzarmi di questi indicatori di cambiamento?

Posso fare:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Ma questo sembra piuttosto rotondo, ci deve essere un modo migliore. E a questo punto, non sono sicuro che git pensi persino che l'unione sia avvenuta, quindi non penso che funzioni necessariamente.

Andare dall'altra parte, fare "accettare il loro" è altrettanto disordinato. L'unico modo per capirlo è fare:

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

Questo mi dà anche un messaggio di commit incasinato, che contiene Conflicts: Makefile due volte.

Qualcuno può far notare come eseguire le due azioni precedenti in un modo più semplice? Grazie


4
Devo dartelo come utente della riga di comando + git per tre anni. Lo trovo incredibilmente difficile da fare dalla memoria. Dovrebbe davvero essere integrato per impostazione predefinita.
Mauvis Ledford

Risposte:


602

La soluzione è molto semplice. git checkout <filename>tenta di estrarre il file dall'indice e quindi non riesce a unire.

Quello che devi fare è (es. Checkout di un commit ):

Per effettuare il checkout della tua versione puoi utilizzare uno di:

git checkout HEAD -- <filename>

o

git checkout --ours -- <filename>

o

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

Per effettuare il checkout dell'altra versione è possibile utilizzare uno di:

git checkout test-branch -- <filename>

o

git checkout --theirs -- <filename>

o

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

Dovresti anche eseguire 'aggiungi' per contrassegnarlo come risolto:

git add <filename>

31
L'ho trovato un po 'strano --ourse --theirssignifica esattamente l'opposto di quello che ho pensato intuitivamente quando ho provato questo comando ...
Joshua Muheim,

6
Fai attenzione quando usi git show: questo salta la normalizzazione di nuova riga.
Cronologico,

2
Questo è utile per alcuni file, ma quando hai molti file in conflitto (perché è stata cambiata una data in un commento!), Come lo fai?
JhovaniC,

4
@Santhos: il --è usato da Git per separare le revisioni (nomi delle filiali ecc.) Dai nomi dei percorsi (nomi di file, directory). È importante se Git non può decidere se un nome è il nome del ramo o il nome del file. Ciò segue la convenzione POSIX (o GNU) sull'uso del doppio trattino per separare le opzioni dagli argomenti (nomi file).
Jakub Narębski,

3
@Sammaron @Joshua Muheim; il theirs/ ourspuò apparire scambiato se si stanno risolvendo conflitti nel contesto di un'operazione rebase. Poiché rebase funziona controllando il ramo di destinazione, la raccolta delle ciliegie commette dal "tuo" ramo sul bersaglio, la modifica in entrata ("loro") proviene dal "tuo" ramo e il ramo corrente è il ramo di destinazione ("nostro" ).
RJFalconer

93

Prova questo:

Per accettare le loro modifiche: git merge --strategy-option theirs

Per accettare il tuo: git merge --strategy-option ours


5
Tieni presente che ciò manterrà le modifiche per TUTTI i file in conflitto, quindi potrebbe essere pericoloso se si verifica un conflitto imprevisto.
Giovanni

3
E puoi usarlo per altri comandi merge-y come cherry-pick e rebase.
idbrii,

50

Sulla base della risposta di Jakub è possibile configurare i seguenti alias git per comodità:

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

Opzionalmente prendono uno o più percorsi di file per risolvere e per impostazione predefinita risolvono tutto nella directory corrente se non ne viene fornito nessuno.

Aggiungili alla [alias]sezione del tuo ~/.gitconfigo esegui

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'

1
Non funziona per me ... Sono questi per bash o qualche altra shell?
user456584

Questi sono alias git, aggiungili alla [alias]sezione nel tuo ~.gitconfigo usa git config --global accept-ours "...". Ho modificato la mia risposta.
kynan,

2
Non hai idea di quanto tempo questo alias mi ha salvato. Pollice su!
Adam Parkin,

1
@hakre Assicurati di citare l'alias, altrimenti la tua shell proverà ad interpretarlo. O semplicemente modifica manualmente il tuo ~/.gitconfig.
kynan,

1
Sintassi della shell per i valori predefiniti:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill

17

Sulla base della risposta di Kynan, ecco gli stessi alias, modificati in modo da poter gestire spazi e trattini iniziali nei nomi dei file:

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

0

La situazione ideale per risolvere i conflitti è quando sai in anticipo in che modo vuoi risolverli e puoi passare le opzioni di strategia di unione ricorsiva -Xourso -Xtheirsricorsiva. Al di fuori di questo posso vedere tre scenari:

  1. Devi solo mantenere una singola versione del file (questo dovrebbe probabilmente essere usato solo su file binari non riunibili, poiché file altrimenti in conflitto e non in conflitto potrebbero non essere sincronizzati tra loro).
  2. Volete semplicemente decidere tutti i conflitti in una direzione particolare.
  3. È necessario risolvere alcuni conflitti manualmente e quindi risolvere tutto il resto in una determinata direzione.

Per risolvere questi tre scenari è possibile aggiungere le seguenti righe al .gitconfigfile (o equivalente):

[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

Lo get(ours|theirs)strumento mantiene semplicemente la rispettiva versione del file e elimina tutte le modifiche dall'altra versione (quindi non si verifica alcuna unione).

Lo merge(ours|theirs)strumento esegue nuovamente l'unione in tre modi dalle versioni locale, base e remota del file, scegliendo di risolvere i conflitti nella direzione indicata. Questo ha alcune avvertenze, in particolare: ignora le opzioni diff che sono state passate al comando merge (come algoritmo e gestione degli spazi bianchi); si fonde in modo pulito dai file originali (quindi ogni modifica manuale al file viene scartata, che potrebbe essere buona o cattiva); e ha il vantaggio di non poter essere confuso dai marcatori diff che dovrebbero essere presenti nel file.

Lo keep(ours|theirs)strumento modifica semplicemente i marcatori di differenza e le sezioni racchiuse, rilevandole mediante un'espressione regolare. Ciò ha il vantaggio di preservare le opzioni diff dal comando di unione e consente di risolvere manualmente alcuni conflitti e di risolvere automaticamente il resto. Ha lo svantaggio che se ci sono altri marcatori di conflitto nel file potrebbe confondersi.

Questi sono tutti usati eseguendo git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]dove se <filename>non viene fornito elabora tutti i file in conflitto.

In generale, supponendo che tu sappia che non ci sono marcatori diff per confondere l'espressione regolare, le keep*varianti del comando sono le più potenti. Se si lascia l' mergetool.keepBackupopzione non impostata o vera, dopo l'unione è possibile differenziare il *.origfile dal risultato dell'unione per verificare che abbia senso. Ad esempio, eseguo quanto segue dopo il mergetoolgiusto per ispezionare le modifiche prima di eseguire il commit:

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

Nota : in caso merge.conflictstylecontrario, diff3il /^|||||||/modello nella sedregola deve essere /^=======/invece.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.