如何以编程方式确定更早或更晚的提交?

How to determine earlier or later commits programatically?

提问人:Nederealm 提问时间:10/30/2018 最后编辑:Nederealm 更新时间:10/31/2018 访问量:22

问:

是否可以以编程方式比较 git 提交或与其整数十六进制值进行比较?后面的提交的整数是否比前一个提交大? 因此,例如,如果我们可以在伪代码中获取哈希值

int commit_a = myrepo.getCurrentCommit();
int commit_b = myrepo.getPreviousCommit();

if(commit_a > commit_b)
printf("Commit A is later than Commit B");
else
printf("Commit B is later than Commit A");

注意:我已经编辑了我的问题,询问是否有 git 库可以执行上述示例。或者如何实现我自己的。

git 哈希 相等

评论

0赞 chepner 10/30/2018
不;就所有意图和目的而言,哈希是从均匀分布中提取的随机数。无论一个哈希值小于还是大于另一个哈希值,都不能说明产生哈希值的提交。
0赞 chepner 10/30/2018
选择您喜欢的任何存储库,经过片刻的检查后,应该很明显,提交哈希值没有按照您询问的方式排序。

答:

2赞 erik258 10/30/2018 #1

提交由其“sha”标识,最好不要将其视为整数,而应视为不透明的二进制值。它不会递增,也不能与另一个提交直接比较。

但是,每个提交中固有的信息都足以确定该提交的历史记录,因此您可以根据历史记录比较提交。关于如何做到这一点,已经有一个非常可靠的问题和答案:我如何判断一个提交是否是另一个提交的后代?

评论

0赞 chepner 10/30/2018
将 SHA 哈希解释为整数(只是一个非常大的整数)是微不足道的。
1赞 Romain Valeri 10/30/2018
@chepner微不足道,是的。但有用吗?
2赞 chepner 10/30/2018
不值得一提,真的。关键是哈希本身只是一个标识符,而不是本身包含有关提交的有用信息的东西。
1赞 torek 10/31/2018 #2

Git 中的提交形式为有向无环图或 DAG。

在任何图中,我们都有一个邻接列表(因为图被定义为 G = (V, E),其中 VE 分别是顶点和边集):图中任何给定的顶点/节点要么与其他顶点相距一跳,穿过某条边,要么距离更远或根本没有连接:

  C--D
 /
B------E  G--H
 \    /
  A  F

节点 A 连接到 B(反之亦然),B 连接到 C 和 E,依此类推。G 和 H 相互连接,但不连接到图形的其余部分,因此该图形由两个不相交的子图形组成。(这些边没有方向。

有向图中,有一个继和后继的概念,因为每个连接都有一个箭头:

A->B->C
   |
   v
   D

这里 A 连接到 B,B 同时连接到 C 和 D。所以 B 是 A 的后继者,C 和 D 都是 B 的后继者(但彼此没有联系)。从前辈的角度来看,A是B的前身,B是C和D的前身。

如果图没有循环(是非循环的),我们可以使用前置/后续操作执行传递闭包操作。Git 的图既是有向的,又是无环的——它是一个 DAG——所以我们可以用 Git 来做到这一点。

在 Git 中,箭头实际上是向后(出于实现原因),但我们仍然执行相同的传递闭包。当我们这样做时,我们发现 A 是 B、C 和 D 的祖先

A <-B <-C
    ^
    |
    D

因为我们可以从这些提交中的任何一个开始,然后回到 A.B 是 C 和 D 的祖先。相反的关系是后代:D 是 A 的后代。

这里必须小心一点。C 和 D 之间没有祖先或后代关系。也就是说,C 不是 D 的祖先,但 C 也不是 D 的后代。你不能只是说“不是祖先,而是后代”:两者可能根本就没有关系。如果 DAG 具有不相交的子图,则对于某些提交也是如此,这是允许的:

A <-B <-C   <-- master1

D <-E   <-- master2

虽然 D 是 E 的祖先,但 D 与 A、B 或 C 中的任何一个都没有关系。

在任何情况下,测试“祖先性”的 Git 命令是:

git merge-base --is-ancestor

它采用两个提交哈希 ID(或其他提交说明符)和答案,作为 true 或 false 查询(通过退出状态,0 表示“是,是祖先”),问题是:由第一个参数命名的提交是否是第二个参数命名的提交的祖先。那是:

hash1=$(git rev-parse $thing1^{commit}) || die "$thing1 does not name a git commit"
hash2=$(git rev-parse $thing2^{commit}) || die "$thing2 does not name a git commit"
if git merge-base --is-ancestor $hash1 $hash2; then
    echo "$thing1 is an ancestor of $thing2"
else
    if git merge-base --is-ancestor $hash2 $hash1; then
        echo "$thing1 is a descendant of $thing2"
    else
        echo "$thing1 and $thing2 are not relatable"
    fi
fi

之所以出现“不相关”的答案,是因为正如我们上面所看到的,我们从前任/后继者的概念中只得到了部分顺序,而不是全部顺序

(就 Git 而言,提交是它自己的祖先,所以总是正确的。git merge-base --is-ancestor $hash1 $hash1