Получить новое местоположение для модифицированного ломоть - PullRequest
1 голос
/ 27 октября 2019

Иногда я вижу конфликты слияния, подобные этому:

<<<<<<< HEAD
...lots of code
line changed by me
...lots of code
line changed by me
...lots of code
=======
>>>>>>> origin/master

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

Если предположить, что код не зависит от местоположения, как мне легче разрешить эту ситуацию?

Пояснение: это имеет место, если новое местоположение находится в другом файле.

1 Ответ

2 голосов
/ 29 октября 2019

Операция слияния 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...