祖先路径如何与git log一起使用?

voi*_*ter 17 git

我已经阅读了git log文档,但我仍然觉得很难理解该--ancestry-path选项的作用.我看到了不同的调用方式git log:

$ git log origin/master..HEAD
$ git log --ancestry-path origin/master..HEAD
Run Code Online (Sandbox Code Playgroud)

在第一个命令中,我得到一个HEAD但未打开的提交列表origin/master,基本上这显示了我的分支上没有合并的内容.

在第二个命令中,我什么都没得到.如果我改为3点(...)它会向我展示一些东西,但我不确定如何理解它.基本上,如何添加--ancestry-path任何不同的?它到底简化了什么?

tor*_*rek 27

Matthieu Moy的答案是正确的,但如果你没有接触到必要的图论,那可能对你没什么帮助.

DAG的

首先,让我们快速浏览一下D arected A cyclic G raphs或DAGs.DAG只是一个图形(因此g),即它们之间的节点和连接的集合 - 这些像铁路线上的火车站一样工作,例如,站点是节点 - "定向"(d火车:只运行一种方式)并且没有循环(the a).

线性链和树结构是有效的DAG:

o <- o <- o
Run Code Online (Sandbox Code Playgroud)

要么:

       o <- o
      /
o <- o
      \   o
       \ /
        o
         \
          o <- o
Run Code Online (Sandbox Code Playgroud)

(想象一下具有箭头的对角连接,以便它们根据需要指向左上或左下).

但是,非树图可以有合并回的节点(这些是git的合并):

       o <- o
      /      \
o <- o        \
      \   o    \
       \ /      \
        o        o
         \      /
          o <- o
Run Code Online (Sandbox Code Playgroud)

要么:

     o--o
    /    \
o--o      o--o
    \    /
     o--o
Run Code Online (Sandbox Code Playgroud)

(我只是在这里进一步压缩符号,节点仍然通常指向左侧).

接下来,git的..表示法并不意味着大多数人通常认为它意味着什么.特别是,让我们再看看这个图,添加另一个节点,并使用一些单个字母来标记特定节点:

     o---o
    /     \
A--o       \
    \   B   \
     \ /     \
      o       C--D
       \     /
        o---o
Run Code Online (Sandbox Code Playgroud)

而且,让我们再做一件事,并且不再考虑这个问题,git log而是将"选择具有祖先的修订"的更一般的案例.

选择具有祖先的修订(提交)

如果我们选择修订版A,我们只会修改A,因为它没有祖先(左边没有).

如果我们选择修订版,B我们会得到这段图:

A--o
    \   B
     \ /
      o
Run Code Online (Sandbox Code Playgroud)

这是因为select-with-ancestry意味着"接受我识别的提交,以及我可以通过跟随箭头返回所有提交的提交." 这里的结果有点有趣,但不是有趣,因为没有合并,跟随箭头给我们一个线性的四个提交链,从开始B和返回到A.

但是,选择任何一个CD与祖先相比,都会让我们更进一步.让我们看看我们得到了什么D:

     o---o
    /     \
A--o       \
    \       \
     \       \
      o       C--D
       \     /
        o---o
Run Code Online (Sandbox Code Playgroud)

事实上,这是除了提交之外的一切B.我们为什么不来B?因为箭头都指向左边:我们得到D,指向C哪个,指向两个非字母提交; 剩下的那两点,依此类推,但是当我们只是左右点击节点时B,我们不允许向右箭头移动,所以我们无法到达B.

双点表示法

现在,git中的双点表示法实际上只是集合减法的简写语法.1 也就是说,如果我们编写B..D例如,它意味着:"选择D带有祖先,然后B用祖先选择,然后在从D选择中排除(减去)所有提交后,从选择中给出一组提交B."

