有没有更好的方法获取存储库中所有对象的原始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
编辑 Josh提出了一个很好的观点:
git rev-list --objects -g --no-walk --all
列出可以从ref-log中访问的所有列表对象。
如果想要查看不可访问提交中的所有对象:
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
为了使输出结果更有用(对于tree / blobs按路径排序,先列出commits),可以使用额外的| sort -k2
,它将为相同路径分组所有不同的blobs(修订)。
refs/
可达的对象,不包括 reflogs 和不可达对象。 - Josh Leeblob
路径,非常棒。是否有一种简单的方法来帮助识别提交和树?例如,通过路径查找树,通过提交消息的第一行查找提交。 - Ciro Santilli OurBigBook.com我不知道这个选项是从什么时候开始存在的,但你可以
git cat-file --batch-check --batch-all-objects
根据 man 手册,这将为您提供存储库中的所有对象和任何备用对象存储 (不仅是可达对象)。
(强调是我的)。
示例输出:
$ git cat-file --batch-check --batch-all-objects
64a77169fe44d06b082cbe52478b3539cb333d45 tree 34
6692c9c6e231b1dfd5594dd59b32001b70060f19 commit 237
740481b1d3ce7de99ed26f7db6687f83ee221d67 blob 50
0e5814c4da88c647652df8b1bd91578f7538c65f tag 200
如上所示,此操作将返回对象类型及其大小以及每个哈希值,但您可以轻松删除此信息,例如使用
git cat-file --batch-check --batch-all-objects | cut -d' ' -f1
或者通过对--batch-check
设置自定义格式来实现。
编辑:如果你不关心顺序,你可以(从Git 2.19开始)添加--unordered
标志以加快速度。有关更多详细信息,请参见VonC的答案。
git cat-file --batch-check --batch-all-objects
命令,新版Git 2.19(2018年第三季度)增加了选项--unordered
,可以使其更快。
API用于迭代所有对象,可选择按打包文件中出现的顺序列出对象,如果调用者在枚举对象时访问这些对象,则有助于访问的本地性。
请查看commit 0889aae, commit 79ed0a5, commit 54d2f0d, commit ced9fff (2018年8月14日)以及commit 0750bb5, commit b1adb38, commit aa2f5ef, commit 736eb88, commit 8b36155, commit a7ff6f5, commit 202e7f1 (2018年8月10日),作者为Jeff King (peff
)。此更改已于2018年8月20日合并,合并者为Junio C Hamano -- gitster
--,合并提交为commit 0c54cda。
cat-file
:支持--batch-all-objects
的"unordered
"输出If you're going to access the contents of every object in a packfile, it's generally much more efficient to do so in pack order, rather than in hash order. That increases the locality of access within the packfile, which in turn is friendlier to the delta base cache, since the packfile puts related deltas next to each other. By contrast, hash order is effectively random, since the sha1 has no discernible relationship to the content.
This patch introduces an "
--unordered
" option tocat-file
which iterates over packs in pack-order under the hood. You can see the results when dumping all of the file content:
$ 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
Same output, different order, way faster. The same speed-up applies even if you end up accessing the object content in a different process, like:
git cat-file --batch-all-objects --buffer --batch-check | grep blob | git cat-file --batch='%(objectname) %(rest)' | wc -c
Adding "
--unordered
" to the first command drops the runtime ingit.git
from 24s to 3.5s.Side note: there are actually further speedups available for doing it all in-process now. Since we are outputting the object content during the actual pack iteration, we know where to find the object and could skip the extra lookup done by
oid_object_info()
. This patch stops short of that optimization since the underlying API isn't ready for us to make those sorts of direct requests.So if
--unordered
is so much better, why not make it the default? Two reasons:
We've promised in the documentation that
--batch-all-objects
outputs in hash order. Sincecat-file
is plumbing, people may be relying on that default, and we can't change it.It's actually slower for some cases. We have to compute the pack revindex to walk in pack order. And our de-duplication step uses an oidset, rather than a sort-and-dedup, which can end up being more expensive.
If we're just accessing the type and size of each object, for example, like:
git cat-file --batch-all-objects --buffer --batch-check
my best-of-five warm cache timings go from 900ms to 1100ms using
--unordered
. Though it's possible in a cold-cache or under memory pressure that we could do better, since we'd have better locality within the packfile.And one final question: why is it "
--unordered
" and not "--pack-order
"? The answer is again two-fold:
"pack order" isn't a well-defined thing across the whole set of objects. We're hitting loose objects, as well as objects in multiple packs, and the only ordering we're promising is within a single pack. The rest is apparently random.
The point here is optimization. So we don't want to promise any particular ordering, but only to say that we will choose an ordering which is likely to be efficient for accessing the object content. That leaves the door open for further changes in the future without having to add another compatibility option
在Git 2.20(2018年第四季度)中,它甚至更快了,具体表现为:
请查看commit 8c84ae6、commit 8b2f8cb、commit 9249ca2、commit 22a1646和commit bf73282(于2018年10月4日由René Scharfe (rscharfe
)提交)。gitster
合并到commit 82d0a8c中)
oidset
: 使用khash
Reimplement
oidset
usingkhash.h
in order to reduce its memory footprint and make it faster.Performance of a command that mainly checks for duplicate objects using an oidset, with
master
and 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
avar
)。gitster
--于commit 04d67b6合并,2019年2月5日)FOR_EACH_OBJECT_PACK_ORDER
和for_each_object_in_pack()
略微优化“提交图写入”步骤。Derrick Stolee在Windows上进行了自己的测试,显示精度高,提高了2%。
git rev-list --objects
”,该命令学会了使用"--no-object-names
"选项来消除作为pack-objects分组提示的对象路径。nasamuffin
)于2019年6月19日提交)。
(由Junio C Hamano -- gitster
--合并于commit f4f7e75,2019年7月9日)
rev-list
:教授--no-object-names
以启用管道传输
Allow easier parsing by
cat-file
by giving rev-list an option to print only the OID of a non-commit object without any additional information.
This is a short-term shim; later on,rev-list
should be taught how to print the types of objects it finds in a format similar tocat-file
's.Before this commit, the output from
rev-list
needed to be massaged before being piped to cat-file, like so:
git rev-list --objects HEAD | cut -f 1 -d ' ' | git cat-file --batch-check
This was especially unexpected when dealing with root trees, as an invisible whitespace exists at the end of the OID:
git rev-list --objects --filter=tree:1 --max-count=1 HEAD | xargs -I% echo "AA%AA"
Now, it can be piped directly, as in the added test case:
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做准备,这应该可以加快这些操作的速度。
请查看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, commit 66cbd3e, commit 952fc68, commit f33fb6e (2021年1月13日) 由 Taylor Blau (ttaylorr
) 提交。
请查看commit 779412b (2021年1月14日) 由 Jeff King (peff
) 提交。
(合并于 commit bcaaf97,2021年1月25日,由 Junio C Hamano -- gitster
--)
pack-revindex
: 引入新的API签署者:Taylor Blau
在接下来的几个补丁中,我们将准备好在内存中加载反向索引(将.idx内容的反向映射到内核中),或直接从尚未介绍的磁盘格式中加载。packed_git
结构中的revindex指针。pack-revindex.h
导出四个函数。load_pack_revindex()
')返回一个整数。struct
packed_git``'。pack_pos_to_offset()
。off_t
。uint32_t
。pack_pos_to_index()
。offset_to_pack_pos()
。uint32_t
的指针,如果该偏移量处存在对象,则将位置写入其中。find_revindex_position()
中的有符号-1作为对'revindex'数组的索引。offset_to_pack_pos()
懒惰地加载反向索引,但pack_pos_to_index()
不会(前者的调用者适合处理错误,但后者的调用者不适合)。
git (branch|tag) --format=...
"已经进行了微小的优化。adlternative
)提交。gitster
--在commit c108c8c中合并,2021年5月7日)
ref-filter
: 重用输出缓冲区协助者:Junio C Hamano
协助者:Jeff King
协助者:René Scharfe
签名:ZheNing Hu
git for-each-ref
(man) 时,每个引用都会分配自己的输出 strbuf 和错误 strbuf。format_ref_array_item()
返回非零值并输出错误缓冲区的内容时将退出程序,但错误缓冲区也将被重复使用。hyperfine
在 Git 存储库本身上对 git for-each-ref
进行性能测试,其性能从 23.7 ms ± 0.9 ms 改变为 22.2 ms ± 1.0 ms。git tag -l
(man) 和 git branch -l
(man)。cat-file
: use a single strbuf for all output",2018-08-14,Git v2.19.0-rc0 -- merge)用于加速 cat-file 内置命令的方法。它使用rev-parse --git-path
在更复杂的Git存储库设置中查找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
$p
未引用,因为使用extglob
语法意味着变量不可能包含需要引用的文件名。如果您切换到可以捕获IFS字符的Glob,则还应该在使用值时添加引号。 — 但是,“extglob”会有什么问题呢? - Aristotle Pagaltzis编辑:Aristotle 发布了一个更好的答案,应该标记为正确答案。
编辑:脚本包含语法错误,在 grep -v
行末缺少反斜杠。
Mark 的回答对我有用,经过一些修改:
--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:' \
;
objects/pack/pack-23fd2d5638f6cd3bf2a433de4ea3cc26ebe7bdfa.idx
。 - Ethan Tgrep -v
,现在对我来说已经修复了。看起来 Aristotle 的答案没有这个错误,我还是推荐使用他的答案。 - willkil我不知道有什么比查看所有松散的对象文件和所有打包文件的索引更好的方法。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,'
我的原始版本是基于查找包文件中最大对象的实用脚本,但我改用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
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)
ls -alR .git/objects
,但我同意这不是理想的方法... +1 - johnnygit cat-file --batch-check --batch-all-objects --unordered
命令速度相当快。请参见 我的答案。 - VonC