简体   繁体   English

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

[英]How does ancestry path work with git log?

I've read the git log documentation , but I still find it very difficult to understand what the --ancestry-path option does.我已经阅读了git 日志文档,但我仍然很难理解--ancestry-path选项的作用。 I see different ways to invoke git log :我看到调用git log的不同方法:

$ git log origin/master..HEAD
$ git log --ancestry-path origin/master..HEAD

In the first command, I get a list of commits that are on HEAD but not on origin/master , basically this shows me what is on my branch that isn't merged.在第一个命令中,我得到一个在 HEAD 但不在origin/master上的提交列表,基本上这向我显示了我的分支上未合并的内容。

In the second command, I get nothing.在第二个命令中,我什么也没得到。 If I change to 3 dots ( ... ) it shows me something , but I'm not sure how to make sense of it.如果我改为 3 个点 ( ... ),它会向我展示一些东西,但我不知道如何理解它。 Basically, how is the addition of --ancestry-path any different?基本上,添加--ancestry-path有什么不同? What exactly does it simplify ?它究竟简化了什么?

Matthieu Moy's answer is correct but may not help you very much, if you haven't been exposed to the necessary graph theory. Matthieu Moy 的回答是正确的,但如果您没有接触过必要的图论,可能不会对您有多大帮助。

DAGs有向无环图

First, let's take a quick look at D irected A cyclic G raphs or DAGs.首先,让我们快速浏览一下d irected循环raphs或DAG的。 A DAG is just a graph (hence the g ), ie, a collection of nodes and connections between them—these work like train stations on rail lines, for instance, where the stations are the nodes—that is "directed" (the d : trains only run one way) and have no loops in them (the a ). DAG 只是一个图(因此g ),即节点和它们之间的连接的集合 - 这些工作就像铁路线上的火车站,例如,车站是节点 - 即“有向”( d :火车只以一种方式运行)并且其中没有循环( a )。

Linear chains and tree structures are valid DAGs (note: newer commits are to the right, in general, here):线性链和树结构是有效的 DAG(注意:较新的提交在右边,一般来说,在这里):

o <- o <- o

or:或:

       o <- o
      /
o <- o
      \   o
       \ /
        o
         \
          o <- o

(imagine the diagonal connections having arrow heads so that they point up-and-left or down-and-left, as needed). (想象一下有箭头的对角线连接,以便它们根据需要向上和向左或向下和向左指向)。

