我已阅读git log documentation,但我仍然觉得很难理解--ancestry-path
选项的作用。我看到了调用git log
的不同方法:
$ git log origin/master..HEAD
$ git log --ancestry-path origin/master..HEAD
在第一个命令中,我得到了HEAD上但不在origin/master
上的提交列表,基本上这显示了我的分支上没有合并的内容。
在第二个命令中,我什么都没得到。如果我更改为3个点(...
),它会向我显示某些内容,但我不确定如何理解它。基本上,--ancestry-path
的添加有何不同?它究竟是什么简化?
答案 0 :(得分:22)
Matthieu Moy's answer是正确的,但如果你没有接触到必要的图论,那可能对你没有多大帮助。
首先,让我们快速了解 D 竖立的 A 循环 G raphs或DAG。 DAG只是一个图形(因此是g
),即它们之间的节点和连接的集合 - 这些像铁路线上的火车站一样工作,例如,站点是节点 - 即&# 34;针对" (d
:列车只运行一个方向)并且没有循环(a
)。
线性链和树结构是有效的DAG:
o <- o <- o
或:
o <- o
/
o <- o
\ o
\ /
o
\
o <- o
(想象一下对角连接有箭头,以便它们根据需要指向左上或左下)。
但是,非树图可以有合并回的节点(这些是git的合并):
o <- o
/ \
o <- o \
\ o \
\ / \
o o
\ /
o <- o
或:
o--o
/ \
o--o o--o
\ /
o--o
(我只是在这里进一步压缩符号,节点通常仍指向左侧)。
接下来,git的..
符号并不代表大多数人通常认为的含义。特别是,让我们再看看这个图,添加另一个节点,并使用一些单个字母来标记特定节点:
o---o
/ \
A--o \
\ B \
\ / \
o C--D
\ /
o---o
并且,让我们再做一件事,并且不再仅仅考虑git log
,而是更常见的情况&#34;选择具有祖先的修订&#34;。
如果我们选择修订版A
,我们只会获得修订版A
,因为它没有祖先(左边没有任何东西)。
如果我们选择修订版B
,我们会得到图表的这一部分:
A--o
\ B
\ /
o
这是因为select-with-ancestry意味着&#34;接受我识别的提交,以及我可以通过跟随箭头返回的所有提交。&#34;这里的结果有点有趣,但不是非常有趣,因为没有合并,并且跟随箭头会给我们一个四个提交的线性链,从B
开始并返回{{1 }}
选择带有祖先的A
或C
会让我们更进一步。让我们看一下D
得到的结果:
D
事实上,除了提交 o---o
/ \
A--o \
\ \
\ \
o C--D
\ /
o---o
之外,其他一切都是。为什么我们没有B
?因为箭头都指向左边:我们得到B
,它指向D
,指向两个非字母提交;剩下的那两点,依此类推,但是当我们点击C
左右的节点时,我们不能向右箭头移动,所以我们不能到达B
。
现在,git中的双点表示法实际上只是set subtraction的简写语法。 1 也就是说,如果我们写B
,例如,它意味着:&#34;选择带有祖先的B..D
,然后选择带有祖先的D
,然后从B
中排除(减去)所有提交后,从D
选项中提交一组提交。选择。&#34;
选择带有祖先的B
会获得D
提交的除之外的整个图形。减去B
选项将删除B
,我们之前绘制的两个A
节点以及o
。如果B
不在集合中,我们该如何删除?简单:我们只是假装删除它并说我们已经完成了!也就是说,set subtraction只是为了删除实际上在集合中的东西。
B
的结果就是这张图:
B..D
三点符号不同。它在一个简单的branch-y图中更有用,甚至可能是一棵直树。让我们这次从树状图开始,看看两点和三点符号。这是我们的树状图,其中包含一些用于放置节点的单个字母名称:
o---o
\
\
\
\
C--D
/
o---o
这次我添加了额外的信件,因为我们需要谈谈提交和加入的某些地方,特别是节点 o--I
/
G--H
\ J
\ /
K
\
o--L
和{ {1}}。
使用双点表示法,我们可以获得H
的内容?要找到答案,请从节点K
开始并向后工作。您必须始终向左移动,即使您也向上或向下移动。这些是所选的提交。然后,从节点L..I
开始并向后工作,找到 un -select的节点;如果你遇到任何早先选择的,扔掉它们。 (最后的清单留作练习,但我会把答案作为一个脚注。 2 )
现在让我们看看三点符号的实际效果。它的作用有点复杂,因为它必须在图中的两个分支之间找到合并基础。合并基础有一个正式的定义, 3 但是出于我们的目的,它只是:&#34;当我们向后追踪图表时,我们会在一些提交时见面。&# 34;
在这种情况下,例如,如果我们要求I
或L
- 两者都产生相同的结果 - git会查找可从 提交到达的所有提交,但不是来自两者。也就是说,它排除了合并基础和所有早期提交,但保留了超出该点的提交。
L...I
和I...L
(或L
和I
)的合并基础是提交I
,因此我们会在L
之后获取内容但不是H
本身,我们无法从H
或H
到达节点J
,因为它不在其祖先中。因此,I
或L
的结果是:
I...L
(请注意,这些历史记录没有加入,因为我们抛弃了节点L...I
。)
o--I
K
\
o--L
现在,所有这些都是普通的选择操作。没有使用H
修改任何内容。 documentation for git log
and git rev-list
- 这两个几乎是相同的命令,除了它们的输出格式 - 用这种方式描述--ancestry-path
:
当给出一系列要显示的提交时(例如
--ancestry-path
或--ancestry-path
),只显示直接存在于提交上的提交commit1..commit2
和commit2 ^commit1
之间的祖先链,即提交 既是commit1
的后代,也是commit2
的祖先。
我们在这里根据提交DAG定义祖先:如果第二个提交的箭头指向第一个,则第一个提交是直接祖先。如果第二个指向第一个通过某个提交链,那么间接祖先。 (出于选择目的,提交也被视为其自身的祖先。)
Descendants (有时也称为 children )的定义类似,但是与图中的箭头相反。提交是另一个提交的子(或后代),如果它们之间有路径。
请注意,commit1
的描述是关于使用双点表示法而不是三点表示法,可能是因为三点表示法的实现在内部有点奇怪。如前所述,commit2
将两个提交中的合并基础(或基数,如果有/多个)排除(就像使用前导--ancestry-path
一样)合并基础是一个发挥&#34;必须是孩子的&#34;角色。我将提及B...D
如何使用此功能,但我不确定它在现实世界中是否有用#34;实例
这在实践中意味着什么?嗯,这取决于你给出的参数,以及实际的提交DAG。让我们再次看一下时髦的循环图:
^
假设我们在 --ancestry-path
请求 o---o
/ \
A--o \
\ B \
\ / \
o C--D
\ /
o---o
{em>}。这意味着我们接受提交B..D
及其祖先,但排除--ancestry-path
及其祖先,就像我们之前看到的那样。现在让我们添加D
。我们之前的所有内容都是B
的祖先,这仍然是正确的,但是这个新标志说我们必须还抛弃不 --ancestry-path
的孩子。
节点D
有多少个孩子?好吧,没有!所以我们必须抛弃每个提交,给我们一个完全空列表。
如果我们在没有特殊B
符号的情况下请求B
该怎么办?这为我们提供了从B...D
或 --ancestry-path
可以访问的所有内容,但排除了从D
和 B
可以访问的所有内容:
D
这与B
相同,只是我们也获得了节点 o---o
\
\
B \
\
C--D
/
o---o
。
[注意:关于B..D
与B
混合的部分在2016年4月到2017年2月之间差不多有一年的时间是错误的。已经修复了注意到&#34;必须是子&#34; part从 merge base(s)开始,而不是从--ancestry-path
表示法的左侧开始。]
假设我们在这里添加B...D
。我们开始使用我们为B...D
而不是--ancestry-path
获得的相同图表,但是 discard 项目不是合并基础的子项。合并基数是B...D
左侧的--ancestry-path
。顶行o
提交不是此节点的子节点,因此它们将被丢弃。同样,与祖先一样,我们认为一个节点是它自己的子节点,所以我们将保留这个节点本身 - 给出这个部分结果:
B
但是,当我们(或o
)丢弃此合并基节点的子节点时,合并基节点本身( B
/
o C--D
\ /
o---o
的左下方)不在{ {1}}首先是图表。因此,最终结果(实际在Git 2.10.1中测试)是:
--ancestry-path
(同样,我并不确定这在实践中有多么有用。起始图再次是B
:从可以提交的所有内容,减去从两个提交的一切都可以提交:这可以通过从每个合并库开始丢弃,如果有两个或更多。子检查代码也处理提交列表。它保留了的所有内容如果有多个合并基础,则 任何合并基础的子项。请参阅revision.c
中的函数B...D
。)
B
C--D
/
o---o
或B...D
的最终操作(包含或不包含limit_to_ancestry
)取决于提交图。要预测它,您必须绘制图形。 (使用X..Y
,也许使用X...Y
,或使用为您绘制图表的查看器。)
1 --ancestry-path
中有一个例外,它对git log --graph
和--oneline --decorate --all
进行了自己的特殊处理。当您不使用git diff
时,您应该忽略它的特殊处理。
2 我们从X..Y
开始,左侧是X...Y
,git diff
和I
。然后,当我们从o
返回时,我们会失去H
和G
,因此结果只是H
。
3 正式的定义是合并基础是 L owest C ommon A ncestor,或者LCA,图中给定节点。在一些图中,可能存在多个LCA;对于Git,这些都是合并基础,G
将排除所有这些基础。
为我绘制的图表运行L
是有趣/有益的。这里的这些提交哈希不仅取决于图本身,提交,还取决于提交提交的时间戳,因此如果你构建同一个图,你会得到不同的哈希值,但是这里是我修改答案以修复o--I
与X...Y
交互的说明时所得到的:
git rev-parse B...D
但我们可以看到这些是--ancestry-path
,B...D
和合并基础,使用更多命令:
$ git rev-parse B...D
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2
7f0b666b4098282301a9f95e056a646483c2e5fc
^843eaf75d78520f9a569da35d4e561a036a7f107
和
D
和
B
确实会出现具有多个合并基础的图形,但它们构建起来有点困难 - 简单的方法是使用&#34; criss cross&#34;合并,运行$ git rev-parse B # this produces the middle hash
7f0b666b4098282301a9f95e056a646483c2e5fc
。如果你遇到这种情况并运行$ git rev-parse D # this produces the first hash
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2
,你会看到几个否定的哈希值,每个合并基数一个。运行$ git merge-base B D # this produces the last, negated, hash
843eaf75d78520f9a569da35d4e561a036a7f107
,您将看到相同的合并基础集。
答案 1 :(得分:2)
正如文档所述,--ancestry-path
删除了不属于origin/master
后代的提交。如果您有一个本地的,未合并的分支,并且此分支基于早于origin/master
的提交,则不会显示此分支中的提交,因为这些提交不是origin/master
的后代。 / p>