首页 > 解决方案 > 合并后 Diff 选项卡中的类别是什么

问题描述

我将一个分支合并到另一个分支,在 Diff 选项卡中包含三个包含文件的类别:

与:origin/branch @hash1的差异
与:origin/working_branch @hash2 的
组合 差异

每个类别中有哪些文件?我将 origin/working_branch 合并到 origin/branch。

是这样吗?

Diff with:origin/branch @hash1 - 包含在 origin/working_branch 中更改的文件
Diff with:origin/working_branch @hash2 - 包含在 origin/branch
中更改的文件 Combined Diff - 包含在两个分支中更改的文件

标签: gitgit-extensions

解决方案


其中一些问题是关于Git Extensions的,即图形用户界面。我没有使用它,也无法回答任何关于它的问题。不过,这个问题的另一部分是这些差异的含义/显示,这是基本的命令行 Git。

请记住, Git 中的每个提交都包含您所有文件的完整快照。对于常规(非合并)提交,很容易看到它是如何工作的。每个提交都有一个提交,因此提交一个接一个地串在一起,我们可以画出一行:

...---F--G--H   <-- master

或者:

* 9fadedd637 (master) a commit message subject
|
* 3bab5d5625 some other commit message subject
|
* 1c56d6f57a this commit does something or other
|
:

如示例所示git log --graph(上面的输出是模拟的,与显示的不太--graph一样,但足够接近)。

查看提交,我们可以将其签出,这样我们就可以在工作树中查看所有文件;或者我们可以要求 Git通过将提交与其父级进行比较来显示提交。要查看 commit H,在 的一角master,我们让 Git 提取G H到一个临时区域(在内存中)并比较它们。

中的某些文件GH. Git 对这些文件只字未提。有些文件匹配;对于这些,Git 向我们展示了一个配方——一个差异列表——我们可以通过它更改文件G以便它与.H

合并提交有多个父级。当用较新的提交来绘制它们时,就像我通常在 StackOverflow 上所做的那样,我们会得到这样的结果:

          I--J
         /    \
...--G--H      M--N--...
         \    /
          K--L

提交M是一个合并,与父母JL.

合并就像任何其他提交一样:它拥有完整的快照,而不是一组更改。但是我们可以通过将快照与快照进行比较来视为更改:MJ

git diff <hash-of-J> <hash-of-M>

例如,正是这样做的。

我们还可以通过比较Lvs将其视为更改M

git diff <hash-of-L> <hash-of-M>

任何一个差异都会显示变化。但是,它们将显示的变化是不同的。

因为M是合并提交,所以当查看J-vs-时,我们将看到M的是我们从行完成的工作中引入的更改,在提交KL. 如果我们比较L-vs- ,我们将看到我们从M行所做的工作中带来的变化,在提交和. 请注意,例如,它可能是 的精选,因此工作被重复。在这种情况下,合并只获取了这些更改的一个副本,而不是两个副本,因此我们不会在这两个差异中看到这一点。IJLI

组合差异完全是另一回事。在我看来,这是一种有用的尝试,但并不像人们想象的那么成功。Git 组合 diff 所做的是,首先运行每个单独的 diff(在本例中J为 vsMLvs ) M,然后从该 diff 中丢弃其他 diff 中根本没有更改的任何文件。

也就是说,假设在 中H,我们有四个文件,所有这些文件也在IJKL、 和M中,并且这些属性具有:

  • same.txt在所有六个提交中都是相同的。
  • top-only.txtI在和/或中被修改J,但在K和/或中不被修改L
  • bot-only.txtK在和/或中被修改L,但在I和/或中不被修改J
  • both.txtJ在和 中被修改L

我们还将假设这M不是一个邪恶的合并(参见Evil merges in git?)。也就是说,H-vs- M,如果我们运行该差异,将不会显示任何不是通过IJK或获得的更改L。此外,我们将假设对 的更改both.txt根本没有重叠,因此合并合并了两个更改:没有冲突,没有其他问题。

