如何在每个更改的文件都与一个父提交相一致时,“git show”合并提交并输出组合差异?

218

进行“简单”的合并(没有冲突的合并)之后,git show通常只显示类似如下的内容:

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

Merge branch 'testing' into master
这是因为在合并时,git show 使用了组合差异格式,该格式省略了与任一父版本一致的文件。

有没有办法强制 git 在组合差异模式下仍显示所有差异?

执行 git show -m 将显示差异(分别使用新版本和所有父版本之间的成对差异),但我更喜欢在组合模式下将其标记为各自列中的 +/- 差异。

2
@ Tilman Vogel:请审核已接受的答案 - 看起来有更好的答案。 - Jayan
2
@Jayan 虽然其他答案因为包含有用的提示而更受欢迎,但它们实际上并没有像执行双向差异一样接近我的问题。我正在寻找三向差异。 - Tilman Vogel
11个回答

285

看一下提交信息:

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

Merge branch 'testing' into master

注意下面这行代码:

Merge: fc17405 ee2de56

取这两个提交 ID 并将它们反转。为了获得您想要的差异,您需要执行以下操作:

git diff ee2de56...fc17405

只显示更改文件的名称:

git diff --name-only ee2de56..fc17405

你可以在gitconfig中添加以下内容来提取它们:

exportfiles = !sh -c 'git diff $0 --name-only | "while read files; do mkdir -p \"$1/$(dirname $files)\"; cp -vf $files $1/$(dirname $files); done"'

然后通过以下方式使用它:

git exportfiles ee2de56..fc17405 /c/temp/myproject

6
看来我的编辑被拒绝了。总之,你的差异显示不出哪些添加来自哪个分支。你也无法区分更改是在第二个分支中添加还是在第一个分支中删除。 - Tilman Vogel
53
更好的解决方案是 git diff fc17405...ee2de56 - 这将显示 ee2de56 上所有可通过 fc17405 的提交到达的更改,我认为这正是您想要的。请注意使用三个点而不是两个。 - Kris Nuttycombe
3
三个点和顺序。你的评论正是我所寻找的,我认为它更符合原帖作者的要求。 - Izkata
@KrisNuttycombe 这种方法在 git log 中似乎不起作用,它仍然显示所有提交,就像 .. 变体一样。对于 log 来说,..... 是相同的,但是对于 diff 来说它们是不同的!?我该如何获取已合并到此分支的提交列表? - Rudie
仅供参考:我更喜欢使用 git diff --stat ee2de56..fc17405 而不是 git diff --name-only ee2de56..fc17405,因为 --stat 显示的信息简单明了,但包含更多有用的细节 :) - kenju
显示剩余2条评论

90

更好的解决方案(由@KrisNuttycombe提到):

git diff fc17405...ee2de56

对于合并提交:

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

展示从fc17405向可达的ee2de56所有更改。注意提交哈希的顺序-与合并信息中显示的相同:Merge: fc17405 ee2de56

还要注意三个点...而不是两个

要获取更改文件列表,可以使用:

git diff fc17405...ee2de56 --name-only

这正是我想要的 +1。 - geedoubleya
这实际上展示了合并冲突的结果,而另一个答案没有展示。 - Pod
这不正确。我不知道为什么它会得到那么多赞。这将显示普通祖先和ee2de56之间的差异,无论其他分支或合并提交中发生了什么。(来自手册:“git diff A...B”等同于“git diff $(git-merge-base A B) B”。) - philipp2100

12

你可以将HEAD设置为合并前的一个提交来创建分支。然后,你可以执行以下操作:

git merge --squash testing

这将合并但不提交。然后:

git diff

6
如果您正在合并提交时,可以显示差异内容:
git diff HEAD~1..HEAD
如果您不在合并提交时,则将HEAD替换为合并提交。这种方法似乎是最简单和最直观的。

1
这不是“合并差异”输出。在此处获取每对父级和HEAD之间的差异不是问题。 - Tilman Vogel

6

