有没有比获取存储库中所有对象的 SHA1 原始列表更好的方法:
ls .git/objects/??/\*
和
cat .git/objects/pack/*.idx | git show-index
我知道,git rev-list --all
但这仅列出了由 引用的提交对象.git/refs
,并且我正在寻找所有内容git-hash-object
,包括由等创建的未引用对象git-mktree
。
有没有比获取存储库中所有对象的 SHA1 原始列表更好的方法:
ls .git/objects/??/\*
和
cat .git/objects/pack/*.idx | git show-index
我知道,git rev-list --all
但这仅列出了由 引用的提交对象.git/refs
,并且我正在寻找所有内容git-hash-object
,包括由等创建的未引用对象git-mktree
。
尝试
git rev-list --objects --all
编辑乔什提出了一个很好的观点:
git rev-list --objects -g --no-walk --all
列出可从 ref-logs 访问的对象。
要查看无法访问的提交中的所有对象:
git rev-list --objects --no-walk \
$(git fsck --unreachable |
grep '^unreachable commit' |
cut -d' ' -f3)
把它们放在一起,要真正得到输出格式的所有对象rev-list --objects
,你需要类似的东西
{
git rev-list --objects --all
git rev-list --objects -g --no-walk --all
git rev-list --objects --no-walk \
$(git fsck --unreachable |
grep '^unreachable commit' |
cut -d' ' -f3)
} | sort | uniq
要以稍微更有用的方式对输出进行排序(通过树/blob 的路径,首先提交),请使用附加功能| sort -k2
,它将所有不同的 blob(修订)分组为相同的路径。
我不知道这个选项什么时候存在,但你可以
git cat-file --batch-check --batch-all-objects
根据手册页,这给了你,
存储库中的所有对象和任何备用对象存储(不仅仅是可访问的对象)
(强调我的)。
默认情况下,这会产生对象类型及其大小以及每个散列,但您可以轻松删除此信息,例如
git cat-file --batch-check --batch-all-objects | cut -d' ' -f1
或通过为--batch-check
.
编辑:如果您不关心顺序,您可以(自 Git 2.19 起)添加标志--unordered
以加快速度。有关详细信息,请参阅VonC 的答案。
这是从Mark和willkill的答案中对脚本的更正确、更简单和更快的再现。
即使在更复杂的 Git 存储库设置中(例如,在多工作树情况下或诸如此类的情况下),它rev-parse --git-path
也可以用来查找目录。objects
它避免了所有不必要的使用find
, grep
, perl
, sed
。
即使您没有松散的对象或没有包(或者两者都没有……如果您倾向于在新的存储库上运行它),如果可以正常工作。
但是,它确实需要从这个千年开始的 Bash(2.02 或更高版本,特别是针对该extglob
位)。
分享和享受。
#!/bin/bash
set -e
shopt -s nullglob extglob
cd "`git rev-parse --git-path objects`"
# packed objects
for p in pack/pack-*([0-9a-f]).idx ; do
git show-index < $p | cut -f 2 -d ' '
done
# loose objects
for o in [0-9a-f][0-9a-f]/*([0-9a-f]) ; do
echo ${o/\/}
done
Erki Der Loony的回答中建议的git cat-file --batch-check --batch-all-objects
命令可以使用新的 Git 2.19 (Q3 2018) 选项更快。--unordered
遍历所有对象的 API 学会了按对象出现在 packfiles 中的顺序选择性地列出对象,如果调用者在枚举对象时访问这些对象,这有助于访问的局部性。
请参阅提交 0889aae、提交 79ed0a5、提交 54d2f0d、提交 ced9fff(2018 年 8 月 14 日)和提交 0750bb5、提交 b1adb38、提交 aa2f5ef、提交 736eb88、提交 8b36155、提交 a7ff6f5、提交 202e7f1(2018 年 8 月 10 日)(Jeff
King peff
)(由Junio C Hamano 合并 -- gitster
--在提交 0c54cda中,2018 年 8 月 20 日)
cat-file
:支持“unordered
”输出--batch-all-objects
如果要访问包文件中每个对象的内容,通常按包顺序而不是散列顺序这样做效率更高。这增加了包文件中的访问局部性,这反过来对增量基本缓存更友好,因为包文件将相关的增量彼此相邻。相比之下,哈希顺序实际上是随机的,因为 sha1 与内容没有明显的关系。
这个补丁引入了一个 "
--unordered
" 选项,cat-file
它在引擎盖下按包顺序迭代包。转储所有文件内容时,您可以看到结果:$ time ./git cat-file --batch-all-objects --buffer --batch | wc -c 6883195596 real 0m44.491s user 0m42.902s sys 0m5.230s $ time ./git cat-file --unordered \ --batch-all-objects --buffer --batch | wc -c 6883195596 real 0m6.075s user 0m4.774s sys 0m3.548s
相同的输出,不同的顺序,速度更快。即使您最终在不同的进程中访问对象内容,同样的加速也适用,例如:
git cat-file --batch-all-objects --buffer --batch-check | grep blob | git cat-file --batch='%(objectname) %(rest)' | wc -c
--unordered
在第一个命令中添加“ ”会将运行时间git.git
从 24 秒缩短到 3.5 秒。旁注:实际上现在有进一步的加速可用于在进程中完成所有操作。由于我们在实际包迭代期间输出对象内容,因此我们知道在哪里可以找到对象,并且可以跳过由
oid_object_info()
. 这个补丁没有优化,因为底层 API 还没有准备好让我们发出这些类型的直接请求。因此,如果
--unordered
更好,为什么不将其设为默认值呢?两个原因:
我们在文档中承诺以
--batch-all-objects
哈希顺序输出。由于cat-file
是管道,人们可能依赖于该默认值,我们无法更改它。在某些情况下它实际上更慢。我们必须计算包 revindex 以按包顺序行走。我们的重复数据删除步骤使用 oidset,而不是 sort-and-dedup,这最终可能会更昂贵。
如果我们只是访问每个对象的类型和大小,例如:
git cat-file --batch-all-objects --buffer --batch-check
我使用
--unordered
. 尽管在冷缓存或内存压力下我们可以做得更好,因为我们在包文件中会有更好的局部性。最后一个问题:为什么是“
--unordered
”而不是“--pack-order
”?答案又是两方面的:
“打包顺序”在整个对象集中并不是一个明确定义的东西。我们正在击打松散的物体,以及多个包装中的物体,我们承诺的唯一订购是在单个包装内。其余的显然是随机的。
这里的重点是优化。所以我们不想承诺任何特定的排序,而只是说我们将选择一个可能对访问对象内容有效的排序。这为将来的进一步更改打开了大门,而无需添加另一个兼容性选项
在 Git 2.20(2018 年第四季度)中它甚至更快:
请参阅René Scharfe ( ) 的commit 8c84ae6、commit 8b2f8cb、commit 9249ca2、commit 22a1646、commit bf73282(2018 年 10 月 4 日)。(由Junio C Hamano 合并 -- --在82d0a8c 提交中,2018 年 10 月 19 日)rscharfe
gitster
oidset
: 利用khash
重新实现
oidset
usingkhash.h
以减少其内存占用并使其更快。
master
使用 oidset和 Clang 6.0.1主要检查重复对象的命令的性能:$ cmd="./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'" $ /usr/bin/time $cmd >/dev/null 0.22user 0.03system 0:00.25elapsed 99%CPU (0avgtext+0avgdata 48484maxresident)k 0inputs+0outputs (0major+11204minor)pagefaults 0swaps $ hyperfine "$cmd" Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)' Time (mean ± σ): 250.0 ms ± 6.0 ms [User: 225.9 ms, System: 23.6 ms] Range (min … max): 242.0 ms … 261.1 ms
有了这个补丁:
$ /usr/bin/time $cmd >/dev/null 0.14user 0.00system 0:00.15elapsed 100%CPU (0avgtext+0avgdata 41396maxresident)k 0inputs+0outputs (0major+8318minor)pagefaults 0swaps $ hyperfine "$cmd" Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)' Time (mean ± σ): 151.9 ms ± 4.9 ms [User: 130.5 ms, System: 21.2 ms] Range (min … max): 148.2 ms … 170.4 ms
Git 2.21(2019 年第一季度)通过遵循按包内顺序访问对象的通常模式,进一步优化了代码路径以写出提交图。
请参阅Ævar Arnfjörð Bjarmason ( ) 提交的d7574c9(2019 年 1 月 19 日)。(由Junio C Hamano 合并——在提交 04d67b6中,2019 年 2 月 5 日)avar
gitster
FOR_EACH_OBJECT_PACK_ORDER
使用with稍微优化“提交图写入”步骤for_each_object_in_pack()
。
Derrick Stolee在 Windows 上进行了自己的测试,结果显示提高了 2%,并且准确度很高。
Git 2.23(2019 年第 3 季度)改进了“ git rev-list --objects
”,它使用“ --no-object-names
”选项学习以压制对象的路径,该对象用作打包对象的分组提示。
请参阅Emily Shaffer ( ) 的提交 42357b4(2019 年 6 月 19 日)。(由Junio C Hamano 合并 -- --在提交 f4f7e75中,2019 年 7 月 9 日)nasamuffin
gitster
rev-list
:教导--no-object-names
启用管道
cat-file
通过为 rev-list 提供一个选项来仅打印非提交对象的 OID 而无需任何其他信息,从而允许更轻松地解析。
这是一个短期垫片;稍后,rev-list
应该教你如何以类似于cat-file
's 的格式打印它找到的对象类型。
rev-list
在此提交之前,需要在通过管道传输到 cat 文件之前对来自的输出进行按摩,如下所示:git rev-list --objects HEAD | cut -f 1 -d ' ' | git cat-file --batch-check
这在处理根树时尤其出乎意料,因为在 OID 的末尾存在一个不可见的空格:
git rev-list --objects --filter=tree:1 --max-count=1 HEAD | xargs -I% echo "AA%AA"
现在,它可以直接通过管道传输,就像添加的测试用例一样:
git rev-list --objects --no-object-names HEAD | git cat-file --batch-check
这就是两者之间的区别:
vonc@vonvb:~/gits/src/git$ git rev-list --objects HEAD~1..
9d418600f4d10dcbbfb0b5fdbc71d509e03ba719
590f2375e0f944e3b76a055acd2cb036823d4b44
55d368920b2bba16689cb6d4aef2a09e8cfac8ef Documentation
9903384d43ab88f5a124bc667f8d6d3a8bce7dff Documentation/RelNotes
a63204ffe8a040479654c3e44db6c170feca2a58 Documentation/RelNotes/2.23.0.txt
并且,与--no-object-name
:
vonc@vonvb:~/gits/src/git$ git rev-list --objects --no-object-names HEAD~1..
9d418600f4d10dcbbfb0b5fdbc71d509e03ba719
590f2375e0f944e3b76a055acd2cb036823d4b44
55d368920b2bba16689cb6d4aef2a09e8cfac8ef
9903384d43ab88f5a124bc667f8d6d3a8bce7dff
a63204ffe8a040479654c3e44db6c170feca2a58
使用 Git 2.31(2021 年第一季度),对内核 revindex 的抽象访问允许按照它们在包中出现的顺序枚举存储在包文件中的对象,以准备引入磁盘上预先计算的 revindex,这应该会加快这些操作。
See commit e5dcd78 , commit d5bc7c6 , commit 8389855 , commit 1c3855f , commit 2891b43 , commit b130aef , commit 0a7e364 , commit fc150ca , commit 3a3f54d , commit 45bef5c , commit 78232bf , commit 011f3fd , commit a78a903 , commit cf98f2e , commit 5766508 , commit eb3fd99 , commit 6a5c10c,提交 66cbd3e,提交 952fc68,提交 f33fb6e(2021 年 1 月 13 日),泰勒·布劳(Taylor Blau ttaylorr
)。
请参阅Jeff King ( ) 的提交 779412b(2021 年 1 月 14 日)。(由Junio C Hamano 合并 -- --在提交 bcaaf97中,2021 年 1 月 25 日)peff
gitster
pack-revindex
: 引入一个新的 API签字人:Taylor Blau
在接下来的几个补丁中,我们将准备在内存中加载反向索引(映射
.idx
内核内容的倒数),或者直接从尚未引入的磁盘格式加载。
为此,我们将引入一个 API,以避免调用者显式索引packed_git
结构中的 revindex 指针。与反向索引交互的方式有四种。因此,在移除现有 API 时,
将从“ ”中导出四个函数。 来电者可以:pack-revindex.h
加载包的反向索引。
这涉及打开索引,生成数组,然后对其进行排序。
由于打开索引可能会失败,因此此函数 ('load_pack_revindex()
') 返回一个 int。
因此,它只需要一个参数:struct
调用者想要为其构建反向索引的“packed_git”。
此函数非常适合当前和新的 API。
调用者必须继续显式地打开反向索引,但该函数最终将学习如何从磁盘格式中检测和加载反向索引(如果存在)。
否则,它将回退到从头开始在内存中生成一个。将包位置转换为偏移量。
此操作现在称为pack_pos_to_offset()
。
它接受一个包和一个位置,并返回相应的off_t
.
任何错误都只是调用 BUG(),因为调用者不适合处理失败并继续运行。将包位置转换为索引位置。
同上;这需要一个包和一个位置,并返回一个uint32_t
.
此操作称为pack_pos_to_index()
.
关于错误条件的相同想法也适用于此。查找给定偏移量的包装位置。
此操作现在称为offset_to_pack_pos()
.
如果在该偏移处存在对象,则它需要一个包、一个偏移量和一个指向uint32_t
写入位置的指针。
否则,返回-1 表示失败。
'->offset'
与过去直接访问和直接访问的一些调用者不同,'->nr'
围绕此调用的错误检查更加健壮。
这很重要,因为调用者应该总是传递一个指向两个对象边界的偏移量。
与直接访问不同,API 强制执行此操作。这将在随后的补丁中变得重要,其中不检查返回值但可以检查返回值的调用者将签名
-1
的 fromfind_revindex_position()
视为 'revindex' 数组的索引。两个设计缺陷被转移到新的 API 中:
- 询问越界对象的索引位置会产生一个BUG()(因为不存在这样的对象),但是询问包末尾不存在的对象的偏移量会返回总大小包裹。
这对于总是想要获取两个相邻对象的偏移量的差异(计算磁盘大小)但不想担心包末尾的边界的调用者来说很方便。offset_to_pack_pos()
延迟加载反向索引,但pack_pos_to_index()
不加载(前者的调用者非常适合处理错误,但后者的调用者则不然)。
在 Git 2.32(2021 年第二季度)中,“ git (branch|tag) --format=...
”已进行了微优化。
参见提交 844c3f0(2021 年 4 月 20 日)和提交 22f69a8(2021 年 4 月 19 日)由ZheNing Hu ( adlternative
)。
(由Junio C Hamano 合并 -- gitster
--在提交 c108c8c中,2021 年 5 月 7 日)
ref-filter
: 重用输出缓冲区协助人:Junio C Hamano
协助人:Jeff King
协助人:René Scharfe
签字人:胡哲宁
当我们使用( man )时,每个 ref 都会分配自己的输出 strbuf 和错误 strbuf。 但是我们可以为每个 step ref 的输出重用最终的 strbuf。 错误缓冲区也将被重用,尽管 git 将在返回非零值时退出并输出错误缓冲区的内容。
git for-each-ref
format_ref_array_item()
使用性能
git for-each-ref
测试工具对 Git 存储库本身的性能hyperfine
从 23.7 ms ± 0.9 ms 更改为 22.2 ms ± 1.0 ms。
优化相对较小。同时,我们将此优化应用于( man )和( man )。
git tag -l
git branch -l
这种方法类似于79ed0a5 ("
cat-file
: use a single strbuf for all output", 2018-08-14, Git v2.19.0-rc0 -- merge ) 用于加速 cat-file builtin 的方法。
编辑:grep -v
脚本包含语法错误,行尾缺少反斜杠
经过一些修改,马克的回答对我有用:
--git-dir
代替--show-cdup
支持裸仓库perl
因为 OS X Mountain Lion 的 BSD 风格sed
不支持-r
#!/bin/sh
set -e
cd "$(git rev-parse --git-dir)"
# Find all the objects that are in packs:
find objects/pack -name 'pack-*.idx' | while read p ; do
git show-index < $p | cut -f 2 -d ' '
done
# And now find all loose objects:
find objects/ \
| egrep '[0-9a-f]{38}' \
| grep -v /pack/ \
| perl -pe 's:^.*([0-9a-f][0-9a-f])/([0-9a-f]{38}):\1\2:' \
;
我不知道比只查看所有松散的目标文件和所有包文件的索引更好的方法。git 存储库的格式非常稳定,使用这种方法,您不必依赖于完全正确的选项git fsck
,它被归类为瓷器。我认为这种方法也更快。以下脚本显示了存储库中的所有对象:
#!/bin/sh
set -e
cd "$(git rev-parse --show-cdup)"
# Find all the objects that are in packs:
for p in .git/objects/pack/pack-*.idx
do
git show-index < $p | cut -f 2 -d ' '
done
# And now find all loose objects:
find .git/objects/ | egrep '[0-9a-f]{38}' | \
sed -r 's,^.*([0-9a-f][0-9a-f])/([0-9a-f]{38}),\1\2,'
(我的这个脚本的原始版本是基于这个有用的脚本来查找你的包文件中最大的对象,但我切换到 using git show-index
,正如你的问题中所建议的那样。)
我已经把这个脚本变成了 GitHub gist。
另一个有用的选择是使用git verify-pack -v <packfile>
verify-pack -v
列出数据库中的所有对象及其对象类型。
结合Erki der Loony和sehe解决方案,我们可以得到几乎所有 blob 对象文件名(git 中排除的文件除外):
git cat-file --batch-check --batch-all-objects | grep blob | cut -d" " -f1 | xargs -n1 git rev-list --objects -g --no-walk --all > .recovered/allblobobject.txt
cat .recovered/allblobobject.txt | sort | uniq > .recovered/allblobuniqobject.txt
此 blob 到文件名映射可用于恢复通过以下方式检索到的消失文件:
git fsck --full --no-reflogs --unreachable --lost-found | grep blob | cut -d" " -f3 > .recovered/bloblist.txt
for /F "tokens=*" %A in (.recovered/bloblist.txt) do (git cat-file -p %A > .recovered/%A)