Как разбить файл на наш и свой на конфликт
У меня есть способ получить 2 копии файла, где первый будет иметь удаленный контент, а второй - локальный.
2 ответа
Я не думаю, что есть опция git, чтобы хранить обе версии файла в двух копиях.
Но вы можете легко достичь этой цели с помощью следующих команд:
git merge the_branch
git checkout --theirs -- path/to/file ; mv path/to/file path/to/file.theirs
git checkout --ours -- path/to/file ; mv path/to/file path/to/file.ours
git checkout -m -- path/to/file
В итоге у вас есть три файла:
file.theirs
с их версией;file.ours
, с вашей версией;file
, как с версиями, так и с конфликтами.
Взяв идеи из официальной документации и других комментариев здесь, я придумал небольшую функцию, которая может вам помочь.
Этот сценарий также охватывает случай конфликтов перебазирования, что по какой-то причине решение, использующее git checkout --ours
не работал над ними.
В моем конкретном случае использования я заинтересован в том, чтобы все файлы разделялись каждый раз, когда я запускаю команду, адаптируя ее по мере необходимости.
Функции для рыбы:
function git-conflict-split
for file in (git ls-files -u | cut -c 51- | sort -u)
rm $file
git checkout-index --stage 3 -- $file
mv $file $file.THEIRS
git checkout-index --stage 2 -- $file
mv $file $file.OURS
git checkout-index --stage 1 -- $file
echo "meld $file.OURS $file $file.THEIRS"
end
end
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
разошлись, но имеют общий коммит базы слияния:1
o--...--L <-- ours (HEAD)
/
...--o--B
\
o--...--R <-- theirs
Фиксация L является левой или локальной или --ours
совершить. Commit 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
для вас, скрывая дополнительный шаг. Некоторые люди считают это особенно удобным.