Proste narzędzie do „zaakceptuj swoje” lub „zaakceptuj moje” w całym pliku za pomocą git

399

Nie chcę wizualnego narzędzia do scalania, a także nie chcę zmuszać do vi konfliktu pliku i ręcznego wybierania między HEAD (moim) a zaimportowaną zmianą (ich). Przez większość czasu chcę albo wszystkie ich zmiany, albo wszystkie moje. Zwykle dzieje się tak, ponieważ moja zmiana sprawiła, że ​​grałem w górę i wraca do mnie poprzez przyciągnięcie, ale może być nieco zmodyfikowana w różnych miejscach.

Czy istnieje narzędzie wiersza polecenia, które pozbywa się znaczników konfliktu i wybiera wszystkie takie czy inne sposoby na podstawie mojego wyboru? Lub zestaw komend git, które mogę aliasu sobie do każdego z nich.

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

Robienie tego jest raczej denerwujące. Do „zaakceptuj moje” próbowałem:

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.

Jak mam się pozbyć tych znaczników zmian?

Potrafię:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Ale wydaje się to dość okrągłe, musi być lepszy sposób. I w tym momencie nie jestem pewien, czy git nawet myśli, że nastąpiło scalenie, więc nie sądzę, że to koniecznie działa.

Idąc w drugą stronę, robienie „zaakceptuj ich” jest równie nieporządne. Jedyny sposób, w jaki mogę to rozgryźć, to:

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

Daje mi to także błędny komunikat zatwierdzenia, który zawiera dwa razy Konflikty: Makefile.

Czy ktoś może wskazać, jak wykonać powyższe dwie czynności w prostszy sposób? Dzięki

nosatalian
źródło
4
Muszę ci to dać jako trzyletni + użytkownik wiersza poleceń git. Trudno mi to zrobić z pamięci. To naprawdę powinno być wbudowane domyślnie.
Mauvis Ledford,

Odpowiedzi:

602

Rozwiązanie jest bardzo proste. git checkout <filename>próbuje wyewidencjonować plik z indeksu , a zatem nie powiedzie się podczas scalania.

To, co musisz zrobić, to (tzn. Sprawdzić zatwierdzenie ):

Aby pobrać własną wersję , możesz użyć jednego z:

git checkout HEAD -- <filename>

lub

git checkout --ours -- <filename>

lub

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

Aby pobrać drugą wersję , możesz użyć jednego z:

git checkout test-branch -- <filename>

lub

git checkout --theirs -- <filename>

lub

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

Musisz także uruchomić polecenie „dodaj”, aby oznaczyć go jako rozwiązany:

git add <filename>
Jakub Narębski
źródło
31
Uznałem to za nieco dziwne --oursi --theirsoznacza dokładnie odwrotność tego, co intuicyjnie myślałem, wypróbowując to polecenie ...
Joshua Muheim
6
Zachowaj ostrożność podczas używania git show- pomija to normalizację nowej linii.
Chronial
2
Przydaje się to w przypadku kilku plików, ale jeśli masz wiele plików w konflikcie (ponieważ zmieniono datę w komentarzu!), Jak to zrobić?
JhovaniC 18.04.13
4
@ Santos: --jest używany przez Git do oddzielania wersji (nazwy oddziałów itp.) Od nazw ścieżek (nazwy plików, katalogi). Ważne jest, aby Git nie mógł zdecydować, czy nazwa jest nazwą oddziału, czy nazwą pliku. Jest to zgodne z konwencją POSIX (lub GNU) polegającą na używaniu podwójnego myślnika do oddzielania opcji od argumentów (nazw plików).
Jakub Narębski
3
@Sammaron @Joshua Muheim; theirs/ oursmoże pojawić zamienione jeśli rozwiązywania konfliktów w kontekście operacji rebase. Ponieważ rebase działa poprzez sprawdzenie gałęzi docelowej, a następnie wybieranie cherry commits z „twojej” gałęzi na cel, nadchodząca zmiana („ich”) pochodzi z „twojej” gałęzi, a bieżąca gałąź jest gałęzią docelową („naszą”) ).
RJFalconer,
93

Spróbuj tego:

Aby zaakceptować ich zmiany: git merge --strategy-option theirs

Aby zaakceptować swoje: git merge --strategy-option ours

Siva Mandadi
źródło
5
Pamiętaj, że spowoduje to zachowanie twoich zmian dla WSZYSTKICH kolidujących plików, więc może być niebezpieczne, jeśli wystąpi nieoczekiwany konflikt.
John
3
I możesz użyć tego do innych poleceń scalania, takich jak cherry-pick i rebase.
idbrii
50

