从本地签出的单个文件中查找提交哈希



在我的Go项目中,我有一份https://github.com/HouzuoGuo/tiedot本地制造。这可能是几年前手工制作的。

我不知道签出了什么版本/标签,因为它在任何地方都没有维护。

有什么方法可以让我从单个文件的哈希中找到提交哈希吗?例如,一些散列如下:

github.com/HouzuoGuo/tiedot/db> shasum *.go
79b42b7af9784255b39b4307950709880df4a86f  col.go
b5f5a127c990229e8ac085eb8e7c72d0e6617e1c  col_test.go
be45a7eae65803df2dc31e23db7eb27bcffa17cc  db.go
290c32d11498aacb0456117f2bffa8e7ab74ccd8  db_test.go
3d0e0dc06fbd8191b5d68b32b4ac4200444e98f2  doc.go
f15745867ccfcb8609194b617cc6e8911174dad9  doc_test.go
40fcd698a680b39bd8405b9bc62d0f4b99411cbf  idx_test.go
d1c481d7d75140b229440819bb21eb64095a7b35  query.go
c83114227dc59100de953ffceb4398e4d8a6075b  query_test.go

一旦我有了commit has,我就可以使用go get github.com/HouzuoGuo/tiedot@<hash>之类的东西将其添加到我的go.mod文件中

根据下面@torek的建议,我从github中签出了代码,并编写了一个示例脚本来读取所有提交,并检查其中一个文件的哈希是否匹配。但这不起作用。我错过了什么?

COMMITS=$(git rev-list --all)
for COMMIT_HASH in $COMMITS
do
TREE_HASH=$(git cat-file -p $COMMIT_HASH | grep tree | cut -d' ' -f2)
if [[ -z "$TREE_HASH" ]]; then
echo "Tree hash is empty"
continue
fi
DB_DIR_HASH=$(git cat-file -p $TREE_HASH | grep '[[:space:]]db$' | awk '{print $3}')
if [[ -z "$DB_DIR_HASH" ]]; then
echo "db dir hash is empty"
continue
fi
DBGO_HASH=$(git cat-file -p $DB_DIR_HASH | grep db.go | awk '{print $3}')
if [[ -z "$DBGO_HASH" ]]; then
echo "db.go hash is empty"
continue
fi
if [[ "$DBGO_HASH" == "be45a7eae65803df2dc31e23db7eb27bcffa17cc" ]]; then
echo "db.go hash matched!!!   Commit $COMMIT_HASH"
fi
done

我有没有办法从单个文件的哈希中找到提交哈希?

坏消息是:不,因为提交哈希不仅取决于文件本身,还取决于提交的元数据。

好消息是:您不需要这样做,因为您可以简单地从提交哈希到文件的另一个方向。也就是说,使用存储库的克隆,遍历提交图。对于过程中发现的每个提交,请将保存的源快照与您关心的文件集进行比较。

编辑2:确保您使用的校验和是Git将要使用的,而不是通过运行shasum或任何类似命令生成的。也就是说,使用git hash-object命令来计算要搜索的对象的哈希ID。(默认情况是计算blob散列ID,这样您就可以运行git hash-object db/db.go了。(

你可能会发现不止一个匹配(这就是为什么这是不可逆的(:例如,也许v2.4.2v2.4.4都匹配,因为v2.4.3被破坏,错误被还原为makev2.4.4。但这并不重要,只要结果对你有效。

要比较您关心的源的哈希,请在有问题的提交中使用git ls-tree -r。使用git rev-list枚举提交哈希ID。如果你有一个完整的树,你可以通过计算哈希并比较每个$commit值的git rev-parse $commit^{tree}的结果来加快速度,而不是比较一些已知文件子集的所有文件哈希,但无论哪种方式,这都应该很快。

编辑:我不确定你的脚本出了什么问题,但这里有一个更简单的变体:

git rev-list --branches |
while read commit; do
h=$(git rev-parse --quiet --verify $commit:db/db.go) || continue
if [ $h == be45a7eae65803df2dc31e23db7eb27bcffa17cc ]; then
echo "db/db.go hash matched in commit $commit"
fi
done

请注意,该文件可能在多个提交中!当我在Git的Git存储库上运行这样的变体,查找levenshtein.c的哈希IDd2632690d5107b53ee8a7ac4832cd85eb8c7bfc1时,我得到了18132个匹配的提交(大约花了十分钟,扫描了60000多个提交(。但是,散列ID可能在no提交中:一种快速的检查方法是使用jthill注释中的选项:git log --find-object=hash(带有--all--branches或其他(。如果出现至少一个匹配,则至少有一个提交具有该对象;脚本将查找所有具有该对象的提交。

使用git rev-list --tags --no-walk在大约8秒内发现181个提交:

$ time git rev-list --tags --no-walk | while read commit; do h=$(git rev-parse --quiet --verify $commit:levenshtein.c) || continue; test $h = d2632690d5107b53ee8a7ac4832cd85eb8c7bfc1 && echo "found in $commit"; done | wc -l
181
real    0m7.810s
user    0m2.449s
sys     0m3.434s

如果没有脚本,同样的事情会在0.046秒内找到772个标记的提交,所以这个脚本片段在我的旧Mac笔记本电脑上每秒处理大约100个提交。(我用这个来估计10分钟:我知道它很慢!(

最新更新