Вопрос:

Возможно ли разрешить конфликт Git для одного файла с помощью Ours / Theirs?

git command-line merge

2596 просмотра

2 ответа

735 Репутация автора

Я нашел много инструкций по переполнению стека и в других местах для разрешения конфликтов с помощью динамического OURS / THEIRS, в случаях, когда вы просто хотите перезаписать один файл другим (особенно двоичные файлы). Однако почти в каждом примере, который я нахожу, он всегда применяется ко всем конфликтам, тогда как я хочу применить его только к одному конфликтующему файлу.

Одно из возможных решений, которое я нашел, - использовать команду git mergetool. Однако инструмент mergetool вызывает у меня проблемы, когда я выбираю «выбрать слева» или «выбрать право», и ничего не происходит.

Независимо от mergetool, я все равно хотел бы знать, есть ли способ сделать это из командной строки. Я уверен, что есть, и если кто-нибудь даст мне знать команду или иным образом свяжет меня с таким вопросом, который я не должен найти, я был бы очень признателен.

Я также пытался использовать ...

Git Checkout - ПУТЬ / К / КОНФЛИКТ / ФАЙЛ

Но затем, когда я вхожу в «git status», он все равно показывает файл как конфликтующий.

Большое вам спасибо за ваше время.

Автор: SemperCallide Источник Размещён: 05.01.2018 02:54

Ответы (2)


3 плюса

214354 Репутация автора

Решение

TL; DR

Вам понадобится git addокончательное решение, если только вы не используете другой метод для извлечения «нашей» или «их» версии.

Долго

Каждый инструмент слияния не зависит от Git (Git просто запускает их и позволяет им делать свое дело), ​​поэтому для этой конкретной части этого вопроса вы должны обратиться к самому инструменту слияния.

Что касается git checkout --oursили git checkout --theirs, ну, это то, что Git называет индексом в полной мере. Помните, что индекс, который в некотором роде является загадочным и также называется промежуточной областью, а иногда и кешем , по сути, где вы и Git создаете следующий коммит, который вы сделаете .

Когда вы бежите:

git merge <commit-or-branch-specifier>

Git находит три коммита:

  • Во-первых, это ваш текущий коммит , с которым вы всегда работаете в любой момент времени, так что это не особенный случай, за исключением того, что вы можете ссылаться на него по имени HEADили по одному символу @(например, git rev-parse HEADили git rev-parse @получить его хэш-идентификатор). ).
  • Одним из них является коммит, который вы только что назвали. Если вы запустились git merge otherbranch, вы можете запустить, git rev-parse otherbranchчтобы увидеть, каков его хеш-идентификатор коммита. (Имена ветвей имеют свойство перемещаться: то есть, фиксация, идентифицируемая по имени ветки прямо сейчас, не обязательно является фиксацией, идентифицируемой этим именем вчера или завтра. Это движение имен ветвей - это то, как растут ветви.) Конечно, если Вы запустили git merge a123456, другой коммит, один для --theirs, это хеш-идентификатор a123456.
  • Последний коммит - это база слияния , которую Git находит для вас автоматически. Он находит этот коммит, используя родительские связи из вашего коммита и другого коммита, чтобы работать в обратном направлении через обе ветви, пока не найдет подходящую точку, где две ветви сначала возвращаются вместе.

Найдя три коммита, Git выполняет:

git diff --find-renames <merge-base> <ours>    # see what we changed
git diff --find-renames <merge-base> <theirs>  # see what they changed

Процесс слияния - как бы слияния как глагол - состоит из нахождения этих трех коммитов, выполнения различий и объединения изменений. Вы получаете конфликт слияния, когда два набора изменений влияют на одни и те же строки.

В файле, где нет конфликтов слияния, Git помещает результат как в ваше рабочее дерево (как обычный файл), так и в индекс (как специальную Git-форму файла, готовую для фиксации). Так что для неконфликтных файлов, как правило, больше ничего не нужно делать.

