合并后差异选项卡中的类别是什么

What are categories in Diff tab after merge

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

Diff with: origin/branch @hash1
Diff with: origin/working_branch @hash2
Combined Diff

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

是这样的吗?

差异:origin/branch @hash1 - 包含在 origin/working_branch
中更改的文件 差异:origin/working_branch @hash2 - 包含在 origin/branch
中更改的文件 组合差异 - 包含在两个分支中更改的文件

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

请记住,在 Git 中的每个 提交都包含所有文件的完整快照。对于常规 (non-merge) 提交,很容易看出它是如何工作的。每个提交都有一个 parent 提交,因此提交是 st运行g,一个接一个,在一条我们可以画的线上:

...---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 所显示的不完全相同,但足够接近)。

查看一个提交,我们可以检查它,这样我们就有所有个文件在work-tree 我们在哪里可以看到它们;或者我们可以要求 Git 通过将其与 parent 进行比较来 显示 提交。要查看提交 H,在 master 的末尾,我们 Git 将 G H 提取到一个临时区域(在内存中)和比较它们。

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

中的副本

合并提交有多个 parent。当用较新的提交向右绘制它们时,就像我通常在 Whosebug 上做的那样,我们得到这样的东西:

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

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

合并就像任何其他提交一样:它包含一个完整的快照,而不是一些更改集。但是我们可以查看as变化,通过比较M中的快照和J中的快照:

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

例如,正是这样做的。

我们还可以通过比较 LM:

来将其视为变化
git diff <hash-of-L> <hash-of-M>

任何一个 diff 都会显示 变化 。不过,它们显示的变化是不同的。

由于 M 是一个合并提交,在查看 J-vs-M 时,我们将看到的是我们从一起完成的工作中带来的变化底部 行,在提交 KL 中。如果我们比较 L-vs-M,我们将看到我们从 top 行所做的工作带来的变化,在提交 IJ。请注意,例如,L 可能是 I 的 cherry-pick,因此工作是重复的。在这种情况下,合并仅采用这些更改的 一个副本,而不是两个副本,因此我们不会在[=216=中看到]这两个差异中的任何一个。

A combined diff 完全是另一回事。在我看来,这是一种有用的尝试,但并不像人们希望的那样成功。 Git 组合差异首先是 运行 每个单独的差异——在这种情况下,J vs ML vs M—然后 从这个 diff 中删除 任何 not 在另一个 diff 中完全没有改变的文件。

也就是说,假设在H中,我们有四个文件,都在IJK、[=29=中],和 M,这些属性成立:

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

我们还将假设 M 不是恶意合并(参见 Evil merges in git?)。也就是说,H-vs-M,如果我们运行那个差异,不会显示任何未通过[=41获得的变化=]、JKL。此外,我们假设对 both.txt 的更改根本没有重叠,因此合并合并了两个更改:没有冲突,没有其他问题。

显然,JM 的差异显示 same.txt 没有任何意义:在 H.[=154 之后的五次提交中都没有触及它=]

比较 JM 不显示 top-only.txt 因为 M 中的副本与 J。此文件的更改发生在 I and/or J 中,即比较 HJ 显示更改,但这些更改对 H 版本top-only.txtJ 版本应用于 H 版本以获得 M 版本。

差异 LM 不显示 bot-only.txt 出于同样的原因。同样,bot-only.txtM 副本与 L 副本匹配。

只有both.txtJM的区别L和[=27=的区别].

如果w向 Git 询问 组合差异 视图 M,Git 现在将:

  • diff J vs M:改变了两个文件:top-only.txtboth.txt;和
  • diff L vs M:更改了两个文件:bot-only.txtboth.txt.

因此列表更改了三个文件,一个在两个差异中,一个在每个差异中。合并的 diff 现在 丢弃了 only-in-one-diff 个文件,只留下一个文件 both.txt.

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

  • git diff --cc(默认值:注意两个破折号和两个 cs):这会打印 nothing.
  • git diff -c(注意一个破折号和一个 c):这显示了 both.txt.
  • 的差异

--cc 一个实际上是有道理的。这里的想法是向您展示 合并冲突 的位置。由于 没有 合并冲突,因此它不显示任何内容。 (对于它确实显示某些内容的情况,请参阅 the combined diff format section of the git diff documentation。)

-c 也有道理,除了一件事:它不会显示 top-only.txt,也不会显示 bot-only.txt。我们真正需要的,但 Git 没有,是一种将 merge result 快照与 merge base 快照进行比较的方法.也就是说,我们可能希望查看 HM。尽管我认为可能应该有,但没有内置操作可以做到这一点。

来自 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)。这基本上从指定合并的 parent 中找到合并基础(或 HEAD 作为默认值),然后 运行s 所需的差异。

Git 也有 -m 选项,可用于 git showgit log 两者。此标志有效地 "splits" 合并为单独的 "virtual commits"。每个都是一个 single-parent 提交,其单个 parent 是来自实际合并的 parent 之一。 Git 的内部差异引擎与 diff-ing 一样好,它作为普通差异提交,向您显示它更改了哪些文件。

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

看起来 Git 扩展没有 -m 选项,但可以选择显示 J-vs-M 差异,或者L-vs-M 差异,或者——如果你 select 它——combined-diff 格式之一,可能是 --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=
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 ";;
esac
git diff  $hash