从master合并后如何找出分支上的变化?

How to find out what changes on a branch after merges from master?

我分叉了 master 一个分支。有时我将 master 合并到我的分支以进行更新。

       /--B--D--G--H--J--L--M--> dev (HEAD)
      /        /     /
-----A--C--E--F--I--K---> master

如何只显示我的分支上的更改,不包括合并中的更改? IE。仅在提交时显示差异 BDHLM.

git diff A 不起作用,因为它包含来自 master.

的合并更改

顺便说一句,如果有人知道无需继续向下滚动日志即可快速找到 A 的方法,我将不胜感激。

我不清楚你在找什么。但请注意:

  • 提交存储快照。这意味着提交 K 有一个完整的源代码树,它独立于提交 ABC 等中的任何内容。同样,提交 J 有一个完整的快照,独立于 AK 或任何其他提交中的任何内容。

    (此处“独立”一词的意思是,如果您要求 Git 检索提交 J,那么您是否以某种方式设法更改提交 K 之后就没有关系了J。实际上 不可能 更改任何提交;git commit --amend 似乎 更改提交,但实际上没有。)

  • 在两个特定的提交上使用 git diff,Git 提取两个快照中的每一个,并进行比较。

简单的差异

因此,git diff K M 将向您展示 master 当前提示(提交 K)和 dev 当前提示(提交 M).

你也可以只拼这个git diff master dev

这可能是您想看到的(同样,我也不是很清楚)。所以 答案 1:git diff master dev.

多个独立差异

另一方面,也许您想要的是为提交 B 显示一个差异,为提交 D 显示一个差异,为提交 H 显示一个差异,为 H 显示一个差异提交 L,以及提交 M 的一个差异。也就是说,您希望一次看到每个 non-merge 提交,与其(单个)parent.

相比

您可以 git diff A Bgit diff B D,等等。但您也可以只使用 git show Bgit show D,等等。这将提交显示为更改,而不是快照。

您可能想知道如果提交存储快照而不是更改,这怎么可能。 (这让很多人感到困惑,因为大多数 other 版本控制系统实际上会进行存储更改。)这个 apparent 矛盾的答案是 git show 查找和你画的一样。

再次查看提交 B。它之前有什么提交?也就是说,哪个提交在它的左边,向左跟随行?提交 B 只有一个可能的祖先,那就是它的单个 parent,提交 A。所以 git show B:

  1. 提取提交 A 的快照,然后
  2. 提取提交 B 的快照,然后
  3. 比较这两个快照。

同样,提交 M 只有一个直接祖先 (parent),那就是提交 L。所以 git show M:

  1. 提取 L 的快照,然后
  2. 提取 M 的快照,然后
  3. 比较这两个快照。

如果这是您想要的,那么有趣的问题就变成了:您如何在 BDH 中找到每个提交的 ID , L, 和 M 序列 ?这个问题的答案有点复杂,但关键命令是git rev-list,这与git log本质上是相同的命令。这些命令(git loggit rev-list 两者)所做的是 遍历提交图 。也就是说,您选择一些起点——在本例中,提交 Mdev 的尖端——并告诉 Git 向后遍历提交,查看每个提交的 parents.

问题是,当您点击合并提交时,例如提交 J,Git 返回到其 [=346= 的 all ]s。您希望将 Git 限制为仅查找 parent,即您进行合并提交时分支 dev 的顶端。 Git 有一个标志,拼写为 --first-parent。这告诉 git rev-list 仅遵循每个合并提交的 first parent。

我们也想跳过合并,所以我们可以添加--no-merges(这不会影响walking-back过程,它只是限制打印的修订ID以排除合并)。

这导致 答案 2a:git rev-list --first-parent --no-merges ^A dev(我们稍后会谈到“非 A”部分)。

现在,实际上将它与 git rev-list 一起使用有点痛苦,因为现在我们必须获取每个提交 ID,然后在其上添加 运行 git show。不过,还有一种更简单的方法,因为 git rev-listgit log 本质上是相同的命令 ,而 git log -p 将每个提交显示为 补丁,和git show.