Однако, когда возникает конфликт слияния, Git делает две необычные вещи: во-первых, он записывает версию с конфликтом слияний в рабочее дерево, чтобы вы могли редактировать ее как обычный файл. Во-вторых, в индекс записывается не одна версия файла, а все три : базовая версия слияния, «наша» версия и «их» версия.

Git называет эти дополнительные версии более высокими стадиями . Номер этапа - это база слияния, и нет --baseвозможности получить к ней доступ, но вы можете использовать ее, чтобы увидеть. Этап номер два - это «наша» версия: есть, но вы также можете посмотреть ее. Этап № 3 - это «их» версия, доступная через . Эти три этапа заменяют обычную запись нулевого этапа, которая сейчас отсутствует.git show :1:<em>path</em>--oursgit show :2:<em>path</em>git show :3:<em>path</em>

Фактически, когда вы запускаете git mergetool, вы обнаруживаете три версии в индексе, извлекаете их в обычные (не Git-ified) файлы и запускаете фактический инструмент слияния для этих трех файлов. Предполагается, что инструмент слияния должен делать правильные вещи (какими бы они ни были), чтобы объединить эти три файла в один объединенный файл, после чего git mergetoolможно запустить git addрезультат.

Однако из командной строки - как я делаю слияния - вы можете просто отредактировать файл рабочего дерева с его маркерами конфликта и выяснить, каков правильный результат. Запишите это, git addполучившийся файл, и вы в порядке, потому что git addзамечаете, что файл существует в форме трехступенчатой ​​версии, и стираете эти три версии, записывая вместо этого нулевую ступень.

Если есть нулевой этап (и больше нет этапов 1-3), файл считается разрешенным.

Теперь просто скажем Git: выньте версию этапа 2 из индекса и поместите ее в рабочее дерево. Версия с указанием Git взять версию стадии 3 вместо. В обоих случаях индекс с его трехступенчатой ​​версией остается один. Это только извлекает из индекса, к рабочему дереву. ( Здесь на всякий случай часть, скажем, с именем файла . Если имя файла не похоже на опцию, вам не нужно . Это хорошая привычка использовать все время, но большинство люди не делают.)git checkout --ours -- <em>path</em>--theirs--path--theirs----

Поскольку в индексе по-прежнему есть все три поэтапные версии, файл еще не разрешен. Выполнение git addберет файл рабочего дерева и помещает его в нулевой слот, стирая записи с 1 по 3, и теперь файл разрешен.

Любопытно, что запускается или вызывает разрешение файла. Это артефакт Git, позволяющий реализации диктовать интерфейс: внутренне, когда вы используете , Git должен сначала найти Git-форму файла в заданном (хеш коммита или имя типа или ). Затем он должен скопировать эту форму Git в индекс ... и это копирование стирает записи слота 1-3, записывая в обычную запись нулевого слота. Наконец, Git затем извлекает файл (Git-формы) из нулевой записи индекса в рабочее дерево.git checkout HEAD -- <em>path</em>git checkout <em>otherbranch</em> -- <em>path</em>git checkout <em>name</em> -- <em>path</em>nameHEADotherbranch

Побочный эффект этого «сначала записать в индекс, а затем извлечь из индекса в рабочее дерево» состоит в том, что если файл находился в конфликтном состоянии - были активны этапы 1-3 - он больше не конфликтовал! Следовательно:

git checkout --ours -- file

не разрешает файл (потому что он извлекает из индексного слота 2), но:

git checkout HEAD -- file

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

Автор: torek Размещён: 05.01.2018 04:31

0 плюса

836 Репутация автора

Это (немного) измененная версия моего ответа на этот вопрос .

Всякий раз, когда вам нужна помощь в объединении одного файла, я считаю, что лучше всего написать кастом, mergetoolкоторый делает то, что вы хотите. Для этого я предлагаю добавить следующие строки в ваш .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 Размещён: 14.05.2019 01:01
Вопросы из категории :
32x32