在我的 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
一旦获得提交哈希值,我就可以使用类似
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.2
和 v2.4.4
都匹配,因为 v2.4.3
已损坏并且错误已恢复为 make v2.4.4
。 但这并不重要,只要结果对你有用即可。
要比较您关心的源的哈希值,请在相关提交上使用
git ls-tree -r
。 使用 git rev-list
枚举提交哈希 ID。 如果您有一棵完整的树,您可以通过计算 tree 哈希并比较每个 git rev-parse $commit^{tree}
值的 $commit
结果来加快速度,而不是比较某些已知文件子集的所有文件哈希,但是这样应该会进展得很快。
编辑:我不确定你的脚本出了什么问题,但这里有一个更简单的变体:
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 存储库上运行此变体时,查找
d2632690d5107b53ee8a7ac4832cd85eb8c7bfc1
的哈希 ID levenshtein.c
,我得到了 18132 个匹配的提交(大约花了 10 分钟,扫描了超过 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 分钟:我知道这很慢!)