我需要生成一个特定分支上两个提交之间发生的提交列表。因此,其他分支上进行的任何提交都应该被忽略。有什么办法可以做到这一点吗?
“between”这个概念在这里有点模糊,“branch”这个术语也没有被很好地定义(通常与git有关)。尽管存在这些问题,我会尽量让解释简短一些(对我来说可能有点困难 :-) ):
我假设你所说的“between”,是指git提交图中的图论路径。例如,假设我们有以下图形片段(我无法为节点之间的弧线绘制箭头,但是假装每个-
,/
或\
在左侧都有一个箭头,以使提交指向直线向左、向左下或向左上的前任):
o - o
/ \
... - A - o - o - o - B <-- branch-x
\ /
o - o - C <-- branch-y
我已确定了三个特定的提交-A
、B
和C
,并提供了两个指向提交B
和C
的分支顶点名称branch-x
和branch-y
。为了Git的实用性,假设有一个标签A
指向提交A
,这样我们就不必拼写它的SHA-1了。B
到提交A
有三条可能的路径。其中一条从B
开始,向上走到顶线,再返回到中间,然后向左移动到A
。另一条从B
开始,直接向左走,最终到达A
。最后一条从B
开始,退回一步,向下向左,向上向左,再向左移动一次,才能到达A
。A..branch-x
(如果没有名为A
的标签,请替换节点A
的实际SHA-1),对于大多数命令,它表示它们应该访问从B
到A
的所有可能路径上的所有节点,通常包括节点B
本身,但不包括节点A
。这几乎是你想要的,但不完全是,因为你希望排除在其他分支上进行的提交。B
到达。Git通常是正确的,但“通常”并不意味着“总是”。不幸的是,我没有找到任何好的方法来描述你应该(或实际上)关心的情况(实际的好例子对我正在尝试写的文本会有帮助)。branch-y
上进行的”。这里存在一个问题,因为它看起来很清楚,但它可能并不实际为真。考虑一下如果我们这样重新绘制图形会发生什么: o - o
/ \
... - A - o - o - o - B <-- branch-x
\ /
o
\
o - C <-- branch-y
这次似乎是创建了branch-y
分支,只是为了容纳最下面的两个提交。如果有另一个分支名称指向单独的第三行提交,那么更有可能(尽管您原始问题陈述说要排除所有其他分支——不仅仅是分支branch-y
——在这种情况下,这并不重要)。
无论如何,虽然我不太清楚您所说的“分支”是什么意思,也不确定您想要哪些提交,但让我们看一下git实际提供的选择器。有一个重要的选择器可能恰好是您所需的。
我之前提到过,“大多数”git命令都使用相同的语法规范。事实上,大多数git命令包含来自或简单运行git rev-list
程序的代码,其工作是选择对象(通常是提交对象)以获取您想要处理的提交ID列表。它也是您想要进行任何类型脚本编写的命令。
rev-list
命令有大量的选项,可以帮助各种图形遍历。我认为这里最有趣的两个选项是--first-parent
和--not
。
--first-parent
首先考虑--first-parent
。查看上面的图(任何一种布局:它们可能看起来不同,但在拓扑上相同)。请注意,只有在合并提交(如节点B
本身和左侧一步的节点)处,路径才会分叉。这是因为只有合并提交具有多个出站弧(实际上这是合并提交的定义:它是具有两个或多个父节点的节点)。
当git进行合并提交时,它对多个父对象中的每个对象定了编号。第一个弧是特殊的:它是提交时的当前分支。也就是说,当您执行git merge <sha-1-or-equivalent>
时,您在某个分支上,在该时间点上当前提交的SHA-1成为新合并提交的“第一个父级”。其他父节点(合并的ID通常只有一个,但git允许更多)是第二个、第三个等等。
使用--first-parent
标志告诉git仅遍历第一个父级弧。因此git rev-list --first-parent branch-x
将从提交B
开始,然后找到它的第一个父对象(我们无法从上面的图表中确定哪个是第一个),跟随该父对象的第一个(仅有的)父节点等等,一直回溯到根提交。
这可能不是你想要的(虽然它并没有帮助理解“之间”的概念)。
--not
现在让我们看看--not
标志。2通常,git rev-list <SHA-1-ID-or-name>
会生成从给定SHA-1可达的所有提交集合(根据需要先将名称解析为ID)。也就是说,它跟随所有路径回到所有根。结果是一组SHA-1 ID。使用--not
会使rev-list
排除这些ID。单独使用,这个否定集是没有用处的,但与正常的(非否定的)集合结合使用时,它是有用的。事实上,这就是A..B
起作用的方式:首先rev-list
生成从B
可达的所有提交集,然后减去从A
可达的所有提交集。
因此,取决于您对“排除其他分支上的所有提交”是什么意思,您可能需要的是:
git rev-list branch1 --not branch2 branch3 ... branchN
在 --not
后面列出除了 branch1
之外的每个分支。
如果我们最后再看一遍我们的图示,让我们看看哪些提交被 branch-x --not branch-y
选中:
o - o
/ \
... - A - o - o - o - B <-- branch-x
\ /
o - o - C <-- branch-y
显然,提交C
可以从branch-y
到达,包括最底部行的所有提交。位于A
右侧的提交也是可以到达的,以及提交A
本身和之前的所有提交。其余的提交无法从branch-y
到达,但可以从branch-x
提交B
到达,因此结果图如下:
o - o
/ \
- o - o - B <-- branch-x
注意到rev-list
有--boundary
选项,可以包括"snip点"(如果我这样称呼它们的话);添加--boundary
会将节点放回原始图中 A
之后的节点(但是 A
本身被剪切掉了)。--not
,并且您只需要获取所有分支的列表,对于这一点,git for-each-ref --format '%(refname:short)' refs/heads
是适当的脚本命令。将要保留的一个分支分开,将其余的放在--not
后面,然后运行git rev-list
。)
--not
只是翻转了一个标记后面的SHA-1或标识符参数的位,使它们成为否定引用。如果它们已经有一个前缀^
符号,它们将变成“正”引用,否则它们将成为负引用。因此,x ^y z
意味着“是x,不是y,是z”,而x --not y z
意味着“是x,不是y,不是z”,x --not y ^z
则表示“是x,不是y,是z”。git log branch_name commit_x..commit_y
例如, git log dev HEAD~20..HEAD~10
将为您显示分支 dev
的第 10 至 20 个提交记录列表。git log
的参数来筛选所需内容。git log dev HEAD~20..HEAD~10 >> logs.txt
将这些日志存储到文件中。
git rev-list --parents -n <要跟踪的提交数量> <提交哈希>
沿着父提交进行跟踪。 - orb