显示最初在分支上进行的提交(过滤掉合并的提交)

Show commits made on branch originally (filter out merged commits)

在一个有多个分支的 git 仓库中,我将一个功能分支合并到 master 中,然后从 master 合并回功能分支。 我现在如何使用 git log 仅查看在功能分支上所做的那些提交? 我需要过滤掉对其他分支所做的那些评论,即使它们已被合并回来进入这个功能分支。

$ git status
On branch some_feature

$ git checkout master
$ git merge some_feature
$ git checkout some_feature
$ git merge master

$ # Lots of coding and days gone by

$ git log --some_magic_here

如果重要的话,我想要 运行 git log 的机器与用于编写该功能的机器不是同一台机器。我需要此功能来对其他开发人员的工作执行 代码审查。按 author=SomeDev 过滤没有帮助,因为我正在审查一个特定的功能分支。

TL;DR:您需要 --first-parent(参见 git rev-list documentation)和其他一些选择标准。

Git 不跟踪 "on which branch a commit was actually made"。例如,假设您在分支 B 上并且您这样做:

$ git checkout --detach B
[message about "detached head"]
[edit some file here, and git add result]
$ git commit -m message
$ git branch -f B HEAD
$ git checkout B

这将向分支 B 的顶端添加一个新提交,即使该提交根本不在分支上。或者,假设有两个分支 B1B2 都指向提交 C7:

... <- C5 <- C6 <- C7   <-- B1, B2

如果您进行新提交 C8,其 parent 为 C7,则使分支 B2 指向 C8,该提交现在开始分支B2。如果您在分支 B1 上进行了新提交,则 B1 也指向 C8,但如果您随后使用 git resetB1 移回指向C7,不再可能告诉 1 提交最初是在 B1 而不是 B2.

综上所述,如果您在合并分支时保持良好的一致性,则可以在提交图上执行图遍历,找到提交 "between" 特定的合并,然后处理一个特定的 parent链,以检测提交是 可能 done-on 的分支。例如,如前所述,使用 featuremaster,假设提交始终在 feature 上进行,然后 feature 定期合并到 master--no-ff 以确保 master 上的每个 merge-commit 都是真正的合并提交,而不仅仅是 fast-forward。然后图形模式将如下所示:

... --X--*------*-o---o-----*--*   <-- master
       \       /   \       /  /
        A--B--C--D--E--F--G--H     <-- feature

(其中 "time" 随着向右移动而增加,* 节点是在分支 master 上进行的合并提交,o 节点代表分支上的其他提交大师)。

在这种情况下,您可以通过从 feature 的提示(提交 H)开始并仅遍历 [=178] 来找到(可能)在 feature 上进行的提交=] 提交——这些是 AH——然后丢弃可以通过仅遵循 master 上的 first-parent 提交找到的提交。第二个标准消除了上面标记为 X 的提交(以及任何之前的提交)。 (注意在merge commit E的情况下,它的first parent是D,它的second parent 是在 master 左侧的 o 提交。对于 master 上的所有合并, 第一个 parent 在同一行,second parent 在 feature 的左下方。)

这是否足够取决于您(和其他人)在提交过程中的纪律程度。如果 master 有自己的内部 branch-and-merges,你可能有一个更像这样的图表:

... --X---Y---*-----*-o---o---------*--*   <-- master
       \   \ /     /   \           /  /
        \   Z     /     \         /  /
         \       /       \       /  /
          A--B--C--D------E--F--G--H       <-- feature

现在你不能简单地使用 first/not-first parent 测试,因为提交 YZ 必然是第二个 parent master 上的合并,但请注意,如果不先遍历 E,则无法从 feature 访问 Z,这是合并提交 "directly on" feature 的第二个 parent 提交在 master.

换句话说,我使用稍微特殊的短语 "directly on" a branch 来识别 find-able 的提交,方法是从分支尖端开始,然后仅通过第一个 parent 向后工作.对于 feature,这些(仍然)是 AH,加上不幸的是 X 及其(第一个)parents.

git rev-list 命令,它是 git 非常重要的命令之一——它是 git rev-parse 的 big-sister 版本,也是非常重要的——有一个标志专门实现了这个"directly on"概念,即--first-parent。 rev-list 命令遍历提交图的某些部分2:您告诉它从某个提交开始,它会发现提交的 parents ,以及那些 parents' parents,以及他们的 parents,等等。如果您指定 --first-parent,它只会找到每个提交的 first parent。 (这显然只影响合并提交,因为根据定义,一个 non-merge 提交最多有一个 parent 提交。)

在您的特定情况下,您还说过您希望消除合并 您的 feature 分支,即合并直接在 [=35] 上的提交=].为此,只需使用 --no-merges.

告诉 rev-list 从其输出中排除合并提交

消除提交 X 和更早版本有点困难,但事实证明,如果您知道哪个分支包含像 X,您要排除的是打开的。 --not 参数(包括几个替代拼写)告诉 rev-list 提交给 exclude 的内容。所以:

git rev-list --first-parent feature --not master --no-merges

标识提交列表 "directly on" 功能(AH)但 不是 直接在 master 上(X 和更早版本),然后只列出那些不是合并的(从而消除提交 E)。

您可能希望进一步削减它,例如,可能通过使用提交时间戳(--since--until)。您可能(或可能不)想要更改列出修订的顺序,也许使用 --topo-order 将它们放在图形顺序中例如,r 而不是日期顺序。

将其与 git log

放在一起

幸运的是,git log 使用您的修订说明符有效地调用了 git rev-list。因此,不必先使用 git rev-list,然后以某种方式将结果提供给 git log,您只需将这些相同的说明符提供给 git log。事实上,在确定你想要的限制器时(使用 --since--until 之类的东西),git log 更容易使用,因为一大堆原始 SHA-1 是对人类不是很有用。


1实际上,保留分支运动历史记录的 reflog 确实让您能够分辨,但仅限于一台本地机器;这个问题说这必须在不同的系统上完成,reflogs 不可用。在任何情况下,reflog 条目都会在一段时间后过期,此处感兴趣的条目默认为 90 天。

2当然除非你用--no-walk.