获取修改后笨拙的新位置



有时我会看到这样的合并冲突:

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

如果我手动环顾四周,我可以找到新位置,但我不知道代码是否已移动或同时更改和移动(真正的冲突)。我只能重复上游移动,看看除了原始更改之外,我是否在其上进行了任何更改。

假设代码与位置无关,如何更轻松地解决此问题?

澄清一下,如果新位置位于不同的文件中,则往往会发生这种情况。

Git 的合并操作,在你看到冲突的地方,是逐个文件工作的。 如果文件orig-file.ext中的某些代码现在在文件new-file.ext中,git merge在这里不会帮助您。1

你可以做的一件事是使用git blame(又名git annotate)来查看在其分支上发生的提交,以查看代码移动到的位置。 您将需要在此处查阅git blame文档。 但是,默认情况下,要轻松找到新代码,您需要知道新文件的名称。 然后,Git 将帮助您找到原始文件的名称。 但是您已经知道原始文件的名称:您想要新文件的名称。 幸运的是,有一个git blame选项可以在这里提供帮助——至少是潜在的。 这里没有什么是可以保证的。

您可以做的另一件事是使用git grep. 这实际上可能是你最好的选择。 如果您确定新代码中的某些行不会更改,git grep将允许您在另一个分支的提示提交中搜索代码。 这将立即告诉您新文件的文件名。


1如果整个文件被重命名,Git在这里帮助你,因为它会将分支中对文件的更改与其分支中的文件重命名相匹配。您将获得修改/重命名冲突。 但这不是这里发生的事情。


关于合并要记住的事项

我做了一个具有相同问题的示例合并,通过在 Git 的 Git 存储库克隆中对我从master所做的两个分支进行冲突的更改:

$ 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通过将一些合并基提交与两个分支提示提交进行比较来工作,以查看您更改了哪些内容,并查看它们更改了哪些内容。 (请记住,每次提交都会保存所有文件的完整快照

就我而言,我只进行了两次提交,一次在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打印出此合并基提交的哈希 ID:

$ git merge-base --all move-code change-code
566a1439f6f56c2171b8853ddbca0ad3f5098770

Git 在此处打印的哈希 ID 是合并库的哈希 ID。阿拉伯数字

现在,找到合并库后(使用git merge-base或通过目测git log --graph打印的图形或任何你可能用来查找它的东西),您可以通过运行两个git diff命令来查看Git认为需要合并的内容

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,就像我移动了一些代码一样。 您必须在此处查看整个差异输出,以找到他们将代码移动到其中的文件。


2如果这会打印多个合并基础,则您可能已经执行了递归合并,事情变得更加困难。 没有一种正确的方法可以继续,尽管如果您愿意,可以在此处使用每个合并基础。 请注意,如果您使用git grep方法,则不需要任何这些东西!


使用git grep

假设无论代码发生什么情况 — 无论它移动到何处,从wt-status.c到 _______,它肯定会包含单词status_printf_more。 然后:

$ grep -e status_printf_more move-code

将显示其分支的提示提交(在本例中为提交J)中具有匹配行的所有文件。 (-e选项不是绝对必要的,我只是在这里使用它。 以下是我的情况:

move-code:wt-status.c:  status_printf_more(s, c, "%s%.*s%sn", 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, "%sn", 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 --reverse ${H}^..移动代码 WT-状态.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,但在这种情况下,这就足够了。

请注意,提交范围在提交$H(我们之前找到的合并基础)之前开始一次提交。 这并不总是必要的,但在这种情况下,因为我移动代码的提交是紧随$H之后的提交,所以我也需要 Git 查看提交$H本身。

最新更新