TL; DR
Подумайте об использовании git mergetool
(хотя я сам никогда так не делаю) или git show
(я иногда так делаю). Также рассмотрите возможность установки merge.conflictStyle
в diff3
, которая заставляет Git писать, в конфликтующую копию рабочего дерева, базовую версию , а также левую и правую версии. Я обнаружил, что с этим включенным мне почти никогда не нужно , чтобы видеть два входа. (Но только почти никогда.)
Long (МОГ)
На самом деле файл уже присутствует в во всех трех версиях, но все они находятся в индексе, а не в рабочем дереве. Поэтому хитрость заключается в том, чтобы вывести их из индекса, где они представлены в специальном формате Git, в ваше рабочее дерево, где вы можете видеть их и работать с ними.
Давайте кратко, но подробно рассмотрим, как git merge
работает при возникновении конфликтов. Чтобы возникли конфликты, мы должны были сделать что-то вроде этого:
$ git checkout ours
Switched to branch 'ours'
$ git merge theirs
Git рассмотрит график фиксации и обнаружит, что ours
и theirs
разошлись, но имеют общую базу слияния commit: 1
o--...--L <-- ours (HEAD)
/
...--o--B
\
o--...--R <-- theirs
Коммит L - это левая сторона или локальная или --ours
фиксация. Коммит R - это правая сторона или удаленный или --theirs
коммит. Совершите B здесь база слияния. Затем Git фактически выполнил две команды git diff
, одну, чтобы узнать, что мы изменили с момента объединения базы:
git diff --find-renames <hash-of-B> <hash-of-L> # what we changed
git diff --find-renames <hash-of-B> <hash-of-R> # what they changed
Слияние затем попыталось объединить эти два набора изменений, используя содержимое, связанное с коммитом B , в качестве основы для объединенных изменений. Однако мы изменили какой-то файл, они изменили тот же файл, и наши изменения столкнулись с их изменениями, и мы получили конфликт слияния.
На этом этапе Git поместил все три копии файла в указатель с ненулевым промежуточным слотом чисел:
- Этап 1 содержит базу слияния: файл B: P , где B - базовый коммит, а P - путь к файлу (как в любом случае мы нашли в этом коммите - мы могли бы переименовать файл!).
- Этап 2 содержит нашу версию файла: файл L: P .
- Этап 3 содержит свою версию файла: файл R: P .
Рабочее дерево содержит попытку Git объединить два набора изменений с маркерами конфликта. Обратите внимание, что эта версия отличается от любой из трех версий ввода! Некоторые части слияния могут быть уже разрешены. Стиль по умолчанию для конфликтующих изменений - merge
, который показывает только левый ( B -vs- L ) и правый ( B -vs- R ) меняет без отображения раздела, который был в B . Во многих случаях этого достаточно, но когда изменения являются чисто удалениями, часто очень полезно знать, какие строки в B удаляются / удаляются, и это не то, что вы можете просто вывести. При установке стиля конфликта на diff3
Git также записывает кодовую секцию B , между двумя секциями изменений.
1 Возможно наличие нескольких коммитов базы слияния. В этом случае Git по умолчанию создает новый коммит базы слияния путем слияния баз слияния. Этот процесс немного запутанный, но, к счастью, он редок, и даже когда это происходит, он редко делает что-либо хуже.
Извлечение трех версий
Вы можете извлечь любую из трех версий, используя git checkout-index
, хотя эту команду немного затруднит:
git checkout-index --all -- path/to/file
Это записывает все три в файлы со странными временными именами, которые вы затем должны переименовать. (Есть несколько вариантов этой темы, но все они немного раздражают.)
Вы можете использовать git mergetool
, который автоматически извлекает все три версии, а затем вызывает выбранную вами команду инструмента слияния для всех из них.
Или вы можете вручную извлечь один или оба файла. Метод из ответа DogEata будет работать, но если у вас нет проблем с окончанием строки, этот путь будет короче:
git show :1:path/to/file > path/to/file.base
git show :2:path/to/file > path/to/file.ours
git show :3:path/to/file > path/to/file.theirs
При этом используется команда git show
вместе с синтаксисом gitrevisions для доступа к индексным копиям, их отображения в стандартном выводе и перенаправления вывода в новые файлы.
Обратите внимание, что git add
записывает в нулевой слот
Git знает, что объединение выполняется, и еще не завершено, несколькими способами, но самое главное, что эти файлы находятся в слотах 1, 2 и / или 3 для некоторого пути в индексе. Когда вы выяснили правильное содержимое для этого пути и записали их в этот путь в своем рабочем дереве, вы запускаете:
git add path/to/file
чтобы скопировать файл обратно в индекс, взяв копию рабочего дерева нормального формата и сжав в специальный формат Git-only, который входит в индекс.
Если бы файл находился в индексе в нулевом слоте, как обычно, то это просто переписало бы старую копию индекса с версией исправленного рабочего дерева. Если в индексе есть несколько копий файла с использованием слотов с более высокими номерами, git add
все еще записывает в нулевой слот, но на этот раз также удаляет записи с более высокими номерами полностью. Теперь файл разрешен.
Если вы используете git mergetool
, команда git mergetool
может автоматически запустить для вас git add
, скрывая дополнительный шаг. Некоторые люди считают это особенно удобным.