显示分支上最初提交的提交记录(过滤已合并的提交记录)

6
在一个有多个分支的 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过滤并不能帮助我,因为我正在审核一个特定的功能分支。
1个回答

7

简而言之: 您需要使用--first-parent (请参阅git rev-list文档) 和其他一些选择条件。

Git不会记录“提交实际上是在哪个分支上进行的”。例如,假设您在分支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,其父提交为C7,然后使分支B2指向C8,那么该提交现在就在分支B2上。如果您在分支B1上创建了新的提交,则B1也会指向C8,但是如果您使用git resetB1移动回指向C7,则不再可能确定该提交最初是在B1而不是B2上创建的。1 尽管如此,如果您在合并分支时保持良好的一致性,您可以在提交图中执行图遍历,查找“介于”特定合并之间的提交,然后通过一个特定的父链来检测这些提交最可能是在哪个分支上创建的。例如,在描述的情况下,使用featuremaster,假设提交是一致地在feature上进行的,然后使用--no-fffeature定期合并到master中,以确保每个合并提交都是真正的合并提交,而不仅仅是快进提交。然后,图形模式将如下所示:
... --X--*------*-o---o-----*--*   <-- master
       \       /   \       /  /
        A--B--C--D--E--F--G--H     <-- feature

在这种情况下,您可以通过从feature的顶端(提交H)开始遍历仅第一个父提交来查找可能在feature上创建的提交。这些是AH,然后丢弃可通过仅遵循master上的第一个父提交找到的提交。第二个标准消除了上面标记为X的提交(以及任何先前的提交)。 (请注意,在合并提交E第一个父级是D,而其第二个父级位于master上左侧的o提交之上。对于master上的所有合并,第一个父级在同一行上,而第二个父级则在feature下方并向左。)

这是否足够取决于您(和其他人)在提交过程中的纪律性。如果master有自己的内部分支和合并,则可能会出现以下类似的图形:

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

现在你不能像以前那样轻松地使用第一个/非第一个父测试,因为提交Y或Z必定是主分支上合并的第二个父提交,但请注意,没有经过E的情况下,Z是不可从feature到达的,E是一个直接在feature上的合并提交,其第二个父提交在主分支上。换句话说,我使用了有点奇怪的短语“直接在”一个分支来确定可以通过从分支尖端开始,并向后遍历仅第一个父代找到的提交。 对于feature这些仍然是A到H,加上不幸的X及其(第一个)父代。git rev-list命令是git的核心命令之一 - 它是git rev-parse的大姐版本,后者也是非常核心的命令 - 具有明确实现此“直接在”概念的标志,即--first-parent。rev-list命令遍历提交图的某些部分:您告诉它要从哪个提交开始,它会找到该提交的父提交,以及那些父提交的父提交等。如果指定--first-parent,它仅查找每个提交的第一个父提交。(显然,这仅影响合并提交,因为根据定义,非合并提交最多只有一个父提交。)在您的特定情况下,您还表示希望消除合并到您的feature分支中的合并提交,即直接在feature上的合并提交。要做到这一点,请简单地告诉rev-list在其输出中排除合并提交,使用--no-merges。消除提交X及更早提交的难度有点大,但事实证明,并不是那么难,前提是您知道包含类似于X的提交的分支以及您想要排除的分支。--not参数(包括几种替代拼写)告诉rev-list要排除哪些提交。所以:
git rev-list --first-parent feature --not master --no-merges

识别“直接在”功能上的提交列表(从AH),但不直接在masterX及更早)上,然后仅列出不是合并的提交(因此消除提交E)。
您可能还想进一步简化此操作,例如,通过使用提交时间戳(--since--until)来完成。您可能(或可能不)还想更改列出修订版本的顺序,例如使用--topo-order按图形顺序而不是日期顺序进行排序。
将此与git log结合使用
幸运的是,git log有效地使用您的修订说明符调用git rev-list。因此,您可以直接将这些相同的说明符提供给git log,而无需先使用git rev-list,然后以某种方式将结果提供给git log。事实上,在确定要使用哪些限制器(例如--since--until之类的内容)时,git log要容易得多,因为一堆原始SHA-1对于人类来说并不是很有用。
注1:实际上,reflog(保留分支动态历史记录)确实可以让您知道,但仅限于一台本地计算机;问题说必须在另一台系统上进行此操作,在该系统上reflog不可用。无论如何,reflog条目会在一段时间后过期,这里默认为90天。
注2:当然,除非您使用--no-walk

谢谢,这非常有启发性,让我尝试了一些我从未尝试过的方法! - dotancohen

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