Na podstawie odpowiedzi Jakuba możesz dla wygody skonfigurować następujące aliasy git:

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

Opcjonalnie biorą jedną lub kilka ścieżek plików do rozwiązania i domyślnie rozwiązują wszystko w bieżącym katalogu, jeśli nie są podane.

Dodaj je do [alias]sekcji swojego ~/.gitconfiglub uruchom

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
źródło
1
Nie działa dla mnie ... Czy to jest bash czy jakaś inna powłoka?
user456584,
Są to aliasy git, dodaj je do [alias]sekcji w swoim ~.gitconfiglub użyj git config --global accept-ours "...". Zredagowałem moją odpowiedź.
kynan
2
Nie masz pojęcia, ile czasu zaoszczędził mi ten alias. Kciuki w górę!
Adam Parkin,
1
@hakre Upewnij się, że zacytowałeś alias, w przeciwnym razie twoja powłoka spróbuje go zinterpretować. Lub po prostu ręcznie edytuj ~/.gitconfig.
kynan
1
Składnia powłoki dla wartości domyślnych:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill
17

W oparciu o odpowiedź Kynana, oto te same aliasy, zmodyfikowane, aby mogły obsługiwać spacje i początkowe myślniki w nazwach plików:

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
źródło
0

Idealną sytuacją dla rozwiązywania konfliktów jest gdy wiesz z wyprzedzeniem, w jaki sposób chcesz je rozwiązać i może przekazać -Xourslub -Xtheirscyklicznych opcje strategiczne seryjnej. Poza tym widzę trzy scenariusze:

  1. Chcesz zachować tylko jedną wersję pliku (prawdopodobnie powinno się jej używać tylko na niemożliwych do scalenia plikach binarnych, ponieważ w przeciwnym razie pliki, które nie są w konflikcie i mogą nie być ze sobą zsynchronizowane).
  2. Chcesz po prostu rozstrzygnąć wszystkie konflikty w określonym kierunku.
  3. Musisz rozwiązać niektóre konflikty ręcznie, a następnie wszystkie pozostałe rozwiązać w określonym kierunku.

Aby rozwiązać te trzy scenariusze, możesz dodać następujące wiersze do .gitconfigpliku (lub odpowiednika):

[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

The get(ours|theirs)Narzędzie po prostu utrzymuje odpowiednią wersję pliku i odrzuca wszystkie zmiany z innej wersji (więc nie występuje łączenie).

merge(ours|theirs)Narzędzie ponownie robi trzy sposób scalania z lokalnego, zasady i zdalne wersje pliku, wybierając do rozwiązywania konfliktów w danym kierunku. Ma to pewne zastrzeżenia, w szczególności: ignoruje opcje diff, które zostały przekazane do polecenia scalania (takie jak obsługa algorytmu i białych znaków); czy scalanie odbywa się czysto z oryginalnych plików (więc wszelkie ręczne zmiany w pliku są odrzucane, co może być dobre lub złe); i ma tę zaletę, że nie można go pomylić ze znacznikami różnic, które powinny znajdować się w pliku.

The keep(ours|theirs)Narzędzie po prostu edytuje się znaczników diff oraz wydzielone sekcje, wykrywanie ich przez wyrażenie regularne. Ma to tę zaletę, że zachowuje opcje różnic z polecenia scalania i pozwala ręcznie rozwiązać niektóre konflikty, a następnie automatycznie rozwiązać pozostałe. Ma tę wadę, że jeśli w pliku znajdują się inne znaczniki konfliktu, może się pomylić.

Wszystkie są używane przez uruchamianie, git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]jeśli jeśli <filename>nie zostanie dostarczone, przetwarza wszystkie pliki będące w konflikcie.

Ogólnie rzecz biorąc, zakładając, że wiesz, że nie ma żadnych znaczników różnic, które mylą wyrażenie regularne, keep*warianty polecenia są najpotężniejsze. Jeśli pozostawisz mergetool.keepBackupopcję nieustawioną lub true, po scaleniu możesz różnicować *.origplik względem wyniku scalenia, aby sprawdzić, czy ma to sens. Jako przykład uruchamiam następujące po mergetoolprostu, aby sprawdzić zmiany przed zatwierdzeniem:

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

Uwaga : jeśli merge.conflictstylenie jest, to zamiast diff3tego należy zastosować /^|||||||/wzorzec w sedregule /^=======/.

Parakleta
źródło