However, non-tree graphs can have nodes that merge back (these are git's merges):但是,非树图可以具有合并回的节点(这些是 git 的合并):

       o <- o
      /      \
o <- o        \
      \   o    \
       \ /      \
        o        o
         \      /
          o <- o

or:或:

     o--o
    /    \
o--o      o--o
    \    /
     o--o

(I'm just compressing the notation further here, nodes still generally point leftward). (我只是在这里进一步压缩符号,节点通常仍指向左侧)。

Next, git's .. notation does not mean what most people usually first think it means.其次,git 的..符号并不意味着大多数人通常首先认为的意思。 In particular, let's take a look at this graph again, add another node, and use some single letters to mark particular nodes:特别是,让我们再次看一下这个图,添加另一个节点,并使用一些单个字母来标记特定节点:

     o---o
    /     \
A--o       \
    \   B   \
     \ /     \
      o       C--D
       \     /
        o---o

And, let's do one more thing, and stop thinking about this as just git log but rather the more general case of "selecting revisions with ancestry".并且,让我们再做一件事,不要将其视为git log ,而是“选择具有祖先的修订”的更一般情况。

Selecting revisions (commits), with ancestry选择修订(提交),具有祖先

If we select revision A , we get just revision A , because it has no ancestors (nothing to the left of it).如果我们选择修订版A ,我们只会得到修订版A ,因为它没有祖先(它的左边没有任何东西)。

If we select revision B we get this piece of the graph:如果我们选择修订版B我们会得到这张图:

A--o
    \   B
     \ /
      o

This is because select-with-ancestry means "Take the commit I identify, and all the commits I can get to by following the arrows back out of it."这是因为 select-with-ancestry 的意思是“获取我确定的提交,以及我可以通过跟随箭头返回的所有提交。” Here the result is somewhat interesting, but not very interesting since there are no merges and following the arrows nets us a linear chain of four commits, starting from B and going back to A .这里的结果有点有趣,但不是有趣,因为没有合并,并且按照箭头为我们提供了四个提交的线性链,从B开始并返回到A

Selecting either C or D with ancestry, though, gets us much further.但是,选择具有祖先的CD会让我们走得更远。 Let's see what we get with D :让我们看看D得到了什么:

     o---o
    /     \
A--o       \
    \       \
     \       \
      o       C--D
       \     /
        o---o

This is, in fact, everything except commit B .事实上,这就是除了提交B之外的所有内容。 Why didn't we get B ?为什么我们没有得到B Because the arrows all point leftward: we get D , which points to C , which points to two un-lettered commits;因为箭头都指向左边:我们得到D ,它指向C ,它指向两个无字母的提交; those two point left, and so on, but when we hit the node just left-and-down of B , we aren't allowed to go rightward, against the arrow, so we can't reach B .这两个点向左,依此类推,但是当我们击中B左下方的节点时,我们不允许逆着箭头向右走,因此我们无法到达B

Two-dot notation两点符号

Now, the two-dot notation in git is really just shorthand syntax for set subtraction.现在,git 中的两点表示法实际上只是集合减法的速记语法。 1 That is, if we write B..D for instance, it means: "Select D with ancestry, and then select B with ancestry, and then give me the set of commits from the D selection after excluding (subtracting away) all commits from the B selection." 1也就是说,如果我们以B..D为例,它的意思是:“选择D with ancestry,然后选择B with ancestry,然后在排除(减去)所有提交后从D选择中给我一组提交从B选择。”

Selecting D with ancestry gets the entire graph except for the B commit.选择具有祖先的D将获得B提交之外的整个图。 Subtracting away the B selection removes A , the two o nodes we drew earlier, and B .减去B选择将删除A 、我们之前绘制的两个o节点和B How can we remove B when it's not in the set?B不在集合中时,我们如何删除它? Easy: we just pretend to remove it and say we're done!简单:我们只是假装删除它并说我们完成了! That is, set subtraction only bothers to remove things that are actually in the set.也就是说,集合减法只会删除实际上在集合中的东西。

The result for B..D is therefore this graph:因此B..D的结果是这个图:

     o---o
          \
           \
            \
             \
              C--D
             /
        o---o

Three-dot notation三点符号

The three-dot notation is different.三点符号是不同的。 It's more useful in a simple branch-y graph, perhaps even a straight tree.它在简单的分支 y 图中更有用,甚至可能是直树。 Let's start with the tree-like graph this time and look at both two- and three-dot notation.这次让我们从树状图开始,看看两点和三点表示法。 Here's our tree-like graph, with some single letter names for nodes put in:这是我们的树状图,其中包含一些节点的单字母名称:

     o--I
    /
G--H
    \   J
     \ /
      K
       \
        o--L

This time I've added extra letters because we'll need to talk about some of the places the commits "join up", in particular at nodes H and K .这次我添加了额外的字母,因为我们需要讨论提交“连接”的一些地方,特别是在节点HK

Using two-dot notation, what do we get for L..I ?使用两点表示法,我们可以得到L..I什么? To find the answer, start at node I and work backwards.要找到答案,请从节点I开始并向后工作。 You must always move leftward, even if you also go up or down.您必须始终向左移动,即使您也向上或向下移动。 These are the commits that are selected.这些是选择的提交。 Then, start at node L and work backwards, finding the nodes to un -select;然后,从节点L开始向后工作,找到要取消选择的节点; if you come across any earlier selected ones, toss them out.如果您遇到任何较早选择的内容,请将它们扔掉。 (Making the final list is left as an exercise, though I'll put the answer in as a footnote. 2 ) (制作最终列表留作练习,但我会将答案放在脚注中。2

Now let's see the three-dot notation in action.现在让我们看看实际使用的三点符号。 What it does is a bit complicated, because it must find the merge base between two branches in the graph.它所做的有点复杂,因为它必须找到图中两个分支之间的合并基 The merge base has a formal definition, 3 but for our purposes it's just: "The point where, when following the graph backwards, we meet up at some commit."合并基础有一个正式的定义, 3但就我们的目的而言,它只是:“当向后跟踪图表时,我们会在某个提交处相遇。”

In this case, for instance, if we ask for L...I or I...L —both produce the same result—git finds all commits that are reachable from either commit, but not from both .在这种情况下,例如,如果我们要求L...II...L两者都产生相同的结果——git 会找到所有可从任一提交访问的提交,但不能从两者中找到 That is, it excludes the merge base and all earlier commits, but keeps the commits beyond that point.也就是说,它排除了合并基础和所有较早的提交,但保留了超出该点的提交。

The merge base of L and I (or I and L ) is commit H , so we get things after H , but not H itself, and we cannot reach node J from either I or L since it's not in their ancestry. LI (或IL )的合并基础是提交H ,所以我们在H之后得到东西,但不是H本身,而且我们无法从IL到达节点J ,因为它不在它们的祖先中。 Hence, the result for I...L or L...I is:因此, I...LL...I是:

     o--I
 



      K
       \
        o--L

(Note that these histories do not join up, since we tossed out node H .) (请注意,这些历史记录不会合并,因为我们丢弃了节点H 。)

--ancestry-path

Now, all these are ordinary selection operations.现在,所有这些都是普通的选择操作。 None have been modified with --ancestry-path .没有被修改过--ancestry-path The documentation for git log and git rev-list —these two are almost the same command, except for their output format—describes --ancestry-path this way: git loggit rev-list文档——这两个命令几乎相同,除了它们的输出格式——这样描述--ancestry-path

When given a range of commits to display (eg commit1..commit2 or commit2 ^commit1 ), only display commits that exist directly on the ancestry chain between the commit1 and commit2 , ie commits that are both descendants of commit1 , and ancestors of commit2 .当给定要显示的提交范围(例如commit1..commit2commit2 ^commit1 )时,仅显示直接存在于commit1commit2之间的祖先链上的提交,即既是commit1后代又是commit1祖先的commit2

We define ancestors here in terms of the commit DAG: a first commit is a direct ancestor of a second if the second has an arrow pointing back at the first, and an indirect ancestor if the second points back at the first through some chain of commits.我们在这里根据提交 DAG 定义祖先:如果第二个提交的箭头指向第一个,则第一个提交是第二个的直接祖先,如果第二个提交通过一些提交链指向第一个,则为间接祖先. (For selection purposes a commit is also considered an ancestor of itself.) (出于选择目的,提交也被视为其自身的祖先。)

Descendants (also sometimes called children ) are defined similarly, but by going against the arrows in the graph.后代(有时也称为children )的定义类似,但与图中的箭头相反。 A commit is a child (or descendant) of another commit if there's a path between them.如果它们之间存在路径,则提交是另一个提交的子项(或后代)。

Note that the description of the --ancestry-path talks about using the two-dot notation, not the three-dot notation, probably because the implementation of the three-dot notation is a little bit weird inside.请注意,-- --ancestry-path的描述谈到使用两点表示法,而不是三点表示法,可能是因为三点表示法的实现内部有点奇怪。 As noted earlier, B...D excludes (as if with leading ^ ) the merge base (or bases, if there is/are more than one) of the two commits, so the merge base is the one that play the "must be child-of" role.如前所述, B...D排除了(好像带有前导^ )两个提交的合并基(或基,如果有/多于一个),因此合并基是播放“必须成为孩子”的角色。 I'll mention how --ancestry-path works with this, though I'm not sure how useful it is in "real world" examples.我会提到--ancestry-path如何与它一起工作,尽管我不确定它在“现实世界”的例子中有多有用。

Practical examples实际例子

What does this mean in practice?这在实践中意味着什么? Well, it depends on the arguments you give, and the actual commit DAG.好吧,这取决于您提供的参数以及实际提交的 DAG。 Let's look at the funky loopy graph again:让我们再看看这个时髦的循环图:

     o---o
    /     \
A--o       \
    \   B   \
     \ /     \
      o       C--D
       \     /
        o---o

Suppose we ask for B..D here without --ancestry-path .假设我们在这里要求B..D没有--ancestry-path This means we take commit D and its ancestors, but exclude B and its ancestors, just as we saw before.这意味着我们接受提交D及其祖先,但排除B及其祖先,就像我们之前看到的那样。 Now let's add --ancestry-path .现在让我们添加--ancestry-path Everything we had earlier was an ancestor of D , and that's still true, but this new flag says we must also toss out commits that are not children of B .我们之前所有的东西都是D的祖先,这仍然是真的,但是这个新标志说我们必须扔掉不是B孩子的提交。

How many children does node B have?节点B有多少个孩子? Well, none!嗯,没有! So we must toss out every commit, giving us a completely empty list .所以我们必须扔掉每一次提交,给我们一个完全空的 list


What if we ask for B...D , without the special --ancestry-path notation?如果我们要求B...D ,而没有特殊的--ancestry-path符号怎么办? That gives us everything reachable from either D or B , but excludes everything reachable from both D and B :这给了我们从DB可达的一切,但排除了从DB可达的一切:

     o---o
          \
           \
        B   \
             \
              C--D
             /
        o---o

This is the same as B..D except that we get node B as well.这与B..D相同,只是我们也得到了节点B

[Note: the section below on mixing --ancestry-path with B...D was wrong for almost a year, between April 2016 and Feb 2017. It has been fixed to note that the "must be child" part starts from the merge base(s) , not from the left side of the B...D notation.] [注意:下面关于将--ancestry-pathB...D混合的部分在 2016 年 4 月至 2017 年 2 月之间错误了将近一年。它已被修复以注意“必须是孩子”部分从合并 base(s) ,而不是从B...D符号的左侧。]

Suppose we add --ancestry-path here.假设我们在这里添加--ancestry-path We start with the same graph we just got for B...D without --ancestry-path , but then discard items that are not children of the merge base.我们刚刚为B...D获得的相同图开始,没有--ancestry-path ,然后丢弃不是合并基础的子项的项目。 The merge base is the o just to the left of B .合并基础是B左侧的o The top row o commits are not children of this node, so they are discarded.第一行o提交不是该节点的子节点,因此它们被丢弃。 Again, as with ancestors, we consider a node its own child, so we would keep this node itself—giving this partial result:同样,与祖先一样,我们认为一个节点是它自己的子节点,所以我们保留这个节点本身——给出这个部分结果:

        B
       /
      o       C--D
       \     /
        o---o

But, while we are (or --ancestry-path is) discarding children of this merge base node, the merge base node itself, to the down-and-left of B , was not in the B...D graph in the first place.但是,虽然我们(或--ancestry-path是)丢弃了这个合并基节点的子节点,但合并基节点本身,在B的左下角,不在B...D图中第一名。 Hence, the final result (actually tested in Git 2.10.1) is:因此,最终结果(实际在 Git 2.10.1 中测试过)是:

        B

              C--D
             /
        o---o

(Again, I'm not really sure how useful this is in practice. The starting graph, again, is that of B...D : everything reachable from either commit, minus everything reachable from both commits: this works by discarding starting from every merge base, if there are two or more. The child-of checking code also handles a list of commits. It retains everything that is a child of any of the merge bases, if there are multiple merge bases. See the function limit_to_ancestry in revision.c .) (同样,我不确定这在实践中有多有用。同样,起始图是B...D :从任一提交可到达的所有内容,减去从两个提交可到达的所有内容:这通过丢弃从每一个合并的基础上,如果有两个或更多。孩子-的检查代码还处理提交的清单。它保留一切任何合并基础的孩子,如果有多个合并基地。参见功能limit_to_ancestryrevision.c .)

Thus, it depends on the graph and the selectors因此,它取决于图形选择器

The final action of X..Y or X...Y , with or without --ancestry-path , depends on the commit graph. X..YX...Y的最终操作,有或没有--ancestry-path ,取决于提交图。 To predict it, you must draw the graph.要预测它,您必须绘制图形。 (Use git log --graph , perhaps with --oneline --decorate --all , or use a viewer that draws the graph for you.) (使用git log --graph ,也许使用--oneline --decorate --all ,或者使用为您绘制图形的查看器。)


1 There's an exception in git diff , which does its own special handling for X..Y and X...Y . 1 git diff有一个例外,它对X..YX...Y进行自己的特殊处理。 When you are not using git diff you should just ignore its special handling.当您不使用git diff您应该忽略它的特殊处理。

2 We start with I and the o to its left, and also H and G . 2我们从I和左边的o开始,还有HG Then we lose H and G when we work back from L , so the result is just o--I .然后当我们从L返回时我们失去了HG ,所以结果只是o--I

3 The formal definition is that the merge base is the L owest C ommon A ncestor, or LCA, of the given nodes in the graph. 3正式定义是,合并的碱是在图中所述给定节点的LÇ亏欠于ommonncestor,或LCA,。 In some graphs there may be multiple LCAs;在某些图中,可能有多个 LCA; for Git, these are all merge bases, and X...Y will exclude all of them.对于 Git,这些都是合并基础,而X...Y将排除所有这些基础。

It's interesting / instructive to run git rev-parse B...D for the graph I drew.为我绘制的图形运行git rev-parse B...D很有趣/很有启发性。 These commit hashes here depend on not just the graph itself, and the commit, but also the time stamps at which one makes the commits, so if you build this same graph, you will get different hashes, but here are the ones I got while revising the answer to fix the description of --ancestry-path interacting with B...D :这里的这些提交哈希不仅取决于图本身和提交,还取决于提交的时间戳,因此如果您构建相同的图,您将获得不同的哈希,但这里是我得到的修改答案以修复--ancestry-pathB...D交互的描述:

$ git rev-parse B...D
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2
7f0b666b4098282301a9f95e056a646483c2e5fc
^843eaf75d78520f9a569da35d4e561a036a7f107

but we can see that these are D , B , and the merge base, in that order, using several more commands:但是我们可以看到这些是DB和合并基础,按这个顺序,使用更多的命令:

$ git rev-parse B     # this produces the middle hash
7f0b666b4098282301a9f95e056a646483c2e5fc

and:和:

$ git rev-parse D     # this produces the first hash
3f0490d4996aecc6a17419f9cf5a4ab420c34cc2

and:和:

$ git merge-base B D  # this produces the last, negated, hash
843eaf75d78520f9a569da35d4e561a036a7f107

Graphs with multiple merge bases do occur, but they're somewhat harder to construct—the easy way is with "criss cross" merges, where you run git checkout br1; git merge br2; git checkout br2; git merge br1具有多个合并基础的图确实会出现,但它们构建起来有些困难——简单的方法是使用“交叉”合并,在那里你运行git checkout br1; git merge br2; git checkout br2; git merge br1 git checkout br1; git merge br2; git checkout br2; git merge br1 git checkout br1; git merge br2; git checkout br2; git merge br1 . git checkout br1; git merge br2; git checkout br2; git merge br1 If you get this situation and run git rev-list you will see several negated hashes, one per merge base.如果你遇到这种情况并运行git rev-list你会看到几个否定的哈希,每个合并基础一个。 Run git merge-base --all and you will see the same set of merge bases.运行git merge-base --all ,您将看到相同的合并基础集。

As the documentation says, --ancestry-path removes commits that are not descendant of origin/master .正如文档所说, --ancestry-path删除不是origin/master后代的提交。 If you have a local, unmerged branch, and this branch is based on a commit which is older than origin/master , then commits in this branch will not be shown because these commits are not descendant of origin/master .如果您有一个本地未合并的分支,并且此分支基于比origin/master更旧的提交,则不会显示此分支中的提交,因为这些提交不是origin/master后代。

Git 2.38 (Q3 2022) illustrates how git log --ancestry-path works, and extends that --ancestry-path option with a value. Git 2.38(2022 年第三季度)说明了git log --ancestry-path工作原理,并使用值扩展了--ancestry-path选项。

" git rev-list --ancestry-path=C A..B " ( man ) is a natural extension of git rev-list A..B ; " git rev-list --ancestry-path=C A..B " ( man )git rev-list A..B的自然扩展;
instead of choosing a subset of A..B to those that have ancestry relationship with A , it lets a subset with ancestry relationship with C .而不是选择A..B的子集与那些与A具有祖先关系的子集,它允许与C具有祖先关系的子集。

See commit 1838e21 (19 Aug 2022) by Derrick Stolee ( derrickstolee ) .请参阅Derrick Stolee ( derrickstolee ) 的提交 1838e21 (2022 年 8 月 19 日)。
See commit 257418c , commit 11ea33c (19 Aug 2022) by Elijah Newren ( newren ) .请参阅Elijah Newren ( newren )提交 257418c提交 11ea33c (2022 年 8 月 19 日)。
(Merged by Junio C Hamano -- gitster -- in commit 0b08ba7 , 29 Aug 2022) (由Junio C Hamano -- gitster --提交 0b08ba7中合并,2022 年 8 月 29 日)

revision : allow --ancestry-path to take an argument revision :允许 --ancestry-path 接受参数

Signed-off-by: Elijah Newren签字人:以利亚·纽伦
Acked-by: Derrick Stolee委托方:Derrick Stolee

We have long allowed users to run eg我们长期以来允许用户运行例如

git log --ancestry-path master..seen

which shows all commits which satisfy all three of these criteria:它显示了满足所有这三个标准的所有提交:

  • are an ancestor of seenseen的祖先
  • are not an ancestor of master不是master的祖先
  • have master as an ancestormaster

This commit allows another variant:此提交允许另一个变体:

 git log --ancestry-path=$TOPIC master..seen

which shows all commits which satisfy all of these criteria:它显示了满足所有这些标准的所有提交:

  • are an ancestor of seenseen的祖先
  • are not an ancestor of master不是master的祖先
  • have $TOPIC in their ancestry-path在他们的祖先路径中有$TOPIC

that last bullet can be defined as commits meeting any of these criteria:最后一个项目符号可以定义为满足以下任何标准的提交:

  • are an ancestor of $TOPIC$TOPIC的祖先
  • have $TOPIC as an ancestor$TOPIC作为祖先
  • are $TOPIC$TOPIC

This also allows multiple --ancestry-path arguments, which can be used to find commits with any of the given topics in their ancestry path.这也允许多个--ancestry-path arguments,可用于在其祖先路径中查找具有任何给定主题的提交。

rev-list-options now includes in its man page : rev-list-options现在包含在其手册页中:

--ancestry-path[=<commit>]

When given a range of commits to display (eg ' commit1..commit2 ' or ' commit2 {caret}commit1 '), only display commits in that range that are ancestors of <commit> , descendants of <commit> , or <commit> itself.当给定要显示的提交范围时(例如“ commit1..commit2 ”或“ commit2 {caret}commit1 ”),仅显示该范围内的提交,即<commit> <commit><commit>的后代本身。

If no commit is specified, use ' commit1 ' (the excluded part of the range) as <commit> .如果未指定提交,则使用“ commit1 ”(范围的排除部分)作为<commit>

Can be passed multiple times;可以多次通过; if so, a commit is included if it is any of the commits given or if it is an ancestor or descendant of one of them.如果是这样,如果它是任何给定的提交,或者它是其中一个的祖先或后代,则包含一个提交。

As an example use case, consider the following commit history:作为示例用例,请考虑以下提交历史记录:

 ----------------------------------------------------------------------- D---E-------F / \ \ B---C---G---H---I---J / \ A-------K---------------L--M -----------------------------------------------------------------------

When we want to find out what commits in M are contaminated with the bug introduced by D and need fixing, however, we might want to view only the subset of ' D..M ' that are actually descendants of D , ie excluding C and K .但是,当我们想找出M中的哪些提交被D引入的错误污染并需要修复时,我们可能只想查看实际上是D后代的“ D..M ”子集,即不包括CK

This is exactly what the --ancestry-path option does.这正是--ancestry-path选项的作用。
Applied to the 'D..M' range, it results in:应用于“D..M”范围,结果为:

 ----------------------------------------------------------------------- E-------F \ \ G---H---I---J \ L--M -----------------------------------------------------------------------

rev-list-options now includes in its man page : rev-list-options现在包含在其手册页中:

We can also use --ancestry-path=D instead of --ancestry-path which means the same thing when applied to the ' D..M ' range but is just more explicit.我们也可以使用--ancestry-path=D而不是--ancestry-path ,这在应用于 ' D..M ' 范围时意味着同样的事情,但更明确。

If we instead are interested in a given topic within this range, and all commits affected by that topic, we may only want to view the subset of D..M which contain that topic in their ancestry path.相反,如果我们对此范围内的给定主题感兴趣,并且所有受该主题影响的提交,我们可能只想查看D..M的子集,该子集在其祖先路径中包含该主题。

So, using --ancestry-path=H D..M for example would result in:因此,例如使用--ancestry-path=H D..M会导致:

 ----------------------------------------------------------------------- E \ G---H---I---J \ L--M -----------------------------------------------------------------------

Whereas --ancestry-path=K D..M would result in--ancestry-path=K D..M会导致

----------------------------------------------------------------------- K---------------L--M -----------------------------------------------------------------------

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM