git 数据结构探究之index文件
上一篇探究了blob类型,我们知道了它本质就是一个压缩文件。并且这个压缩文件还用哈希值重新取名了。
在git的数据结构中,有个很重要的知识点:文件名和内容是分开存储的。
这样我们的文件名就不会强绑定内容了。
这次我们接着上次的实验,来探究index文件。
我们将上次构造的写有“version 1”的blob文件写入暂存区。
$ git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 temp.txt
- git update-index:这是 Git 操作暂存区(index)的底层命令,用于修改暂存区中文件的元数据(如哈希值、权限、状态等)。
- --add:表示将指定的文件添加到暂存区(如果文件原本不在暂存区中)。
- --cacheinfo:关键参数,用于通过文件模式、哈希值和文件名直接向暂存区添加记录,无需依赖工作区文件。
- 100644:文件模式(mode),表示这是一个普通文件(非 executable、非符号链接等)。
- test.txt:文件名可以随便取。
注:Git 中常见模式:100644(普通文件)、100755(可执行文件)、120000(符号链接)。
查看目录结构:
$ tree -a
.
|-- .git
| |-- HEAD
| |-- config
| |-- description
| |-- index
| |-- info
| | `-- exclude
| |-- objects
| | |-- 1f
| | | `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
| | |-- 7f
| | | |-- 8f011eb73d6043d2e6db9d2c101195ae2801f2
| | | `-- acc89938bbc5635e3d36ffa56b4c85e9b07db8
| | |-- 83
| | | `-- baae61804e65cc73a7201a7252750c76066a30
| | |-- d6
| | | `-- 70460b4b4aece5915caf5c68d12f560a9fe3e4
| | |-- info
| | `-- pack
| `-- refs
| |-- heads
| `-- tags
|-- test.txt
`-- test2.txt
12 directories, 12 files
目录没有变化,但是多了一个index文件。
似乎index就代表着暂存区,或者说他们两个是强相关的。
这个index文件是一个二进制文件,可以把他理解为一个c语言中的结构体,不同字段代表不同内容,具体如何划分,可以参看最后的引用资料。
我们可以使用如下命令查看他包含的主要内容:
$ git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 temp.txt
- 100644:表示普通文件
- 83baae61804e65cc73a7201a7252750c76066a30:哈希值,对应特定blob。
- 0:冲突状态,在合并文件时会用到。
- temp.txt:文件名。
index文件的主要作用就是把一个哈希值指定的blob和文件名关联起来。
相当于给解压缩后的blob取名。
index就像一个对照表,记录着不同名字对应的blob。
在blob关联文件名的过程中,文件名是可以让我们随意指定的。
此时会出现3种情况:
- 没有同名文件:这个文件名还没有其他文件使用过。
- 有同名文件,但内容不同
- 有同名文件,且内容相同
我们接下来分类讨论:
1.没有同名文件
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: temp.txt
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: temp.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
test2.txt
Changes to be committed:
new file: temp.txt
这段表示有个新文件名和blob关联起来了,可以进行最终存储。
Changes not staged for commit:
deleted: temp.txt
这段表示temp.txt只有blob文件,没有对应的源文件,源文件被删除掉了。
Untracked files:
test.txt
test2.txt
这段表示还有两个文件名没有指定blob。也就是说index文件还没有记录他们,所以叫:未追踪状态。
看到这里,用过git的人已经明白,原来暂存区就是index这个对照表。
那删除index是不是就表示暂存区没有了呢?
尝试一下,手动删除index
$ git ls-files --stage
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
test2.txt
nothing added to commit but untracked files present (use "git add" to track)
删除掉index文件后,暂存区的信息的确没有了。
2.有同名文件,但内容不同
先查看blob中的内容,避免与现有文件重复。
$ git cat-file -p 83baa
version 1
test.txt中存储的是version 2.
接下来给blob指定文件名
$ git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt
查看index信息
$ git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 test.txt
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
test2.txt
区别主要在下面这段:
Changes not staged for commit:
modified: test.txt
其实就是提示你test.txt这个文件可能有改动,你要不要重新计算一个blob。
3.文件名相同,内容相同
删除掉index
查看内容
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
version 2
写入index
$ git update-index --add --cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
查看index
$ git ls-files --stage
100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a 0 test.txt
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
test2.txt
很明显,没有被追踪的文件就只剩下一个了。
探究blob是否可以还原为源文件
显然,文件可以变为blob,
那么blob也可以根据index这份对照表变为文件。
手动删除之前的index文件和temp.txt文件
然后查看可用的blob
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/7f/8f011eb73d6043d2e6db9d2c101195ae2801f2
.git/objects/7f/acc89938bbc5635e3d36ffa56b4c85e9b07db8
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
将其中一个写入index,也可以都写,然后赋予不同的文件名
$ git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 temp.txt
注意,输入的是完整哈希值。
下面的代码就可以实现由blob转回源文件:
$ git restore temp.txt
可以查看目录树:
$ tree
.
|-- temp.txt
|-- test.txt
`-- test2.txt
0 directories, 3 files
发现我们多了一个temp.txt文件。
如果是同名文件,那么就会直接替换掉你的源文件,所以使用要谨慎。