显然,diffing JvsM没有显示任何内容same.txt:在之后的五个提交中没有任何一个涉及它H

差异JvsM 不显示 top-only.txt,因为 inM中的副本与 中的副本匹配J。此文件的更改发生在I和/或J中,即HJ显示更改进行比较,但是对 的H版本的更改top-only.txt,导致 的J版本top-only.txt,将应用于H版本以获取M版本。

由于相同的原因,差异LvsM 没有显示。 bot-only.txt同样,M副本与副本bot-only.txt匹配L

只有和both.txt之间有区别JM 之间有区别。LM

如果我们要求 Git 提供 的组合差异视图M,Git 现在将:

  • diff Jvs M: 两个文件被改变:top-only.txtboth.txt; 和
  • diff Lvs M:两个文件被更改:bot-only.txtboth.txt.

所以该列表更改了三个文件,一个在两个差异中,一个在一个中。组合的 diff 现在会抛出only-in-one-diff 文件,只留下一个文件both.txt

此时,您可以选择两种不同的组合差异:

  • git diff --cc(默认值:注意两个破折号和两个cs):这不打印任何内容
  • git diff -c(注意一个破折号和一个c):这显示了both.txt.

--cc一个实际上是有道理的。这里的想法是向您展示发生合并冲突的位置。由于没有合并冲突,因此它不显示任何内容。(对于确实显示某些内容的情况,请参阅文档的组合差异格式部分git diff。)

一个-c也很有意义,除了一件事:它没有显示给你top-only.txt,也不显示bot-only.txt。我们在这里真正需要但 Git 没有的是一种将合并结果快照与合并基础快照进行比较的方法。也就是说,我们可能想查看Hvs M。没有内置的操作可以做到这一点,尽管我认为可能应该有。

git show -c在我在这里创建的测试存储库上运行这种组合差异的输出是:

commit 54627dbf51ab9b27c8e5486e4755651688dd5d21 (HEAD -> merge)
Merge: da92a96 e77c224
[snip]
diff --combined both.txt
index 5d51eed,506867f..1f3f391
--- a/both.txt
+++ b/both.txt
@@@ -2,10 -2,10 +2,11 @@@ this fil
  will be
  modified in
  both branches.
 +here is the top-branch change

  The changes
  will be
  combined into
  one file
  in the merge result.
+ here is the bottom-branch change

我们可以手动完成。请参阅下面的脚本(命名git-show-merge以便我可以将其作为 运行git show-merge)。这基本上从指定合并(或默认)的父级中找到合并基础HEAD,然后运行所需的差异。

Git 也有-m选项,可用于git show两者git log。该标志有效地将合并“拆分”为单独的“虚拟提交”。每一个都是单亲提交,其单亲是实际合并中的父母之一。然后,Git 的内部差异引擎可以很好地将提交作为普通差异进行差异化,以向您显示它更改了哪些文件。

那么,在这种情况下,git show -m <hash-of-M>将首先区分J-vs- M,然后L是 -vs- M,然后向您显示两个差异。

看起来 Git Extensions 没有该-m选项,但可以选择显示J-vs- Mdiff 或L-vs- Mdiff,或者 - 如果您选择它 - 组合差异格式之一,可能是--cc变体。

请注意,git log -p默认情况下甚至不尝试显示合并。使用显式-c--cc选项强制git log显示组合差异。


#! /bin/sh
USAGE="[commit]  the merge commit to show - default is HEAD"
. git-sh-setup
case $# in
0) set HEAD;;
1) ;;
*) usage;;
esac
commit=$1
hash=$(git rev-parse ${commit}^{commit}) || exit
set -- $(git rev-parse $hash^@)
[ $# -ge 2 ] || die "$commit ($hash) is not a merge"
set -- $(git merge-base --all $@)
case $# in
0) die "parents of ${commit} are unrelated";;
1) ;;
*) echo "warning: more than one merge base, diffing against $1";;
esac
git diff $1 $hash

推荐阅读