做的事情差不多

这导致 答案 2b:git log -p --first-parent --no-merges ^A dev。我们也不一定需要这里的 --no-merges;见下文。

合并提交和合并差异

git show 做的一件特别的事情,git diff 没有,就是处理显示 merge 提交的情况,例如提交 J。提交 J 两个 parent,即提交 HK (顺便说一下,这意味着您提交了 K 在提交 J :-) 之前。如果您 运行 git diff H J,Git 将提取 HJ 的快照和 comp重新他们。如果您 运行 git diff K J,Git 将提取 KJ 的快照并进行比较。但是如果你 运行 git show J,Git 将:

  1. 提取 H 的快照,然后
  2. 提取 K 的快照,然后
  3. 提取 J 的快照(合并),最后
  4. 产生Git所谓的组合差异

第 4 步的组合差异试图以紧凑的方式显示从 H KJ 的变化.在压缩更改时,Git 会丢弃 版本在 H K 中的任何文件匹配 J.

中的版本

也就是说,假设文件 README.txtHJ 有一些变化。但假设 README.txtKJ 中的 相同 。换句话说,当您执行 git merge 时,您正在从 K 中获取更改以进行 J,并且没有从另一侧对 README.txt 进行更改的合并。这意味着 README.txt 完全匹配一个“传入方”,因此组合差异 完全忽略文件 .

这意味着合并的差异通常什么也没有显示,即使合并在新快照中发现了一些变化。要查看这些更改,您必须进行 两次 比较,而不仅仅是一次。你必须从 HJ 做一个差异,从 KJ 做另一个差异,而不是依赖组合的差异。

当使用 git log -p 时,您还可以通过在选项中添加 -c--cc 来查看合并的组合差异。但是如果你要求这个,Git所做的实际上简单得可笑:它根本不费心去显示差异.

所以这导致 答案 2c:git log -p --first-parent ^A dev。我们所做的只是删除 --no-merges:我们现在将看到每个合并的 日志消息 ,但没有差异。

这是什么^A东西?

这也与您的另一个问题有关:

BTW, I will appreciate if anyone knows a quick way to find A without keep scrolling down the log.

答案是为提交 A 创建一个 符号名称 。找到它的 ID 一次,然后选择一个名称,例如 devmaster(但不要使用它们,因为它们正在使用中!)。

这就是全部 devmaster :它们只是提交的符号名称,加上额外的 属性,作为分支名称,您可以 git checkout 符号名称并在分支“上”结束。你也可以给 A 一个 branch-name。您将需要确保您没有 git checkout 这个分支并对其进行提交,因为如果您这样做,您将增长一个新分支,而不是仅仅让 branch-name 指向提交 A.

或者,您可以创建一个 tag-name 指向提交 A。这几乎与分支名称完全相同。这两个区别是:

  1. 这是一个标签名称:您不能将其作为分支签出,因此不能意外更改它。
  2. 这是一个标签名称:如果你git push --tags你会把它作为标签发送到上游,然后其他人也会有它。

在这种情况下,第 1 点对您有利,而第 2 点可能不利,因此由您决定优势(不能意外更改)是否值得冒险(可能会意外发布)。

如果您有A的ID,那么,您可以:

$ git tag A <id-of-commit-A>

现在您有了 名称 A。 (您可以稍后 git tag -d A 删除它,但如果您不小心发布了它,您可能会继续从上游取回它。)

回到 git log 命令中 ^A 字符串的问题,^A 所做的只是告诉 git rev-list(因此 git log)到 停止 在到达提交 A 时遍历提交图。提交也未显示(对于 git rev-list,未打印;对于 git log,未显示在日志输出中)。前缀 ^ 符号是“not”的缩写,即“让我从 dev 到达所有提交,但 notA 到达” .添加 --first-parent 使得 Git 仅遍历每个合并的 first parent,这样我们就不会进入从 master 合并的提交.

^A dev 语法也可以拼写为 A..dev。请注意,这适用于 git loggit rev-list,但对于 git diff.)