选择D与祖先得到整个图形,除了B承诺.减去B选择将删除我们之前绘制A的两个o节点,以及B.B当它不在集合中时我们如何删除?容易:我们只是假装删除它并说我们已经完成了!也就是说,set subtraction只是为了删除实际上在集合中的东西.

结果B..D是这个图:

     o---o
          \
           \
            \
             \
              C--D
             /
        o---o
Run Code Online (Sandbox Code Playgroud)

三点符号

三点符号是不同的.它在一个简单的branch-y图中更有用,甚至可能是一棵直树.让我们从这次树状图开始,看看两点和三点符号.这是我们的树状图,其中包含一些用于放置节点的单个字母名称:

     o--I
    /
G--H
    \   J
     \ /
      K
       \
        o--L
Run Code Online (Sandbox Code Playgroud)

这次我添加了额外的字母,因为我们需要谈谈提交"加入"的一些地方,特别是节点HK.

使用双点表示法,我们得到L..I什么?要找到答案,请从节点开始I并向后工作.您必须始终向左移动,即使您也向上或向下移动.这些是所选的提交.然后,从节点开始L向后工作,找到要取消选择的节点; 如果你遇到任何早先选择的,扔掉它们.(将最终列表留作练习,但我会将答案作为脚注.2)

现在让我们看看三点符号的实际效果.它的作用有点复杂,因为它必须在图中的两个分支之间找到合并基础.合并基地有一个正式的定义,3但我们的目的,它只是:"在那里,向后下面的图时,我们在一些见面点提交"

在这种情况下,例如,如果我们要求L...II...L-既产生相同的结果- git的发现距离可达所有提交或者提交,而不是从两个.也就是说,它排除了合并基础和所有早期提交,但保留了超出该点的提交.

LI(或IL)的合并基础是提交H,所以我们得到的东西H,但不是H它自己,我们不能J从任何一个IL因为它不在他们的祖先到达节点.因此,对于结果I...LL...I是:

     o--I




      K
       \
        o--L
Run Code Online (Sandbox Code Playgroud)

(请注意,这些历史记录没有加入,因为我们抛弃了节点H.)

--ancestry-path

现在,所有这些都是普通的选择操作.没有被修改过--ancestry-path.在对文档git loggit rev-list -These两者几乎相同的命令,除了它们的输出格式描述--ancestry-path是这样的:

当给出一系列要显示的提交(例如commit1..commit2commit2 ^commit1)时,只显示直接存在于commit1和之间的祖先链上的commit2提交,即提交的后代commit1和祖先commit2.

我们定义的祖先在提交DAG的条款在此:第一提交的是一个直接祖先第二,如果第二个有在第一回指向箭头,以及间接的祖先如果第二点回到了第一个通过提交的一些连锁.(出于选择目的,提交也被视为其自身的祖先.)

后代(有时也称为子项)的定义类似,但是与图中的箭头相反.如果提交之间存在路径,则提交是另一个提交的子(或后代).

注意--ancestry-path关于使用双点表示法而不是三点表示法的讨论的描述,可能是因为三点表示法的实现在内部有点奇怪.如前所述,B...D排除(好像带有前导^)两个提交的合并基础(或基数,如果有/多个),因此合并基础是扮演"必须是孩子"角色的基础.我会提到--ancestry-path它是如何工作的,虽然我不确定它在"现实世界"的例子中有多么有用.

实际例子

这在实践中意味着什么?嗯,这取决于你给出的参数,以及实际的提交DAG.让我们再看一下时髦的循环图:

     o---o
    /     \
A--o       \
    \   B   \
     \ /     \
      o       C--D
       \     /
        o---o
Run Code Online (Sandbox Code Playgroud)

假设我们在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
Run Code Online (Sandbox Code Playgroud)

B..D除了我们得到节点之外,这是相同的B.

[注:下面的部分上混合--ancestry-path使用B...D是错了将近一年,2016年4月和2017年二月间已定要注意,"一定要孩子"的一部分从开始的合并基础(S) ,而不是从左边B...D符号的一面.]

