Скажите git, чтобы решить конфликт с третьим файлом, который является ответом - PullRequest
0 голосов
/ 08 мая 2018

У нас следующая ситуация: Кто-то вручную разрешил МНОГО (десятки тысяч) конфликтов слияния без какого-либо инструмента git merge, но сделал это вручную и сохранил в другом файле При попытке исправить это я делаю правильное слияние. Теперь я знаю, как разрешить все конфликты - возьмите один в старом, разрешенном вручную файле. Есть ли способ сделать это автоматически - решить «третьим»?

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Помимо Марк Марк Адельсбергер ответил (что я считаю правильным и я проголосовал), стоит добавить еще одну заметку. (Следовательно, это должен быть комментарий, за исключением того, что нельзя форматировать комментарии и существует небольшой предел длины, который превышает «много».)

коммит слияния - это просто коммит как минимум с двумя родителями. То есть «нормальный» или не-слияние коммит - это коммит с одним или, в редких случаях, нет родителей. 1 Вот один такой фактический коммит:

$ git rev-parse HEAD
ccdcbd54c4475c2238b310f7113ab3075b5abc9c
$ git cat-file -p HEAD | sed 's/@/ /'
tree 191f960868564ef1f0978328589aa191219f1ab8
parent 96f29521a3908eb80b9552f11f2b75ca34475686
author Junio C Hamano <gitster pobox.com> 1525762230 +0900
committer Junio C Hamano <gitster pobox.com> 1525762789 +0900

The fifth batch for 2.18

Signed-off-by: Junio C Hamano <gitster pobox.com>

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

Git делает снимок исходного дерева в любом коммите, используя index Git, который является структурой данных, которая либо так важна, либо изначально так плохо названа, 2 имеет три имени: index , область подготовки и cache . В обычных случаях отсутствия конфликта слияния в индексе хранится одна копия каждого файла. Версия индекса представлена ​​в специальном формате Git-only, а версия рабочего дерева, с которой вы работаете, находится в обычном повседневном формате файлов на вашем компьютере. Вы работаете с версией рабочего дерева, затем используете git add в копию версию рабочего дерева поверх индексной версии, превращая файл обычного формата в новый, специальный , Git-only версия готова к фиксации.

Во время конфликтующего слияния, однако, индекс играет большую роль: теперь вместо просто одной копии файла он содержит три копии конфликтующего файла, используя три дополнительных «слота», которые существуют для каждого имени файла. Нормальный, не конфликтующий файл живет в нулевом слоте. Однако, когда возникает конфликт слияния, Git помещает версию base файла в слот 1, текущую (--ours или левую или локальную) версию в слот 2, а другую (* 1046) *, или правую, или удаленную) версию в слоте 3. Таким образом, в индексе есть три копии файла, плюс, конечно, одна в рабочем дереве с маркерами конфликта.

Что вы , как человек, делаете для Git , когда вы обрабатываете конфликты слияния, это манипулируете индексом, пока у него не будет только одна версия конфликта файл, вместо сохранения всех трех, используя три версии в индексах и копии рабочего дерева любым удобным для вас способом. Это верно независимо от того, как вы выполняете слияние: Git предлагает вам все четыре различных варианта файла, три из них в индексе и один в рабочем дереве, и в конце вы говорите Git поместить что-то в индекс Git. в нулевой слот и полностью забыть о трех других версиях.

Как только вы это сделаете - как только вы удалили три записи слотов более высокого уровня в индексе, разрешив конфликты слияния, используя ваш редактор или инструмент слияния или, возможно, с помощью нумерологии или что-нибудь еще , и запустите git add, чтобы скопировать окончательную версию в обычный слот-ноль - только тогда вы сможете git commit результат. Когда вы это делаете, Git делает тот же самый снимок tree, что и для любого коммита. Этот объект дерева является снимком как дерево любого коммита, поэтому объединение содержит снимок точно так же, как любой другой коммит. Другими словами, коммит слияния ничем не отличается от любого другого коммита *, за исключением того, что он имеет как минимум два родительских коммита.

