如何用Git命令行获取指定提交新增Blob哈希及分支首次出现Blob?
一、先明确git diff-tree的输出含义
你观察的没错!git diff-tree输出的每一行里,第3个哈希是旧版本的Blob对象,第4个是新版本的Blob对象。比如你示例里的:100644 100644 03f15b5... 6e0ed0b... M file1.ts,就是说file1.ts在这次提交中从旧Blob 03f15b5... 更新到了新Blob 6e0ed0b...。
补充个小细节:如果是新增文件,旧Blob会是全0的哈希(0000000000000000000000000000000000000000);如果是删除文件,新Blob会是全0哈希。
二、获取单个提交对应的Blob哈希列表
如果你只需要拿到某个提交中所有存在的Blob对象(不管是新增还是修改后的),可以用git ls-tree命令,加上递归参数就能遍历所有文件:
git ls-tree -r --name-only --object-only <commit-hash>
参数说明:
-r:递归处理子目录,不会漏掉文件夹里的文件--name-only:只输出Blob哈希(默认会带文件名,加这个就只保留哈希)--object-only:确保只输出Blob对象,排除目录对应的tree对象
如果只想拿到该提交相对于父提交新增/更新的Blob(也就是这次提交引入的新Blob),可以用git diff-tree过滤出有效新Blob:
git diff-tree --no-commit-id -r <commit-hash> | awk '$4 != "0000000000000000000000000000000000000000" {print $4}'
这里-r是递归展开目录,不然像some_folder这种tree对象不会拆成里面的Blob;awk用来排除删除操作的全0哈希。
三、实现你的最终需求:收集分支上首次出现的Blob哈希
你的需求是按提交顺序遍历分支,收集首次出现的Blob哈希,输出B:<hash>格式。这里给你两种可行方案:
方案1:先输出所有Blob再去重(你可接受的简易方案)
直接把所有提交的Blob按顺序输出,再用awk自动去重,只保留首次出现的内容:
git rev-list --reverse <branch> | while read commit; do git ls-tree -r --object-only "$commit" | sed "s/^/B:/" done | awk '!seen[$0]++'
步骤拆解:
git rev-list --reverse <branch>按时间从早到晚输出分支的所有提交- 遍历每个提交,用
git ls-tree输出所有Blob哈希,并用sed加上B:前缀 awk '!seen[$0]++'会记住已经输出过的行,只保留第一次出现的内容,自动完成去重
方案2:更高效的增量追踪方式
如果分支历史很长,方案1会重复处理大量已出现的Blob,效率偏低。可以优化成每次只对比当前提交和父提交,提取父提交里没有的新增Blob:
# 处理分支的第一个提交(无父提交,所有Blob都是首次出现) first_commit=$(git rev-list --reverse <branch> | head -n1) git ls-tree -r --object-only "$first_commit" | sed "s/^/B:/" # 处理后续提交,对比父提交提取新增Blob git rev-list --reverse <branch> | tail -n +2 | while read commit; do parent_commit=$(git rev-parse "$commit^") # 找出当前提交独有的Blob(父提交没有的) comm -23 <(git ls-tree -r --object-only "$commit" | sort) <(git ls-tree -r --object-only "$parent_commit" | sort) | sed "s/^/B:/" done
步骤拆解:
- 先处理分支的第一个提交,它的所有Blob都是首次出现
- 对后续每个提交,用
comm -23对比它和父提交的Blob列表,筛选出当前提交独有的Blob(也就是新增的) - 同样加上
B:前缀输出,全程只处理新增内容,效率更高
额外小调整(如果需要关联提交哈希)
看你示例里有C:<hash>的格式,如果你需要记录每个Blob首次出现的提交哈希,可以修改脚本如下:
declare -A seen # 用哈希表记录Blob是否已出现 git rev-list --reverse <branch> | while read commit; do git ls-tree -r --object-only "$commit" | while read blob; do if [ -z "${seen[$blob]}" ]; then echo "C:$commit" echo "B:$blob" seen[$blob]=$commit fi done done
这样会同时输出Blob首次出现的提交哈希和Blob哈希,按需选用即可。
内容的提问来源于stack exchange,提问作者Daniel K.

