Когда я попытался объединить «исходную ветвь» с «целевой ветвью», git распознает два файла как конфликтующие. Но оба эти файла соответствуют разным путям файлов в «исходной ветви» и «целевой ветви» (файл был перемещен и изменен).
Чтобы выразить это более конкретно, я считаю,Вы сделали:
git checkout tgtbranch
git merge srcbranch
и получили:
CONFLICT (rename/delete): orig-filename deleted in HEAD and renamed
to new-filename in A. Version A of new-filename left in tree.
Automatic merge failed; fix conflicts and then commit the result.
(Обратите внимание, что я воспроизвел этот конфликт с ветвями с именами A
и B
вместо srcbranch
и tgtbranch
соответственно, также сообщение CONFLICT
было все в одной строке. Я разделил его на две части только для целей публикации.)
Мне нужно получить путь к этому конфликтующему файлу в "исходной ветке"".
Как видно из вышесказанного, эта информация доступна в сообщении CONFLICT
, которое печатает git merge
.
К сожалению, эта информация не доступна , по крайней мере, не напрямую, нигде впоследствии. Вы должны сохранить сообщение CONFLICT
. (См. Ниже два дополнительных варианта, если вы не сохранили сообщение CONFLICT
.)
То, что доступно , доступно для печати git status
:
$ git status --short
UA new-filename
$ git status
On branch B
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
added by them: new-filename
no changes added to commit (use "git add" and/or "git commit -a")
Мы также можем использовать git ls-files --stage
, чтобы увидеть, что в Git index на данный момент:
$ git ls-files --stage
100644 4466f6b253fc4b67ec2749befdd29b7c1f573e36 0 README
100644 923932af0c5b6ddc43c116c648cd793759949b66 3 new-filename
Существует только новое имя файла!
Что вы можете сделатьесли вы не сохранили строку CONFLICT
Способ 1: повторите объединение
Один из вариантов, возможно, самый простой, - , повторите объединение . Конечно, если вы работаете над разрешением слияния, вы, вероятно, не хотите связываться с текущим индексом и вашим рабочим деревом, что делает это немного сложным. Вы можете:
- клонировать репозиторий, чтобы у вас был новый клон с новым индексом и рабочим деревом, или
- использовать
git worktree add
, если у вас есть git worktree
и затем выполните повторное слияние в новом клоне или рабочем дереве. Используйте git checkout
для проверки коммита, на который указывает tgtbranch
в исходном клоне (если вы клонировали) или текущем клоне (если вы используете git worktree
), затем запустите git merge
, чтобы объединить коммит, с которымtgtbranch
очков в исходном клоне (если вы клонировали) или текущем клоне (если вы используете git worktree
). То есть:
$ git rev-parse tgtbranch
<hash>
$ git rev-parse srcbranch
<hash>
$ cd /tmp && git clone <path> reclone && cd reclone
$ git checkout <hash> && git merge <hash>
, если вы используете процедуру повторного клонирования, или:
$ git worktree add --detach <path> tgtbranch
$ cd <path> && git merge srcbranch
, если вы используете git worktree
подход.
Это объединение выплюнет строку CONFLICT
. Сохрани это! Затем удалите новый клон или добавленное рабочее дерево - его единственной целью было получить строку CONFLICT
. Теперь ваша проблема решена: у вас есть исходное имя файла.
Метод 2: используйте git diff
против базы объединения
Операция объединения обнаружена переименована в tgtbranch
с использованием git diff --find-renames
. Порог для поиска переименования был установлен на то, что вы указали в аргументе -X find-renames=<em>number</em>
, или на 50%, если вы не указали число.
Этот git diff
был разницей между двумя конкретными коммитами: объединить базовый commit, который Git обнаружил автоматически, и коммит tip равный tgtbranch
или srcbranch
(какая бы ветвь Git «не увидела» переименование: один коммит tip имел переименование, другой - delete,по сравнению с базой слияния).
Найти коммит базы слияния. Здесь есть одно осложнение, но оно сразу проявится, если оно возникнет. Выполнить:
git merge-base --all srcbranch tgtbranch
Например, в моем случае я получаю:
$ git merge-base --all A B
3a5c9ae0669e9969f8986810ea03c5283e0ac693
Если при этом выдается только один хеш коммита, то это хорошо. Если он печатает более одной, вам, вероятно, следует перейти к методу № 1.
Теперь, когда вы знаете, что существует одна база слияния, вы можете запустить две команды git diff --find-renames
. Добавьте параметр --name-status
, чтобы ограничить объем вывода. Вы также можете добавить --diff-filter=RD
, чтобы показывать только переименованные или удаленные файлы. В моем случае я пропустил эти параметры, потому что переименование или удаление все Я сделал:
$ git diff --find-renames --name-status A...B
D orig-filename
$ git diff --find-renames --name-status B...A
R064 orig-filename new-filename
Это показывает переименованный / удаленный файл: исходное имя было orig-filename
иновое имя new-filename
.
Этот трехточечный синтаксис A...B
(или в вашем случае tgtname...srcname
и srcname...tgtname
) имеет одно специальное значение, исключающее git diff
. В git diff
- но не в других командах Git 1 - это означает Найти базу слияния между двумя указанными коммитами и использовать ее в качестве левой части diff. Используйте правый указатель фиксации в качестве правой части diff. Так как существует только одна база слияния, это позволяет сравнить базу слияния с коммитом, названным аргументом правой стороны.
1 Специальный синтаксис с тремя точками применяется ко всем обычным механизмам Git diff, включая git diff-tree
, а также к пользовательскому интерфейсу git diff
.
Существует ошибка вспособ, которым это реализовано во многих версиях Git, в том случае, если существует более одной базы слияния, результат не предсказуем. Поэтому убедитесь, что есть только одна база слияния.
Примечание
Если мы проверяем индексный файл напрямую, то в этом не объединенном состоянии мы находим запись REUC
с исходнымимя файла:
$ od -c .git/index
0000000 D I R C \0 \0 \0 002 \0 \0 \0 002 ] 264 256 x
0000020 2 270 ) P ] 264 256 x 2 227 221 220 \0 \0 \0 ~
0000040 \0 320 ! a \0 \0 201 244 \0 \0 003 351 \0 \0 003 351
0000060 \0 \0 \0 W D f 366 262 S 374 K g 354 ' I 276
0000100 375 қ ** | 037 W > 6 \0 006 R E A D M E
0000120 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000140 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 201 244
0000160 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 222 9 2 257
0000200 \f [ m 334 C 301 026 306 H 315 y 7 Y 224 233 f
0000220 0 \f n e w - f i l e n a m e \0 \0
0000240 \0 \0 \0 \0 T R E E \0 \0 \0 006 \0 - 1
0000260 0 \n R E U C \0 \0 \0 - o r i g - f
0000300 i l e n a m e \0 1 0 0 6 4 4 \0 0
0000320 \0 0 \0 214 354 + 214 330 360 004 N O 355 \b 243 '
0000340 4 331 225 \n W [ 037 026 216 K } 367 ? 373 354 364
0000360 [ ^ 364 b 001 371 6 , 3 370 320
0000373
Индекс начинается с магического числа DIRC
, как и должно, затем содержит обычные записи индекса для обоих моих файлов (у меня есть только два, как показано в git ls-files --stage
выход). Но тогда у него есть запись TREE
, которую мы видим вблизи смещения 0000240
, за которой следует запись REUC
в следующей строке. Эта запись REUC является записью "отмены", которая позволяет git checkout -m
воспроизвести конфликт слияния. И, как мы видим, запись отмены на самом деле хранит исходное имя файла .
Git действительно должен позволить нам получить доступ к этой информации, возможно, через git ls-files
или - что более удобно - через git status
. Увы, это не так.