Как разбить файл на наш и свой на конфликт

У меня есть способ получить 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 для вас, скрывая дополнительный шаг. Некоторые люди считают это особенно удобным.

Другие вопросы по тегам