git log 的祖先路径如何与多个否定的提交一起工作?



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

git rev-list ^A B
# or it's shorthand form:
git rev-list A..B

那么结果是所有的提交都是A的后代和B的祖先(要获得更全面的解释,请参见此处(。

然而,我想知道如果你有两个(或更多(否定的修订,--ancestry-path的表现如何——即:

git rev-list ^C ^A B

(注意-我将把未否定的修订,如上面的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))

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

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

ancestors(MA3)
-----MA3
/     |  
/      MB3
A3     /|  
/|    B3 |  
/ |   /|  |  
R3-+--- |  |  
|  |    |  |  
|  |  --+--MA2
|  | /  |  |  
|  |/   |  MB2
|  A2   | /|  
| /|    B2 |  
|/ |   /|  |  
R2-+--- |  |  
|  |    |  |  
|  |  --+--M1 
|  | /  |  /  
|  A1   | /   
| /     B1    
|/     /      
R1-----       

这表示一个公共根分支(R(,特征分支a和B都从中继承,a和B依次合并为一个公共合并分支(M(。(这里有一个bash脚本,可以用这种结构建立回购!(

我们希望找到:

--ancestry-path ^R2 ^B3 MA3

如果我们做

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-----        

那么我们期待:

-----MA3
/     |  
/      MB3
A3      |  
|       |  
|       |  
|       |  
|       |  
|  -----MA2
| /     |  
|/      MB2
A2         

然而,我们从中实际得到的

git rev-list --ancestry-path ^R2 ^B3 MA3 | xargs -i git tag --points-at '{}'

是:

MA3
MB3
A3
MA2
A2

即:

-----MA3
/     |  
/      MB3
A3      |  
|       |  
|       |  
|       |  
|       |  
|  -----MA2
| /        
|/         
A2         

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

所以,很明显,我对--ancestry-path如何处理多个^否定的猜测是错误的。有人能解释git实际使用的逻辑吗?为什么MB2被排除在外?

编辑所以,只是为了直观地澄清下面jthill的答案(https://stackoverflow.com/a/65450624/920545),他说它是这样工作的:

  • 首先,git计算ancestors(MA3) - Union(ancestors(R2), ancestors(B3),因此我们得出:
ancestors(MA3) - Union(ancestors(R2),
ancestors(B3))
-----MA3
/     |  
/      MB3
A3      |  
|       |  
|       |  
|       |  
|       |  
|  -----MA2
| /     |  
|/      MB2
A2      |  
|       |  
|       |  
+       |  
|       |  
|  -----M1 
| /        
A1         

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

----[MA3]
/     |  
/      MB3
A3     /|  
|  (B3) |  
|       |  
|       |  
|       |  
|  -----MA2
| /     |  
|/      MB2
A2      |  
/|       |  
/ |       |  
(R2) |       |  
|       |  
|  -----M1 
| /        
A1         

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

MB2位于R2的祖先路径上,正如其名称所示,但当您将B3的祖先排除在行走之外时,该祖先路径超出了您设置的界限。

Git正在执行您的要求:取MA3及其所有祖先,排除B3R2及其所有祖先,然后尝试追踪被排除提示的祖先路径。

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

相关内容

  • 没有找到相关文章