Git Index文件数据结构

前言

在使用git工具管理项目的时候,.git目录下存放了index文件,这个文件是git的staging area,也叫做暂存区,也可以理解为当前工作区已经被托管文件的区域。index文件是使用二进制的方式进行存储的,具体内容是一些基础信息数据,然后面就是暂存区的条目信息。

基础环境

我这里简单创建了一个小环境具体创建过程如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
E:\TEMP\TEMPGIT>dir
驱动器 E 中的卷是 新加卷
卷的序列号是 F238-DE49

E:\TEMP\TEMPGIT 的目录

2024/12/06 12:29 <DIR> .
2024/12/06 12:26 <DIR> ..
2024/12/06 12:27 9 hello.txt
2024/12/06 12:27 16 README.md
2 个文件 25 字节
2 个目录 24,408,936,448 可用字节

E:\TEMP\TEMPGIT>git init
Initialized empty Git repository in E:/TEMP/TEMPGIT/.git/

E:\TEMP\TEMPGIT>git add hello.txt

E:\TEMP\TEMPGIT>git commit -m "feat: add hello.txt"
[master (root-commit) 49c851f] feat: add hello.txt
1 file changed, 1 insertion(+)
create mode 100644 hello.txt

E:\TEMP\TEMPGIT>git add README.md

E:\TEMP\TEMPGIT>git commit -m "feat: add README.md"
[master 699a3fc] feat: add README.md
1 file changed, 1 insertion(+)
create mode 100644 README.md

使用16进制编辑器打开项目下.git/index文件,具体内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
44 49 52 43 00 00 00 02 00 00 00 02 67 52 7D 35
28 C5 D9 74 67 52 7D 42 34 FA 3B A4 00 00 00 00
00 00 00 00 00 00 81 A4 00 00 00 00 00 00 00 00
00 00 00 10 75 7C B5 AA CC 80 7E B5 B1 FD 67 4E
1F 51 EC 71 DE 00 22 A3 00 09 52 45 41 44 4D 45
2E 6D 64 00 67 52 7D 15 2C 21 10 D4 67 52 7D 18
37 F7 20 24 00 00 00 00 00 00 00 00 00 00 81 A4
00 00 00 00 00 00 00 00 00 00 00 09 34 FE 99 B2
F1 96 86 5F 6D E9 3E 35 0A E6 AE 3A FD 34 55 06
00 09 68 65 6C 6C 6F 2E 74 78 74 00 54 52 45 45
00 00 00 19 00 32 20 30 0A 7E 4E 0F A2 7B 51 A7
2C E3 BA 34 E5 70 F0 8B 89 63 58 C5 9F 4D 52 68
C5 94 87 74 75 74 DA D9 91 8F 7E 56 3D B7 01 82
CD

基础信息

基础信息就是前12bytes具体内容位,它们依次代表的内容如下

1
44 49 52 43 00 00 00 02 00 00 00 02 

前4byte是固定的文件头44 49 52 43,转换到10进制的内容就是DIRC.
紧接着的4byte是记录git版本的00 00 00 02这里的git版本就是2
再后面的4byte是记录git提交次数的00 00 00 02这里的提交次数是2

条目信息

条目信息的前62位是固定的,之后的长度是按照文件路径名字来计算的,按照上面的数据来说第一条的数据是

1
2
3
4
5
67 52 7D 35 28 C5 D9 74 67 52 7D 42 34 FA 3B A4
00 00 00 00 00 00 00 00 00 00 81 A4 00 00 00 00
00 00 00 00 00 00 00 10 75 7C B5 AA CC 80 7E B5
B1 FD 67 4E 1F 51 EC 71 DE 00 22 A3 00 09 52 45
41 44 4D 45 2E 6D 64 00

下面依次说一下每个byte代表的作用

  • 8byte内容是ctime,创建文件的时间,前4byte是从1970年1月1日00:00:00计算的时间戳,后4byte是nanosecond微秒,一般前都用前面4byte,文件创建的时间戳67 52 7D 35转换过来就是17334592532024-12-06 12:27:33
  • 8byte内容是mtime,上次修改的时间,他的格式和CTIME一样,前4byte是时间戳,后面是nanosecond微秒。67 52 7D 42转换的时间戳是17334592662024-12-06 12:27:46
  • 4byte的内容应该是device即文件存储的设备,我这里是00 00 00 00,全0肯定是有些问题,具体问题我也没研究出来。
  • 4byte的内容应该是Inode即文件在设备的文件系统中存储的具体块的编号,我这里是00 00 00 00,全0肯定是有些问题,具体问题我也没研究出来。
  • 4byte的内容是文件权限,我这里的内容为00 00 81 A4转换之后就是100644,100是代表普通文件,后面的644就是linux中的权限,这里不多说
  • 4byte的内容是uid,大概率是因为我用的win系统,所以没展示出来全是0x00
  • 4byte的内容是gid,大概率是因为我用的win系统,所以没展示出来全是0x00
  • 4byte的内容是文件大小,我这里是00 00 00 10转换过来即16,就是16字节
  • 20byte的内容是sha-1,我这里的内容为75 7C B5 AA CC 80 7E B5 B1 FD 67 4E 1F 51 EC 71 DE 00 22 A3,这个内容在objects目录中会有对应的文件,这个可以理解为对应文件的索引,以这个sha-1为例,他的位置应该是在.git\objects\75\7cb5aacc807eb5b1fd674e1f51ec71de0022a3
  • 2byte的内容是文件的长度,我这里的长度是00 09即9位
  • 文件名在条目中的占用长度是不固定的,以当前条目为例,其长度为9字节,文件名内容是 52 45 41 44 4D 45 2E 6D 64 00,转换后为 README.md.但它实际占用了10字节.这是因为在存储时,文件名后会填充0x0000作为分隔符.为了满足存储规则,文件名会按照8字节对齐的方式进行填充.因此,如果条目的总字节长度不能被8整除,会继续填充 0x00,直到长度达到8的倍数.比如本条目就通过填充一个 00 字节使得长度符合对齐要求

扩展信息

条目解析结束之后,后面还有一段信息,这段信息的内容是index文件的扩展信息,内容如下

1
2
3
4
54 52 45 45 00 00 00 19 00 32 20 30 0A 7E 4E 0F
A2 7B 51 A7 2C E3 BA 34 E5 70 F0 8B 89 63 58 C5
9F 4D 52 68 C5 94 87 74 75 74 DA D9 91 8F 7E 56
3D B7 01 82 CD

前面四个字节是TREE,代表记录的是TREE的信息,这里会记录当前HEAD的SHA-1还有index的SHA-1来确定文件的完整性,这里当前HEAD的sha-1是7E 4E 0F A2 7B 51 A7 2C E3 BA 34 E5 70 F0 8B 89 63 58 C5 9F紧接着就是index的SHA-1,即4D 52 68 C5 94 87 74 75 74 DA D9 91 8F 7E 56 3D B7 01 82 CD