Git 实质
Git 是一个内容寻址文件系统。 这意味着,Git 的 核心部分是一个简单的键值对数据库。 你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索(retrieve)该内容。
底层命令
涉及到的底层命令有:
命令 | 解释 |
---|---|
cat-file | 获取指定 Git 对象的信息 |
ls-files | 显示暂存区的信息 |
ls-tree | 显示指定结点的 tree 对象 |
hash-object | 用于计算文件的 sha-1 值 |
update-index | 更新暂存区 |
write-tree | 将暂存区中的文件写成 tree 对象 |
read-tree | 把树对象读入暂存区 |
commit-tree | 根据指定的树对象创建一个提交对象 |
rev-parse | 解析分支名或标签名对应的提交对象的 sha-1 值 |
Git 所做的实质工作 —— 将被改写的文件保存为数据对象(hash-object),更新暂存区 ( update-index ),记录树对象(write-tree),最后创建一个指明了顶层树对象和父提交的提交对象(commit-tree)。
ls-tree
用于显示指定结点对应的 tree 对象中的内容。
它会展示所有的文件(即 blob 对象)。
$ git ls-tree -r HEAD
100644 blob 34f7ae03cc475d78515719c5b6f8c34e46002f7f aa
100644 blob c93ab02d540e388e1b83e23bbd8b49cb97c4b4ce bb
100644 blob 837df2b7ed69104790e279524d51880324064492 cc
100644 blob 9b5cf45dcf51d69998fc7cdab86ea8afb2905047 cid.java
100644 blob 79f6e4824f9578f237633353e8d6939c8bffcca1 dd.txt
100644 blob 7f1021754129446ecef3fecab549015ccbe5e59f ee
100644 blob f0e0aa843620d42bb1e72f4949001d0ca4173dc1 i/i.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 i/ii/xx.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new
100644 blob 59f2c499c12c1385dd0ff1131a50820e7f6a618c test.html
100644 blob d6459e005434a49a66a3ddec92279a86160ad71f xxx.xxx
从输出可以看出,其中有几个文件是子目录下的文件,它也会直接列出来。
rev-parse
解析分支、标签等指向的提交对象的 sha-1 值。
如:
$ git rev-parse master
9b335a39f65dff232f239c08baeeb9f7d27f193a
$ git tag t1
$ git rev-parse t1
9b335a39f65dff232f239c08baeeb9f7d27f193a
$ git rev-parse HEAD
9b335a39f65dff232f239c08baeeb9f7d27f193a
master 与 HEAD 为分支名, t1 为标签名。
ls-files
用于获取暂存区的信息。
其常用的几个选项有:
-c : 在输出中显示暂存的文件。默认值。此选项只会输出文件名。
--stage : 显示文件的详细信息。包括文件名,sha-1 值,文件模式等。
$ git ls-files --stage
100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt
100644 24e27b2a30edadba619d26e2d3662ec307cdddb2 0 test.html
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 xx
上述命令显示当前暂存区中暂存的有三个文件。
hash-object 命令
计算文件的 sha-1 值,并可选地根据该文件创建一个 blob 对象。
注意:该命令只适用于文件,不能用于目录。
选项 | 解释 |
---|---|
-w | 不加该选项则不会生成 blob 对象,反之则会生成 |
--stdin | hash-object 命令会从标准输入读取内容 |
-- <path> | 指定 hash-object 操作的文件的路径 |
注意:如果使用 --stdin 选项,就需要指定文件路径。
如:
$ find .git/objects/ -type f
$ git hash-object -- ../appid.txt
a5a06de3b66ee3a609def184e78873c99d6b1221
$ find .git/objects/ -type f
$ git hash-object -w -- ../appid.txt
a5a06de3b66ee3a609def184e78873c99d6b1221
$ find .git/objects/ -type f
.git/objects//a5/a06de3b66ee3a609def184e78873c99d6b1221
find 命令用于查找指定目录( .git/objects )目录下的所有文件( 通过 -type f 限定 )。
从上可以看出,起初 objects 目录下并没有文件。hash-object 不加 -w 操作后,目录下依旧没有文件,只不过输出了指定文件的 sha-1 值。
加上 -w 选项后,objects 目录下多了一个父目录为 a5 的文件。Git 会截取 sha-1 值的前两个字符生成目录名,将剩余的部分做为文件名。因此,objects 目录下会有一个 a5 目录。
cat-file
获取指定的 Git 对象的内容等信息。
其常用的选项如下:
选项 | 解释 |
---|---|
-p | 后跟 sha-1 值,则 cat-file 会返回该 sha-1 值的内容 |
-t | 获取指定对象的类型 |
-s | 获取指定对象的大小 |
在使用时,可以通过 > <path>
后缀,将 cat-file 的输出内容输出到指定的文件中。如 git cat-file -p bf43b62de7d99fa2c427dcc82257d81431ba816f > xxx.xxx
就会将内容输出到同目录下的 xxx.xxx 文件中。
如 -p 选项的使用:
$ git cat-file -p 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
test
其中 test 为该对象的内容。
如 -t 选项的使用:
$ git cat-file -t ee1a8365ec2baa3e5a97cb6e44d8d8b5c553fb30
blob
$ git cat-file -t 8bdb01d84c5ad6456acb4d8df8d82a0db5214972
tree
$ git cat-file -t bf43b62de7d99fa2c427dcc82257d81431ba816f
commit
Git 中一共有三种对象 :commit , tree 与 blob ,所以 -t 只会输出这三个值中的一个。
update-index
该选项用于更新暂存区。
git add
与git rm
等命令都是对该选项的封装。
其常用的选项有:
选项 | 解释 |
---|---|
<无> | 更新已暂存的文件 |
--add | 将未跟踪的文件添加到暂存区中 |
--cacheinfo | 将已保存到本地仓库的文件加入到暂存区中 |
--remove | 将已删除的文件从暂存区中移除 |
各个选项具体说明如下:
-
--add : 如果文件从来没有添加到暂存区中,该选项表示将文件第一次添加到暂存区中。
$ git ls-files --stag 100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt $ git hash-object yy e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 $ git update-index --add yy $ git ls-files --stage 100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 yy
初始时,暂存区中只有一个 a.txt 文件。使用 git update-index --add 命令后,暂存区中多了一个 yy 文件,并且 yy 文件与 update-index 指定的文件的 sha-1 值相同,所以该命令可以将指定的文件添加到暂存区中。
再查看 .git/objects 目录下,可以发现多了一个文件(即 yy 文件)。
-
--cacheinfo:如果要保存的文件已位于 git 仓库中,需要使用该选项。
$ vim yy $ git hash-object -w -- yy 9be86ae7054f7c477afabe3d971046032851b574 $ git update-index --cacheinfo 100644 9be86ae7054f7c477afabe3d971046032851b574 yy
首先修改 yy 文件,并通过 hash-object 将文件存储到 git 目录中。所以更新暂存区时不需要 --add 选项。
在使用 --cacheinfo 修改暂存区时,指定了文件的 sha-1 值,同时也指定了文件模式为 100644(即普通文件)。
-
--remove:将指定的文件从索引中删除,该文件必须已经从工作目录中删除。
$ git ls-files --stage 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 xx 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy $ rm xx $ git update-index --remove -- xx $ git ls-files --stage 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy $ git update-index --remove -- yy $ git ls-files --stage 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy
不删除文件直接使用 --remove 选项时,并不会删除相应的记录(如上例中的 yy 文件)。
write-tree
将暂存区内容写入一个树对象。
通过 update-index 更新暂存区后,基本的文件并没有生成成 tree 对象。而 write-tree 可以根据暂存区生成一个树对象。
$ git write-tree
348eb10c8ce3e5a4561a38aa7e76e61013e153e1
$ git cat-file -t 348eb10c8ce3e5a4561a38aa7e76e61013e153e1
tree
read-tree
将指定的树对象读入到暂存区中。
通过 --prefix=name
指定读入的树对象的名字。
首先将一个树对象读入暂存区中,然后通过 write-tree
将暂存区中的内容写入一个树对象中。则新生成的树对象会包含一个指向通过 read-tree
读入的树对象的指针。
$ git read-tree --prefix=namep 348eb10c8ce3e5a4561a38aa7e76e61013e153e1
执行上述命令后,如果将当前暂存区生成一个 tree 对象后,该对象会包含一个名为 namep 的指针,它指向的是通过 read-tree
读入的树对象。
commit-tree
根据指定的树对象创建一个提交对象。
通过 -p sha-1
指定当前对象的父对象
$ echo "first commit" | git commit-tree 2aabf73883cc35cef05f3c7919274da2aaebf674
cbc41af8ae9347c21299005f40ce998e58ab357e
$ echo "third" | git commit-tree fa197580163fc711a76726db35488f433968699c -p cbc41af8ae9347c21299005f40ce998e58ab357e
7e0792c1e2b634b4721edc6e3ff6f206d18298a2
echo 指定的是本次提交的说明。
上述命令新建了两个 commit 对象,并将第二个 commit 对象的父结点指向第一个对象。
当在第二个 commit 对象处建立分支后,运行 git log 命令,可以发现有两条提交记录。
目录
Git 有几个非常重要的文件:
-
objects 目录存储所有数据内容;
-
refs 目录存储指向数据(分支)的提交对象的指针。
-
refs/heads 存储的是当前的是各个分支,每一个文件记录了该分支最后一个提交结点的 sha-1 值。
-
refs/tags 目录下记录的是各个标签。
-
refs/remotes 存储当前仓库配置的远程服务器。其每一个子目录就对应着一个远程仓库 —— 子目录名字就是通过
git remote add <别名> url
中的别名。子目录下的各个文件指的是远程仓库的各个分支。
-
-
HEAD 文件指示目前被检出的分支;
-
index 文件保存暂存区信息。
-
config 文件保存 --local 的配置信息。例如我们使用
git remote add <别名> url
命令时,会在 config 文件中添加如下信息。其具体信息可参考 remote一节:[remote "demo"] url = https://github.com/birdandcliff/gitdemo.git fetch = +refs/heads/re:refs/remotes/demo/devlocal
网友评论