Разрешение конфликтов из сеанса am

Я хотел выбрать несколько коммитов из одного репозитория в другой. Я следовал инструкциям, приведенным в этом сообщении о переполнении стека:

/path/to/2 $ git --git-dir=/path/to/1/.git format-patch --stdout sha1^..sha1 | git am -3

И возник конфликт:

Applying: commit-name-xxx
fatal: sha1 information is lacking or useless (path/to/conflicted/file).
error: could not build fake ancestor
Patch failed at 0001 commit-name-xxx
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Я попытался разрешить конфликт, запустив:

 git mergetool --tool=DiffMerge

Но ответ был:

No files need merging

Я также запустил git status, но ответ был таким:

On branch develop
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

nothing to commit, working directory clean

Я не уверен, что понимаю, что здесь происходит: моя первая команда сообщает мне, что это конфликт и что он был перемещен в .git/rebase-apply/patch, но ни git mergetool, ни git status не находят этот конфликт.


person MHogge    schedule 06.04.2018    source источник
comment
git-am, кажется, использует совсем другую реализацию, чем выбор вишни, и, по моему опыту, работает намного хуже. Неужели нельзя получить из исходного репозитория, а затем использовать обычный выбор вишни?   -  person max630    schedule 06.04.2018


Ответы (2)


TL;DR

В вашем Git недостаточно информации, чтобы превратить патч в трехстороннее слияние. Без незавершенного трехстороннего слияния в вашем индексе вашему git mergetool не за что будет цепляться. Возможно, вам придется вручную применить патч.

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

Если вы git fetch из одного репозитория (тот, что в 1) в другой (2), вы можете git cherry-pick выполнить рассматриваемую фиксацию, как max630 предложено в комментарии. То есть, находясь в репозитории 2, вы можете либо добавить репозиторий 1 в качестве удаленного:

git remote add <name> <path-to-repo-1>

а затем git fetch из него, как и с любого другого удаленного устройства, или вы можете использовать старый (стиль Git 1.5) синтаксис git fetch <path> для временного получения всех доступных объектов из репо-1, а затем выбрать вишню по хэш-идентификатору.

Если это по-прежнему не работает (но будет) или неудобно по какой-либо другой причине, вам придется установить исправление вручную. Рассмотрите возможность использования git apply --reject с последующей ручной очисткой.

Длинная

Это сообщение об ошибке говорит нам — хорошо, говорит мне — что происходит:

fatal: sha1 information is lacking or useless (path/to/conflicted/file).

Вы используете git format-patch и git am для переноса одного исправления1 из одного репозитория Git в другой, так же, как люди обычно используют (или использовали в прошлом) git format-patch для исправления по электронной почте между сайтами, которые не имеют другого сетевого подключения. Когда Git делает такой патч, он включает в набор изменений для самой фиксации строку index над каждым патчем файла:

diff --git a/Documentation/RelNotes/2.17.0.txt b/Documentation/RelNotes/2.17.0.txt
index 7001dbbf8..c828d3734 100644

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

index 7001dbbf88b7ea5822eb0b798ac983505c57b3dc..c828d37345224550540a1665aaed2566d5bcb40e 100644

Теперь два хэша стали значительно лучше; это может помочь в некоторых случаях. Но что они являются?

Эти два хэш-идентификатора представляют собой хэш-идентификаторы больших двоичных объектов файлов, хранящихся в репозитории, — фактическое содержимое файлов «до» и «после». Фрагменты различий, которые следуют за этой строкой, дают инструкции: если вы измените эти строки в исходном BLOB-объекте (файле), используя эти строки замены, вы превратите исходный BLOB-объект — содержимое с левым хэшем. — в новый большой двоичный объект, содержимое которого названо правым хешем.

Когда вы передаете этот diff в git apply,2, возможно, что файл в HEAD больше не соответствует или даже сильно напоминает в некоторых частях "исходный блоб" в патче. В этом случае строки контекста не будут совпадать и/или раздел «до» нигде в файле не появится. Прямое применение патча становится невозможным.

Если вы указали флаг --3way или -3 для git apply — и git am делает это — Git теперь может использовать информацию в строке index. Поскольку первый хеш — это хэш большого двоичного объекта фактического содержимого файла в репозитории, создавшем набор изменений, ваш собственный Git может просмотреть ваш собственный репозиторий, чтобы узнать, есть ли у вас большой двоичный объект с этим хэш-идентификатором. . Если это так, у вас уже есть исходный файл.3 Git может просто извлечь этот файл и исправить его на месте, чтобы создать «после исправления». версия.

Теперь в Git есть все три версии файла: базовая версия, полученная с помощью хэш-идентификатора «до» и случайно найденная в вашем репозитории; "их" версия, полученная путем применения патча к базовой версии; и «наша» версия, которая является файлом в текущем или HEAD коммите. Таким образом, Git теперь может поместить все три версии в ваш индекс, и теперь возможно трехстороннее слияние.

С другой стороны, возможно, что хэш-идентификатор большого двоичного объекта в строке index не соответствует ни одному объекту в вашем репозитории. В этом случае у вас нет «до» версии файла. Невозможно сделать трехстороннее слияние. Или возможно, хотя и маловероятно,4, что у вас есть укороченный хеш большого двоичного объекта, который соответствует более чем одному большому двоичному объекту в вашем репозитории. В этом случае у вас может быть версия файла "до", но Git не знает наверняка и не будет пытаться определить, является ли какое-либо из этих больших двоичных объектов правильным.

В любом случае, поскольку у вашего Git недостаточно информации для попытки трехстороннего слияния, он не пытается пытаться, оставляя вас в этой ситуации. В конце концов, используя git fetch и git cherry-pick, вы можете получить настоящее трехстороннее слияние. Истории даже не должны быть связаны, так как вишневый выбор заставляет базу слияния быть родителем выбираемого коммита.


1Это также работает для набора исправлений, но директивы format-patch показывают, что это всего лишь одно исправление.

2Обратите внимание, что git am — это, по сути, просто оболочка, которая запускает git apply для каждого патча, за которым следует git commit результата.

3Помните, что Git работает исходя из того, что поскольку вы загружаете патч в git am, у вас нет копии другого репозитория. Кто-то еще отправил вам по электронной почте патч. Только у они есть этот репозиторий; у вас есть только ваш репозиторий. Здесь это не так — у вас есть оба репозитория, но Git этого не знает!

4Вероятность зависит от количества объектов BLOB-объектов в вашем репозитории и длины укороченного хэша. В Git теперь есть код для автоматического выбора подходящей длины сокращенного хэша, но это работает на основе количества объектов в репозитории, в котором создается разница, а не на количестве объектов в принимающем репозитории. . Если принимающий репозиторий значительно больше, отправитель может не предложить достаточно длинный хэш. Более старые версии Git также не имеют этого автоматического вычисления и по умолчанию просто безоговорочно используют 28-битный хэш; это может быть слишком коротко.

person torek    schedule 06.04.2018
comment
Хороший ответ и хорошее объяснение! git remote add + git fetch + cherry-pick — правильный выбор. Как примечание, cherry-pick позволяет выбирать несколько коммитов, чтобы получить больше информации об этом: stackoverflow.com/questions/1670970/ - person MHogge; 10.04.2018

Другое решение, которое сработало для меня лучше, заключалось в использовании patch --merge вместо любого инструмента git.

person Catalin Hritcu    schedule 17.09.2018