Что делает коммит слияния выглядит отличным от того, что когда вы просите Git рассказать вам о коммите - любой коммит - Git делает это, сравнивая коммит с его родителем. Но слияние имеет по крайней мере двух родителей, так как же Git может сравнить его с (одиноким) родителем? Git ответ меняется:

  • git log просто не удосуживается сравнить его по умолчанию.
  • git show производит то, что Git называет комбинированным diff по умолчанию. (Комбинированные различия целенаправленно пропускают многое, поэтому они не очень полезны с точки зрения выяснения того, что произошло. Они в основном показывают, что был какой-то конфликт, и он был разрешен с помощью чего-то другого, кроме выбора одной «стороны» сливаться.)

Другие команды обычно следуют одному из этих двух параметров. Команды, которые могут отображать слияние, обычно также имеют опцию -m, которая сообщает Git split слияние, по крайней мере для целей сравнения, на несколько отдельных «виртуальных коммитов»: каждый такой коммит имеет одинаковый дерево в качестве слияния, но есть только один родитель . Теперь, когда у «виртуального коммита» есть один родительский элемент вместо двух или более, Git может показать его обычным способом, сравнивая (дифференцируя) виртуальный коммит с его (единственным) родителем.

Это не меняет того, что в коммите слияния, который является обычным снимком, сделанным так же, как и для любого обычного коммита без слияния, используя то, что вы имели в индексе, когда вы делали коммит. Так что в этом смысле нет ничего особенного в разрешении конфликтов вообще! Вы просто записываете файл с содержимым в свое рабочее дерево, используете git add, чтобы поместить его в индекс, и git commit, чтобы сделать снимок в коммите, даже если у финального коммита есть два родителя.


1 Коммит без родителей является коммитом root , а самый первый коммит в репозитории всегда является корневым, поскольку у него нет предыдущего коммита для присоединения. Большинство репозиториев, как правило, имеют только один корневой коммит, хотя, поскольку Git отчасти является просто системой обработки графов, вы можете создать столько корневых коммитов, сколько захотите. У большинства коммитов слияния есть два родителя, так как обычный процесс слияния состоит в том, чтобы объединить один конкретный коммит - обычно через имя ветки, но Git заботится только о commit здесь - в вашей текущей ветке, так что выдает «текущий коммит» "в качестве родителя # 1 и" другой коммит "в качестве родителя # 2.

Git может сделать то, что Git называет слияниями осьминога , используя текущий коммит в качестве родителя # 1, и целый ряд дополнительных коммитов в качестве остальных родителей, используя Git -s octopus merge стратегия . Однако стратегия слияния осьминога отказывается справляться с конфликтами, поскольку в индексе нет места для дополнительных копий конфликтующего файла. В индекс могут входить только три "стороны": базовая, левая / локальная / наша и правая / удаленная / другая / их.

2 Лично я думаю, что ответ на этот вопрос несколько и тот, и другой: важный и со слабым именем. : -)

0 голосов
/ 08 мая 2018

Комментарии к вопросу не совсем имеют смысл; поэтому я подозреваю, что некоторые комментарии, возможно, были удалены (или в любом случае не отображаются для меня), и я надеюсь, что я не пропустил ничего уместного. Это сказало:

На уровне «отдельное объединение отдельного файла» наилучшим решением является запуск объединения и, когда будет предложено разрешить конфликты, скопировать предварительно разрешенный файл поверх рабочей копии; затем git add it и commit для завершения слияния. [1]

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

Если, с другой стороны, у вас много файлов (возможно, в нескольких слияниях), то, возможно, имеет смысл немного больше автоматизации. Если вы можете настроить параллельную структуру каталогов для своего рабочего дерева и поместить предварительно объединенные файлы в соответствующие места в этой параллельной структуре, тогда cp -R может работать. Или, если они зафиксированы в git, вы можете использовать синтаксис git checkout <commit> -- <path> для получения фиксированных версий.

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


[1] В своих комментариях вы говорите, что беспокоитесь о журнале, показывающем отдельные изменения вместо полностью переписанного файла, и именно здесь я чувствую, что какой-то контекст отсутствует. В любом случае, копирование предварительно разрешенного файла для разрешения конфликта само по себе не приведет к тому, что git увидит этот файл как «полностью переписанный». Вывод журнала - это вычисленный патч. Он показывает различия между файлами независимо от процесса, используемого для создания каждой версии.

Так что, если он показывает «полностью переписанный файл», это означает, что он видит разницу (более или менее) в каждой строке. Это может произойти из-за изменений в пустом пространстве (особенно в некоторых случаях изменения конца строки проскальзывают). Или какое-то бесплатное переформатирование.

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

...