Операция слияния Git, в точке, где вы видите конфликт, работает файл за файлом. Если какой-то код, который был в файле orig-file.ext
, теперь находится в файле new-file.ext
, git merge
вам здесь не поможет. 1
Одна вещь, которую вы можете do - это использование git blame
, он же git annotate
, для просмотра коммитов, которые произошли в их ветви, чтобы увидеть, куда переместился код. Вы можете обратиться к git blame
документации здесь довольно много. Однако по умолчанию, чтобы легко найти новый код, вам нужно знать имя нового файла . Git поможет вам найти имя оригинального файла. Но вы уже знаете имя исходного файла: вы хотите имя нового файла. К счастью, есть опция git blame
, которая может помочь здесь - по крайней мере, потенциально. Ничто здесь не гарантировано.
Другая вещь, которую вы можете сделать, это использовать git grep
. Это на самом деле может быть вашим лучшим выбором. Если вы уверены, что некоторые строки не будут изменены в новом коде, git grep
позволит вам выполнить поиск кода коммитов другой ветви для кода. ,Это немедленно сообщит вам имя нового файла.
1 Если весь файл будет переименован, Git будет здесь вам помогут, потому что он сопоставит изменение файла в вашей ветке с переименованием файла в их ветке. Вы получите конфликт изменения / переименования . Но это не то, что здесь произошло.
Что нужно помнить о слиянии
Я сделал пример слияния с той же проблемой, внеся противоречивые изменения в две ветви, из которых я сделалmaster
в клоне Git-репозитория для Git:
$ git checkout -b move-code master
(move some code, add, commit)
$ git checkout -b change-code master
(modify the same lines I moved; add and commit)
$ git merge move-code
Auto-merging wt-status.c
CONFLICT (content): Merge conflict in wt-status.c
Automatic merge failed; fix conflicts and then commit the result.
Помните, что git merge
работает путем сравнения некоторой базы слияния коммита с двумя tip tip фиксирует, чтобы увидеть, что вы изменили, и посмотреть, что они изменились. (Помните по очереди, что каждый коммит сохраняет полный снимок из всех файлов.)
В моем случае я сделал только два коммита, один на change-code
иодин на move-code
. Но в целом у вас будет что-то вроде этого:
o--o--...--I <-- move-code (HEAD)
/
...--o--H
\
o--o--...--J
Это означает, что коммит H
является лучшим общим коммитом, между всеми коммитами, достижимыми с i
,коммит чаевых моих move-code
и J
коммит чаевых моих change-cde
. Лучший общий коммит - это база слияния. Вы можете заставить Git распечатать хеш-идентификатор этого базового коммита слияния, используя git merge-base
:
$ git merge-base --all move-code change-code
566a1439f6f56c2171b8853ddbca0ad3f5098770
Хеш-код, который Git печатает здесь, является хеш-идентификатором базы слияния. 2
Теперь, найдя базу слияния (с помощью git merge-base
или с помощью графика, напечатанного с помощью git log --graph
или чего-либо еще, что вы можете использовать, чтобы найти его), вы можете посмотрите, что Git видит как то, что нужно объединить, запустив две git diff
команды:
git diff --find-renames <hash-of-H> <hash-of-I>
и:
git diff --find-renames <hash-of-H> <hash-of-J>
или:
$ H=$(git merge-base --all change-code move-code); echo $H
566a1439f6f56c2171b8853ddbca0ad3f5098770
$ git diff --find-renames $H change-code
diff --git a/wt-status.c b/wt-status.c
index cc6f94504d..f2bf294275 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -117,6 +117,7 @@ static void status_printf_more(struct wt_status *s, const char *color,
{
va_list ap;
+ // this is status_printf_more
va_start(ap, fmt);
status_vprintf(s, 0, color, fmt, ap, NULL);
va_end(ap);
и затем:
$ git diff --find-renames $H move-code
[much bigger diff, snipped. This shows both files I changed:
I moved function status_printf_more to wt-status.h.]
Первый показывает , что вы изменили , а второй показывает , что они изменили ,или в этом случае, что я переехал. Это изменения, которые Git объединяет, по одной паре файлов за раз.
К сожалению, для вашего случая это выглядит так: вы изменили код, а они удалили код из одного файла - в моем случае из wt-status.c
. Вы полагаете, что они не просто удалили код, они вместо этого переместили код из файла orig-file.ext
в new-file.ext
, как я переместил некоторый код. Вам придется просмотреть весь вывод diff, чтобы найти файл, в который они переместили код.
2 Если при этом будет напечатано большечем одна база слияния, вы, вероятно, выполнили рекурсивное слияние , и все становится сложнее. Нет единственно правильного способа продолжить, хотя вы можете использовать каждую базу слияния здесь, если хотите. Обратите внимание, что если вы используете метод git grep
, вам ничего не нужно!
Использование git grep
Допустим, что независимо от того, что происходит с кодом - независимо от того, куда он перемещается, с wt-status.c
на _______, в нем обязательно будет слово status_printf_more
. Тогда:
$ grep -e status_printf_more move-code
покажет все файлы в tip commit их ветви - в данном случае, в commit J
- с соответствующей строкой,(Параметр -e
не является строго обязательным, я просто использовал его здесь.) Вот что происходит в моем случае:
move-code:wt-status.c: status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one);
move-code:wt-status.c: status_printf_more(s, c, "%s%.*s%s -> %s",
move-code:wt-status.c: status_printf_more(s, c, "%s%.*s%s",
move-code:wt-status.c: status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
move-code:wt-status.c: status_printf_more(s, GIT_COLOR_NORMAL, "\n");
move-code:wt-status.c: status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
move-code:wt-status.c: status_printf_more(s, branch_status_color, "%s", on_what);
move-code:wt-status.c: status_printf_more(s, branch_color, "%s\n", branch_name);
move-code:wt-status.h:static void status_printf_more(struct wt_status *s, const char *color,
Прямо здесь, у нас есть ответ: функция теперь находится в wt-status.h
.
Использование git blame
git blame
обычно работает в неправильном направлении: от последнего коммита (или некоторого коммита, который вы указываете) назад, что является естественным для Gitнаправление. Однако у него есть опция --reverse
. Он также имеет опцию - -C
- для поиска кода, перемещенного или скопированного по файлу за один конкретный коммит. 3
В моем случае файл, которыйЯ изменил код, из которого, как мне кажется, move-code
переместил код, это wt-status.c
. Поэтому я могу использовать:
git blame -C --reverse ${H}^..move-code wt-status.c
Если я это сделаю, я смогу найти (некоторые из) перемещенные строки и имя wt-status.h
, где я на самом деле их переместил. Но есть много выходных данных. Я могу сократить его, используя номера строк и параметр -s
:
$ git blame -C -s -L 112,139 --reverse ${H}^..move-code wt-status.c
git blame -C -s -L 112,139 - обратный $ {H} ^ .. код состояния перемещения. c
d16f83787f3 wt-status.c 112) va_end(ap);
d16f83787f3 wt-status.c 113) }
d16f83787f3 wt-status.c 114)
d16f83787f3 wt-status.h 115) static void status_printf_more(struct wt_status *s, const char *color,
d16f83787f3 wt-status.h 116) const char *fmt, ...)
d16f83787f3 wt-status.c 117) {
d16f83787f3 wt-status.c 118) va_list ap;
d16f83787f3 wt-status.c 119)
d16f83787f3 wt-status.c 120) va_start(ap, fmt);
[more stuff, snipped]
Обратите внимание, что только две строки кажутся "взятыми" wt-status.h
, но в этом случае этого достаточно.
Обратите внимание, что диапазон коммитов запускает один коммит до commit $H
(база слияния, которую мы нашли ранее). Это не всегда необходимо, но в этом случае, поскольку коммит, в котором я переместил код, является коммитом, который идет сразу после $H
, поэтому мне нужно, чтобы Git также посмотрел на сам коммит $H
.