Git:在特定分支上的两个提交之间获取提交

时间:2015-12-31 04:36:52

标签: git

我已经阅读了类似场景的一些答案,但不是这个答案。

我需要在git上的特定分支上的两个提交之间生成一个提交列表。因此,应忽略对其他分支的任何提交。知道怎么做吗?

2 个答案:

答案 0 :(得分:9)

""之间的概念这里有点模糊,术语"分支"也没有明确定义(通常使用git,即)。让我看看,尽管存在这些问题(对我来说很难:-)),我是否可以保持这么短:

我将假设"在"之间,你的意思是git提交图中的路径的图论理论。例如,假设我们有这个图片段(我不能为节点之间的弧绘制箭头,但假装每个-/\都有一个箭头在左侧,以便提交指向左前,下左,或左上,到他们的前任):

              o - o
            /       \
... - A - o - o - o - B   <-- branch-x
            \   /
              o - o - C   <-- branch-y

我已经确定了三个特定提交 - ABC - 并提供了两个分支提示名称branch-xbranch-y分别指向提交BC。出于git-usefulness的目的,我们还假设标签A指向提交A,因此我们不必拼出其SHA-1。

您可以通过三种可能的路径从提交A获得提交B。一个从B开始,一直到顶行,然后下降到中间,然后向左移动到A。一个从B开始,直接离开,最终到达A。最后一次从B开始,向后退一步,向下,向左,向上和向左,再向左移动到达A

如果您没有名为A..branch-x的标记,Git会为您提供特殊的双点语法A(或在节点A的实际SHA-1中替换),对于大多数命令,表示他们应该从B返回到A 所有可能路径上的所有节点,通常包括节点B本身,不包括节点A。这几乎你想要的只是不完全,因为你想要排除在其他分支上提交的提交。

这会带来无法回答的(一般性)问题&#34;哪些提交是在哪个分支上进行的?&#34;

Git试图告诉你这个问题无效:你不应该关心;您应该只关心所有这些提交是否可以从节点B访问。 Git实际上通常是正确的,但通常&#34;不是&#34;总是&#34;。不幸的是,我还没有找到任何好的方法来描述你应该(或做)关心的时间(实际的好例子会对我试图写的文本有所帮助)。

与此同时,让我们继续吧。从上图中可以清楚地看出,下线的提交都是在branch-y&#34;上进行的。这里有一个问题,因为似乎清楚,但实际上可能不是 true 。考虑如果我们重新绘制图形会发生什么:

              o - o
            /       \
... - A - o - o - o - B   <-- branch-x
            \   /
              o
                \
                  o - C   <-- branch-y

这次似乎创建branch-y只是为了保持两个最低提交。 (如果有另一个分支名称指向单独的第三行提交,则更有可能,因为您的原始问题语句表示要排除所有其他分支 - 而不仅仅是分支{{1 - 在这种情况下,这并不重要。)

无论如何,虽然我不太清楚你的意思是&#34; branch&#34;,也不知道你想要给出这个图表的哪些提交,让我们来看看选择器git实际上提供。 可能正是您的意思之一。

我之前提到过&#34;大多数&#34; git命令使用相同的语法说明符。实际上,大多数git命令要么包含来自branch-y程序的代码,要么只是运行git rev-list程序,其作用是选择对象(通常是提交对象)以获取要使用的提交ID列表。它也是你想要用于任何脚本的命令。

rev-list命令has a dizzying number of options,有很多可以帮助进行各种图形遍历。我认为,这里最有趣的两个是--first-parent--not

使用--first-parent

让我们先考虑--first-parent。检查上面的图表(布局:它们可能看起来不同但拓扑结构,它们相同)。请注意,它在 merge 提交,如节点B本身和节点向左一步B,路径分叉。这是因为它只有具有多个传出弧的合并提交(这实际上是合并提交的定义:它是一个有两个或更多父节点的节点)。

当git进行合并提交时,它会将各个传出弧编号为多个父节点。第一个弧是特殊的:它是提交时当前分支。也就是说,当你执行git merge <sha-1-or-equivalent>时,你当时在某个分支上, 1 并且当时当前提交的SHA-1成为&#34;第一个父亲&#34 ;新的合并提交。额外的父母(合并的ID,通常只有一个,但git允许更多)是第二个,第三个,依此类推。

使用--first-parent标志告诉git只遍历第一父弧。因此,git rev-list --first-parent branch-x将从提交B开始,然后找到它的第一个父级(我们无法从上面的图表中看出哪一个是第一个),首先是(并且只有)父,依此类推,一直回到root提交。

这可能是你想要的,也可能不是你想要的(尽管它并没有帮助&#34;&#34之间的概念)。

使用--not

现在让我们看看--not标志。 2 通常,git rev-list <SHA-1-ID-or-name>生成从给定SHA-1可到达的所有提交的集合(解析a首先根据需要命名为ID。也就是说,它遵循回到所有根的所有路径。结果是一组SHA-1 ID。使用--not会使rev-list 排除这些ID。就其本身而言,这个否定集是没用的,但是当与正常(非否定)集合结合时, 是有用的。事实上,A..B首先是如何工作的:rev-list首先生成从B可到达的所有提交的集合,然后减去可从中获取的所有提交的集合。 A

因此,根据您的意思&#34;排除其他分支上的所有提交&#34;,可能就是您想要的是:

git rev-list branch1 --not branch2 branch3 ... branchN

您只需在branch1之后列出--not以外的每个分支。

如果我们最后一次查看我们的图表,让我们看看branch-x --not branch-y选择了哪些提交:

              o - o
            /       \
... - A - o - o - o - B   <-- branch-x
            \   /
              o - o - C   <-- branch-y

提交C显然可以从branch-y到达,最下面一行的提交也是如此。 A右侧的提交也是可以访问的,就像提交A本身和所有早期提交一样。其余提交 <{1}} <{1}}

} {}} / p>
branch-y

请注意,branch-xB来包含&#34;剪辑点&#34; (如果我可以称之为那个);添加 o - o / \ - o - o - B <-- branch-x 会在原始图表中rev-list之后放回节点(但--boundary本身仍然被剪掉)。

(基于您修改过的问题,--boundary可能就是您想要的,您只需要获取所有分支的列表,git for-each-ref --format '%(refname:short)' refs/heads是正确的脚本命令。单独列出要保留其节点的一个分支,将其余分支放在A后面,然后运行A。)

1 即使您在匿名分支上(换句话说,在&#34;分离的HEAD&#34;模式中),这也是有效的。一些git命令会说你不在任何分支上,但是你仍然在使用与 build 分支相同的git内部。在这种情况下,您当前的分支根本没有名称。

2 技术上--not只是稍微翻转一下,标记后续的SHA-1或标识符参数被否定。如果他们已经有一个前缀--not符号,那么他们就会成为正面的#34;参考,否则他们成为负面的参考。因此git rev-list表示&#34;是x,没有y,是z&#34;而--not表示&#34;是x,没有y,没有z&#34;并且^表示&#34;是x,没有y,是z&#34;,例如。

答案 1 :(得分:6)

您可以使用以下命令轻松找到提交列表

git log branch_name commit_x..commit_y

例如,

git log dev HEAD~20..HEAD~10将显示分支dev的10 th 到20 th 提交的列表。

您还可以通过git log的参数过滤所需的任何内容。

您还可以使用git log dev HEAD~20..HEAD~10 >> logs.txt

将此日志存储到文件中