假设我们在--ancestry-path这里添加.我们B...D没有得到的相同图形开始--ancestry-path,但随后丢弃不是合并库的子项的项目.合并基地o就在左边B.顶行o提交不是此节点的子节点,因此它们将被丢弃.同样,与祖先一样,我们将节点视为自己的子节点,因此我们保留此节点本身 - 给出此部分结果:

        B
       /
      o       C--D
       \     /
        o---o
Run Code Online (Sandbox Code Playgroud)

但是,当我们(或--ancestry-path正在)丢弃此合并基节点的子节点时,合并基节点本身(在左下方B)首先不在B...D图中.因此,最终结果(实际在Git 2.10.1中测试)是:

        B

              C--D
             /
        o---o
Run Code Online (Sandbox Code Playgroud)

(同样,我真的不知道怎么有用,这是在实践中开始曲线,又是那B...D:一切从可达要么提交,减去一切从到达这两个承诺:这部作品通过丢弃从每一个开始合并的基础,如果有两个或两个以上.校验码的-的孩子也处理提交的清单.它保留一切一个孩子的任何合并基地,如果有多个合并基地.参见功能limit_to_ancestryrevision.c.)

因此,它取决于图形选择器

X..Yor 的最终操作X...Y,有或没有--ancestry-path,取决于提交图.要预测它,您必须绘制图形.(使用git log --graph,也许使用--oneline --decorate --all或使用为您绘制图形的查看器.)


1有一个例外git diff,它有自己的特殊处理X..YX...Y.当你不使用时,git diff你应该忽略它的特殊处理.

2我们从Io左边开始,也是HG.然后我们输了H,G当我们回来工作时L,结果就是这样o--I.

3正式定义是,合并基础是大号亏欠于Ç ommon ncestor,或LCA,在图中给定的节点.在一些图中,可能存在多个LCA; 对于Git来说,这些都是合并基础,X...Y并将排除所有这些基础.

运行git rev-parse B...D我绘制的图表是有趣/ 有益的.这里的这些提交哈希不仅取决于图形本身,提交,还取决于提交提交的时间戳,因此如果你构建同一个图形,你将获得不同的哈希值,但这里是我得到的修改答案以修复与以下内容--ancestry-path交互的描述B...D:

$ git rev-parse B...D
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2
7f0b666b4098282301a9f95e056a646483c2e5fc
^843eaf75d78520f9a569da35d4e561a036a7f107
Run Code Online (Sandbox Code Playgroud)

但我们可以使用更多命令看到这些是D,B和,以及合并基础的顺序:

$ git rev-parse B     # this produces the middle hash
7f0b666b4098282301a9f95e056a646483c2e5fc
Run Code Online (Sandbox Code Playgroud)

和:

$ git rev-parse D     # this produces the first hash
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2
Run Code Online (Sandbox Code Playgroud)

和:

$ git merge-base B D  # this produces the last, negated, hash
843eaf75d78520f9a569da35d4e561a036a7f107
Run Code Online (Sandbox Code Playgroud)

确实会出现具有多个合并基础的图形,但它们构建起来有点困难 - 简单的方法是"纵横交错"合并,在哪里运行git checkout br1; git merge br2; git checkout br2; git merge br1.如果你得到这种情况并运行,git rev-list你会看到几个否定的哈希值,每个合并基数一个.运行git merge-base --all,您将看到相同的合并基础集.

  • 很抱歉这么晚才回到这里......但这是一个非常好的答案.就像读书一样.很好.我觉得很难想象--ancestry-path的用例,但至少我可以看到它如何通过添加1个规则来简化现在的事情. (3认同)

Mat*_*Moy 5

正如文档所述,--ancestry-path删除不是 . 后代的提交origin/master。如果您有一个本地未合并的分支,并且该分支基于早于 的提交origin/master,则该分支中的提交将不会显示,因为这些提交不是 的后代origin/master