你知道是否有任何工具可以配置为以并排的方式显示这样的差异,可能是在几列中(就像在IntelliJ合并冲突解决窗口中一样)?你的答案正是我正在寻找的。 - Max
@Max 不好意思,我没有。谷歌搜索“n-way visual diff”可以提供一些链接,所以我尝试了那些链接。 - max630
这并不仅显示一个提交中的文件,至少如果它是第二个提交。我认为@hesham_EE的解决方案是正确的。 - philipp2100

4
如果您的合并提交是0e1329e5,如上所示,您可以通过以下方式获取包含在此合并中的差异:
git diff 0e1329e5^..0e1329e5

我希望这可以帮助你!

4

我认为您只需要使用“git show -c $ref”命令。在a8e4a59的git存储库上尝试此操作会显示一个合并差异(加号/减号字符在2列之一中)。正如git-show手册所提到的那样,它基本上委托给'git diff-tree',因此这些选项看起来很有用。


4
不,对于“简单”的合并,git show -c $ref 显示的输出与我引用的内容相同,即没有差异。 -c 选择了一种组合的差异模式,该模式与合并提交的默认模式 --cc 非常相似,参见 git help showgit help diff-tree。两者都完全省略了与其任一父版本一致的文件。 - Tilman Vogel
a8e4a59 确实不属于合并提交的范畴。我的意思是,这个合并提交确实包含了一个文件,它与其父版本都有所不同。Documentation/git-fast-import.txt 这个文件从一个父版本中添加了一些东西,同时从另一个父版本中也添加了一些东西。这就导致 git diff-tree --cc 的输出结果非空。然而,只有这种“冲突”情况下的更改才会被显示出来。所有“干净”的合并结果,在 git show -m a8e4a59 中都没有被显示出来。 - Tilman Vogel
1
@TilmanVogel:感谢您指出“无趣”的文件合并被省略在git show -c输出中。(man git-diff-tree确实说:“此外,它仅列出了从所有父级修改的文件。”但我肯定没有注意到。) - Paul Whittaker

3
在您的情况下,您只需要
git diff HEAD^ HEAD^2

或仅为您的提交生成哈希值:

git diff 0e1329e55^ 0e1329e55^2

4
不,这只是在两个父提交之间进行一个普通的双向差异比较。我所要求的是一种模式,它可以同时以同样的方式显示使用 git merge-base HEAD^ HEAD^2HEAD^ 以及 HEAD^2 进行合并的文件的冲突差异。 - Tilman Vogel

3
您可以使用diff-tree命令并加上-c标志。该命令可显示合并提交中更改的文件。
git diff-tree -c {merged_commit_sha}

我从Git-Scm得到了-c标志的描述:

该标志更改合并提交的显示方式(这意味着只有在给定一个命令或--stdin时才有用)。它同时显示每个父提交到合并结果的差异,而不是一次显示每个父提交和结果之间的差异(这就是-m选项所做的)。此外,它仅列出从所有父提交中修改的文件。


3
看起来这是一个关于这个主题的好文章:https://haacked.com/archive/2014/02/21/reviewing-merge-commits/,也许这个也不错:https://longair.net/blog/2009/04/16/git-fetch-and-merge/。 - Devin Rhode

1

我建立了一个通用的方法来执行合并提交的各种操作。

第一步:通过编辑~/.gitconfig为git添加一个别名:

[alias]
  range = "!. ~/.githelpers && run_on_merge_range"
步骤二:在~/.githelpers中定义一个bash函数:
run_on_merge_range() {
  cmd=$1; shift
  commit=$1; shift
  range=$(git show $commit | grep Merge: | awk '{print $2 "..." $3}')
  echo "git $cmd $range $@"
  if [ -z $range ]; then
    echo "No merge detected"
    exit 1
  fi
  git $cmd $range $@
}

第三步: 获利!

git range log <merge SHA> --oneline
git range diff <merge SHA> --reverse -p
git range diff <merge SHA> --name-only

这里可能有很多改进的空间,我只是匆忙地把它们组合起来以解决烦人的情况。请随意嘲笑我的bash语法和/或逻辑。


请注意,在“awk”部分中,您可能需要根据您的需求和正在运行的命令将“...”更改为“..”:https://dev59.com/NXRB5IYBdhLWcg3w7rmV - Nerdmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接