上下文:我希望能够通过我的git提交消息和提交来搜索,而不必经历令人费解的复杂git grep命令,所以我决定看看如何存储git commit消息。
我看了一下.git文件夹,它看起来像提交存储在
中.git/objects
.git对象文件夹包含一堆名称为a6和9b的文件夹。这些文件夹中的每个文件夹都包含一个名称类似于提交的文件2f29598814b07fea915514cfc4d05129967bf7。当我在文本编辑器中打开其中一个文件时,我会感到胡言乱语。
在这个git commit日志中,文件夹9b包含一个提交sha
aed8a9f773efb2f498f19c31f8603b6cb2a4bc
为什么,并且是否存在多个提交sha将存储在文件9b中的情况?
有没有办法将这个乱码转换为纯文本,这样我就可以在文本编辑器中处理提交了?
答案 0 :(得分:18)
创建最小示例并对格式进行反向工程
创建一个简单的存储库,在创建任何包文件(git gc
,git config gc.auto
,git-prune-packed
...)之前,使用以下方法之一解压缩提交对象:{{ 3}}
export GIT_AUTHOR_DATE="1970-01-01T00:00:00+0000"
export GIT_AUTHOR_EMAIL="author@example.com"
export GIT_AUTHOR_NAME="Author Name" \
export GIT_COMMITTER_DATE="2000-01-01T00:00:00+0000" \
export GIT_COMMITTER_EMAIL="committer@example.com" \
export GIT_COMMITTER_NAME="Committer Name" \
git init
# First commit.
echo
touch a
git add a
git commit -m 'First message'
python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" \
<.git/objects/45/3a2378ba0eb310df8741aa26d1c861ac4c512f | hd
# Second commit.
echo
touch b
git add b
git commit -m 'Second message'
python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" \
<.git/objects/74/8e6f7e22cac87acec8c26ee690b4ff0388cbf5 | hd
输出结果为:
Initialized empty Git repository in /home/ciro/test/git/.git/
[master (root-commit) 453a237] First message
Author: Author Name <author@example.com>
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a
00000000 63 6f 6d 6d 69 74 20 31 37 34 00 74 72 65 65 20 |commit 174.tree |
00000010 34 39 36 64 36 34 32 38 62 39 63 66 39 32 39 38 |496d6428b9cf9298|
00000020 31 64 63 39 34 39 35 32 31 31 65 36 65 31 31 32 |1dc9495211e6e112|
00000030 30 66 62 36 66 32 62 61 0a 61 75 74 68 6f 72 20 |0fb6f2ba.author |
00000040 41 75 74 68 6f 72 20 4e 61 6d 65 20 3c 61 75 74 |Author Name <aut|
00000050 68 6f 72 40 65 78 61 6d 70 6c 65 2e 63 6f 6d 3e |hor@example.com>|
00000060 20 30 20 2b 30 30 30 30 0a 63 6f 6d 6d 69 74 74 | 0 +0000.committ|
00000070 65 72 20 43 6f 6d 6d 69 74 74 65 72 20 4e 61 6d |er Committer Nam|
00000080 65 20 3c 63 6f 6d 6d 69 74 74 65 72 40 65 78 61 |e <committer@exa|
00000090 6d 70 6c 65 2e 63 6f 6d 3e 20 39 34 36 36 38 34 |mple.com> 946684|
000000a0 38 30 30 20 2b 30 30 30 30 0a 0a 46 69 72 73 74 |800 +0000..First|
000000b0 20 6d 65 73 73 61 67 65 0a | message.|
000000ba
[master 748e6f7] Second message
Author: Author Name <author@example.com>
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 b
00000000 63 6f 6d 6d 69 74 20 32 32 33 00 74 72 65 65 20 |commit 223.tree |
00000010 32 39 36 65 35 36 30 32 33 63 64 63 30 33 34 64 |296e56023cdc034d|
00000020 32 37 33 35 66 65 65 38 63 30 64 38 35 61 36 35 |2735fee8c0d85a65|
00000030 39 64 31 62 30 37 66 34 0a 70 61 72 65 6e 74 20 |9d1b07f4.parent |
00000040 34 35 33 61 32 33 37 38 62 61 30 65 62 33 31 30 |453a2378ba0eb310|
00000050 64 66 38 37 34 31 61 61 32 36 64 31 63 38 36 31 |df8741aa26d1c861|
00000060 61 63 34 63 35 31 32 66 0a 61 75 74 68 6f 72 20 |ac4c512f.author |
00000070 41 75 74 68 6f 72 20 4e 61 6d 65 20 3c 61 75 74 |Author Name <aut|
00000080 68 6f 72 40 65 78 61 6d 70 6c 65 2e 63 6f 6d 3e |hor@example.com>|
00000090 20 30 20 2b 30 30 30 30 0a 63 6f 6d 6d 69 74 74 | 0 +0000.committ|
000000a0 65 72 20 43 6f 6d 6d 69 74 74 65 72 20 4e 61 6d |er Committer Nam|
000000b0 65 20 3c 63 6f 6d 6d 69 74 74 65 72 40 65 78 61 |e <committer@exa|
000000c0 6d 70 6c 65 2e 63 6f 6d 3e 20 39 34 36 36 38 34 |mple.com> 946684|
000000d0 38 30 30 20 2b 30 30 30 30 0a 0a 53 65 63 6f 6e |800 +0000..Secon|
000000e0 64 20 6d 65 73 73 61 67 65 0a |d message.|
000000eb
然后我们推断出格式如下:
顶级:
commit {size}\0{content}
其中{size}
是{content}
中的字节数。
对于所有对象类型,它遵循相同的模式。
{content}
:
tree {tree_sha}
{parents}
author {author_name} <{author_email}> {author_date_seconds} {author_date_timezone}
committer {committer_name} <{committer_email}> {committer_date_seconds} {committer_date_timezone}
{commit message}
其中:
{tree_sha}
:此提交指向的树对象的SHA。
这代表顶级Git repo目录。
SHA来自树对象的格式:How to DEFLATE with a command line tool to extract a git object?
{parents}
:表单的父提交对象的可选列表:
parent {parent1_sha}
parent {parent2_sha}
...
如果没有父母,列表可以为空,例如对于回购中的第一次提交。
两个父母在定期合并提交中发生。
git merge -Xoctopus
可以使用两个以上的父母,但这不是常见的工作流程。以下是一个示例:What is the internal format of a git tree object?
{author_name}
:例如:Ciro Santilli
。不能包含<
,\n
{author_email}
:例如:cirosantilli@mail.com
。不能包含>
,\n
{author_date_seconds}
:自1970年以来的秒数,例如946684800
是2000年的第一秒
{author_date_timezone}
:例如:+0000
是UTC
提交者字段:类似于作者字段
{commit message}
:任意。
我制作了一个最小的Python脚本,可以在https://github.com/cirosantilli/test-octopus-100k
生成一些git repo并提交一些提交我用它来做有趣的事情:
以下是标记对象格式的类似分析:https://github.com/isaacs/github/issues/1344
答案 1 :(得分:14)
在您进一步探讨这条道路之前,我建议您通读the section in the Git Manual about its internals。我发现知道本章的内容通常是喜欢Git和讨厌它的区别。理解为什么Git以它的方式做事通常会使它对事物的所有奇怪命令更有意义。
要回答您的问题,您所看到的乱码就是使用zlib压缩对象后的数据。如果你看标题&#34;对象存储&#34;在上面的链接中,您可以看到有关其工作原理的一些细节。这是文件存储在git中的简短版本:
因此,回答第二个问题时,文件夹将包含以相同的两个字符开头的所有压缩对象,无论其内容如何。
如果你想看到一个blob的内容,你所要做的就是解压缩它。如果您只想查看文件的内容,可以在大多数编程语言中轻松完成。但是,我会警告你不要试图修改数据。修改文件中的单个字节将改变它的散列。 git中的所有元数据(即目录结构和提交)都是使用哈希引用存储的,因此修改单个文件意味着还必须更新该文件下游引用该文件哈希的所有对象即可。然后,您必须更新引用这些哈希值的所有对象。而且,开启,开启和开启...试图实现这一目标变得非常非常复杂。通过学习git内置的命令,你可以为自己节省大量时间和心痛。
答案 2 :(得分:3)
提醒
请不要在编辑器中编辑对象。如果你不小心,你可以破坏你的Git存储库。值得花时间学习使用git grep
。它与grep
并没有什么不同,而且速度要快得多。
在引擎盖下,git有一个对象的概念。对象通常由标题和一些数据组成。文件内容存储为blob对象。树对象包含文件名并指向表示文件的blob对象,以及表示其他目录的树对象。然后是提交对象,它们记录日志消息并指向表示适当树状态的树对象。还有一些带注释的标记对象,通常指向放在提交上的标记。
在线图书包含有关different kinds of objects and how to view them的一些信息,还包含有关store the objects所用格式的一些详细信息。
请记住,您所看到的是松散的物体。包文件中还包含对象,可以有different format。
Git User's Manual也有关于the object database的良好信息。