Простой инструмент, чтобы «принять их» или «принять мой» на весь файл с помощью git

400

Мне не нужен инструмент визуального слияния, а также я не хочу просматривать конфликтующий файл и вручную выбирать между HEAD (моим) и импортированным изменением (их). Большую часть времени я либо хочу все их изменения, либо все мои. Обычно это происходит потому, что мои изменения сделали его более быстрым и возвращаются ко мне через тягу, но могут быть слегка изменены в разных местах.

Есть ли инструмент командной строки, который избавится от маркеров конфликта и выберет все так или иначе, основываясь на моем выборе? Или набор команд git, которые я могу использовать для каждой из них.

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

Делать это довольно раздражает. Для «принять мое» я попытался:

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.

Как я должен избавиться от этих маркеров изменений?

Я могу сделать:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Но это кажется довольно круглым, должен быть лучший путь. И на данный момент, я не уверен, что Git даже думает, что слияние произошло, поэтому я не думаю, что это обязательно сработает.

Если идти в другую сторону, делать «принять их» одинаково грязно. Единственный способ понять это - сделать:

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

Это также дает мне испорченное сообщение о коммите, в котором дважды есть Conflicts: Makefile.

Может кто-нибудь указать, как сделать два вышеуказанных действия более простым способом? Спасибо

nosatalian
источник
4
Я должен дать вам это как пользователь командной строки git с трехлетним стажем, и мне это нелепо трудно сделать из памяти. Это действительно должно быть встроено по умолчанию.
Мовис Ледфорд

Ответы:

603

Решение очень простое. git checkout <filename>пытается извлечь файл из индекса , и поэтому происходит сбой при слиянии.

Что вам нужно сделать, это (т.е. проверить коммит ):

Чтобы оформить заказ, вы можете воспользоваться одним из следующих вариантов:

git checkout HEAD -- <filename>

или

git checkout --ours -- <filename>

или

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

Чтобы оформить заказ на другую версию, вы можете использовать один из:

git checkout test-branch -- <filename>

или

git checkout --theirs -- <filename>

или

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

Вам также необходимо запустить «добавить», чтобы пометить его как решенный:

git add <filename>
Якуб Наребски
источник
31
Я нашел , что это немного странно , что --oursи --theirsозначает точно противоположное тому , что я интуитивно думал, пытаясь эту команду ...
Джошуа Muheim
6
Будьте осторожны при использовании git show- это пропускает нормализацию новой строки.
Хронический
2
Это хорошо для нескольких файлов, но когда у вас много файлов в конфликте (потому что дата в комментарии была изменена!), Как вы это делаете?
JhovaniC
4
@Santhos: --используется Git для отделения ревизий (имен веток и т. Д.) От путей (имен файлов, каталогов). Важно, если Git не может решить, является ли имя именем ветви или именем файла. Это следует соглашению POSIX (или GNU) об использовании двойной черты для отделения параметров от аргументов (имен файлов).
Якуб Наребски
3
@ Саммарон @ Джошуа Мухайм; theirs/ oursможет оказаться местами , если вы разрешение конфликтов в контексте операции перебазирования. Поскольку rebase работает, проверяя целевую ветвь, тогда вишневый сбор фиксирует из «вашей» ветви в целевую, входящее изменение («их») происходит из «вашей» ветви, а текущая ветвь является целевой ветвью («наша»). ).
RJFalconer
93

Попробуй это:

Чтобы принять их изменения: git merge --strategy-option theirs

Чтобы принять ваше: git merge --strategy-option ours

Шива Мандади
источник
5
Обратите внимание, что это сохранит ваши изменения для ВСЕХ конфликтующих файлов, поэтому может быть опасно, если произойдет неожиданный конфликт.
Джон
3
И вы можете использовать это для других команд merge-y, таких как cherry-pick и rebase.
idbrii
50

Основываясь на ответе Якуба, вы можете для удобства настроить следующие псевдонимы git:

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

При желании они выбирают один или несколько путей к файлам и по умолчанию разрешают все в текущем каталоге, если они не указаны.

Добавьте их в свой [alias]раздел ~/.gitconfigили запустите

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
источник
1
Не работает для меня ... Это для Bash или другой оболочки?
user456584
Это псевдонимы git, добавьте их в свой [alias]раздел ~.gitconfigили используйте git config --global accept-ours "...". Отредактировал мой ответ.
Кинан
2
Вы не представляете, сколько времени этот псевдоним спас мне. Недурно!
Адам Паркин
1
@hakre Убедитесь, что вы указали псевдоним, иначе ваша оболочка попытается его интерпретировать. Или просто вручную отредактируйте свой ~/.gitconfig.
Кинан
1
Синтаксис оболочки для значений по умолчанию:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill
17

Основываясь на ответе Кинана, вот те же псевдонимы, модифицированные так, чтобы они могли обрабатывать пробелы и начальные тире в именах файлов:

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
источник
0

Идеальная ситуация для разрешения конфликтов - это когда вы заранее знаете, каким образом вы хотите их разрешить, и можете передать параметры стратегии рекурсивного -Xoursили -Xtheirsслияния. За пределами этого я вижу три сценарных:

  1. Вы хотите просто сохранить одну версию файла (это, вероятно, следует использовать только для непересекающихся двоичных файлов, так как в противном случае конфликтующие и неконфликтующие файлы могут не синхронизироваться друг с другом).
  2. Вы хотите просто решить все конфликты в определенном направлении.
  3. Вам необходимо разрешить некоторые конфликты вручную, а затем разрешить все остальные в определенном направлении.

Для решения этих трех сценариев вы можете добавить следующие строки в ваш .gitconfigфайл (или эквивалентный):

[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

get(ours|theirs)Инструмент просто хранит соответствующую версию файла и отбрасывает все изменения от другой версии (так не слияние не происходит).

merge(ours|theirs)Инструмент повторно выполняет три способа слияния с местной, базы и удаленные версии файла, выбирая для разрешения конфликтов в данном направлении. Это имеет некоторые оговорки, в частности: он игнорирует параметры diff, которые были переданы команде слияния (например, алгоритм и обработка пробелов); выполняет слияние чисто из исходных файлов (поэтому любые ручные изменения в файле отбрасываются, что может быть как хорошим, так и плохим); и имеет то преимущество, что его нельзя спутать с маркерами diff, которые должны быть в файле.

keep(ours|theirs)Инструмент просто редактирует из маркеров Diff и закрытые секции, их обнаружения с помощью регулярного выражения. Преимущество этого в том, что он сохраняет параметры diff из команды merge и позволяет вручную разрешать некоторые конфликты, а затем автоматически разрешать остальные. Недостатком является то, что если в файле есть другие маркеры конфликта, это может привести к путанице.

Все они используются при запуске git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]где, если <filename>не предоставлено, он обрабатывает все конфликтующие файлы.

Вообще говоря, если вы знаете, что нет никаких различий, которые могли бы спутать регулярное выражение, keep*варианты команды являются наиболее мощными. Если вы оставите mergetool.keepBackupпараметр unset или true, то после слияния вы можете сравнить *.origфайл с результатом слияния, чтобы убедиться, что он имеет смысл. В качестве примера, я запускаю следующее после mergetoolпроверки, чтобы проверить изменения перед фиксацией:

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

Примечание : если merge.conflictstyleнет, diff3то /^|||||||/шаблон в sedправиле должен быть /^=======/вместо этого.

Parakleta
источник