git log 的祖先路径如何处理多个否定提交?

Pau*_*tch 13 git

当只有一个否定提交时,git 的或--ancestry-path选项的行为相对简单 - 也就是说,如果您有:git loggit rev-list

git rev-list ^A B
# or it's shorthand form:
git rev-list A..B
Run Code Online (Sandbox Code Playgroud)

...那么结果是 A 的后代和 B 的祖先的所有提交(有关更完整的解释,请参见此处)。

但是,我想知道--ancestry-path如果您有两个(或更多)否定修订的行为 - 即:

git rev-list ^C ^A B
Run Code Online (Sandbox Code Playgroud)

(注意 - 我将非否定的修订,例如B上述,称为“积极”提交,而所有否定的修订称为“消极”提交。)

在一些简单的测试用例中检查结果后--ancestry-path,负面提交所施加的“后代”限制似乎是 OR'd / union'ed - 也就是说,git 似乎正在做:

  • 首先,找到至少一个正提交的祖先的所有提交
  • 然后,过滤到仅作为至少一个否定提交的后代的提交
  • 然后,过滤掉作为至少一个否定提交的祖先的提交

或者,在更像集合的符号中:

IntersectionOf(
    Union(ancestors(positive1), ...ancestors(positiveN))
       AND
    Union(descendents(negative1), ...descendents(negativeN))
)
- Union(ancestors(negative1), ...ancestors(negativeN))
Run Code Online (Sandbox Code Playgroud)

然而,在一些更复杂的场景中,这被证明是不正确的 - 最终结果中排除的提交比上述逻辑所暗示的要多。

例如,假设您有这个完整的图表:

ancestors(MA3)

      -----MA3
     /     |  
    /      MB3
   A3     /|  
  /|    B3 |  
 / |   /|  |  
R3-+--- |  |  
|  |    |  |  
|  |  --+--MA2
|  | /  |  |  
|  |/   |  MB2
|  A2   | /|  
| /|    B2 |  
|/ |   /|  |  
R2-+--- |  |  
|  |    |  |  
|  |  --+--M1 
|  | /  |  /  
|  A1   | /   
| /     B1    
|/     /      
R1-----       
Run Code Online (Sandbox Code Playgroud)

这表示一个公共根分支(R),特征分支 A 和 B 都继承自该分支,并且 A 和 B 依次合并为一个公共合并分支(M)。(这是一个 bash 脚本,用于设置具有此结构的存储库!

我们希望找到:

--ancestry-path ^R2 ^B3 MA3
Run Code Online (Sandbox Code Playgroud)

如果我们这样做

Union(descendents(R2),      Union(ancestors(R2),
      descendents(B3))            ancestors(B3))

      -----MA3                                 
     /     |                                   
    /      MB3                                 
   A3     /|                                   
  /|    B3 |                             B3    
 / |   /|  |                            /|     
R3-+--- |  |                     R3----- |     
   |    |  |                     |       |     
   |  --+--MA2                   |       |     
   | /  |  |                     |       |     
   |/   |  MB2      MINUS        |       |     
   A2   | /                      |       |     
        B2                       |       B2    
                                 |      /|     
                                 R2----- |     
                                 |       |     
                                 |       |     
                                 |       |     
                                 |       |     
                                 |       B1    
                                 |      /      
                                 R1-----        
Run Code Online (Sandbox Code Playgroud)

那么我们期望:

      -----MA3
     /     |  
    /      MB3
   A3      |  
   |       |  
   |       |  
   |       |  
   |       |  
   |  -----MA2
   | /     |  
   |/      MB2
   A2         
Run Code Online (Sandbox Code Playgroud)

然而,我们实际得到的

git rev-list --ancestry-path ^R2 ^B3 MA3 | xargs -i git tag --points-at '{}'
Run Code Online (Sandbox Code Playgroud)

是:

MA3
MB3
A3
MA2
A2
Run Code Online (Sandbox Code Playgroud)

IE:

      -----MA3
     /     |  
    /      MB3
   A3      |  
   |       |  
   |       |  
   |       |  
   |       |  
   |  -----MA2
   | /        
   |/         
   A2         
Run Code Online (Sandbox Code Playgroud)

...这是相同的,除了 MB2 被排除在外。

所以,很明显,我--ancestry-path对多重^否定如何工作的猜测是错误的。谁能解释git实际使用的逻辑,为什么不包括MB2?

编辑 所以,只是为了直观地澄清下面 jthill 的答案(/sf/answers/4581543711/),他说它是这样工作的:

  • 首先,git 计算ancestors(MA3) - Union(ancestors(R2), ancestors(B3)- 所以我们最终得到:
ancestors(MA3) - Union(ancestors(R2),
                       ancestors(B3))

      -----MA3
     /     |  
    /      MB3
   A3      |  
   |       |  
   |       |  
   |       |  
   |       |  
   |  -----MA2
   | /     |  
   |/      MB2
   A2      |  
   |       |  
   |       |  
   +       |  
   |       |  
   |  -----M1 
   | /        
   A1         
Run Code Online (Sandbox Code Playgroud)

然后,它尝试使用这个有限的节点集,沿着从R2B3到 的路径找到所有节点MA3。因此,它尝试在此图上找到从 (x) 到 [y] 的所有路径:

        ----[MA3]
       /     |  
      /      MB3
     A3     /|  
     |  (B3) |  
     |       |  
     |       |  
     |       |  
     |  -----MA2
     | /     |  
     |/      MB2
     A2      |  
    /|       |  
   / |       |  
(R2) |       |  
     |       |  
     |  -----M1 
     | /        
     A1         
Run Code Online (Sandbox Code Playgroud)

...这就是为什么我们最终得到我上面显示的结果。谢谢jthill!

jth*_*ill 3

MB2R2顾名思义,位于 的祖先路径上,但是该祖先路径超出了您在将 的祖先排除B3在步行范围之外时设置的界限。

Git 正在做你要求的事情:获取MA3和 它的所有祖先,排除B3和以及它们的R2所有祖先,然后尝试追踪到排除的提示的祖先路径。

所获得的效果通常会产生与所有后代减去所有祖先追踪相同的结果,但您已经发现了差异很重要的情况。Git 追溯祖先,而不是血统。