HEAD 文件

HEAD 文件内容:

ref: refs/heads/master

ref 代表引用,refs/heads/master 代表当前引用所指向的分支,即当前工作区所在的分支,当执行切换分支操作时,HEAD 文件中的 ref 值会随着切换的分支变化。

config 文件

config 文件基本内容:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[user]
    name = yourname
    email = youremail

[core] 代表当前 Git 管理中的主要配置,[user] 代表用户配置,随着 Git 管理的不断复杂,所有的配置项都将被存放在 config 文件中。

refs 文件夹

分支 heads

heads 文件夹存储的是本地所有分支文件,文件名与分之名一一对应,文件内容为当前分支所在的提交历史记录的 commit 对象。

查看 heads 文件夹:

$ ls -al .git/refs/heads
# drwxr-xr-x  3 systemname  staff   96  2 24 17:31 .
# drwxr-xr-x  5 systemname  staff  160  2 12 17:35 ..
# -rw-r--r--  1 systemname  staff   41  2 24 17:31 master

查看分支文件:

$ cat .git/refs/tags/master
# ef5aaed0707989ebc069efcd842424f6315ab4e2
$ git cat-file -t ef5aaed0707989ebc069efcd842424f6315ab4e2
# commit

其实使用 git checkout 命令切换分支时,就是在更改 HEAD 文件的引用内容,即上面提到的 ref: refs/heads/branchname,进而找到 heads 文件夹内对应的分支文件内的提交记录,将工作区代码还原到该提交记录的版本。

标签 tags

在项目开发中,经常会在某些阶段达到某一个 “里程碑”,比如版本从 v0.0.1 开发到 v1.0.0,可以专门为这个版本的 commit 打上一个标签,而 refs/tags 文件夹就是用来存放这些标签的(文件名与标签名相同),每一个标签文件内存储的是这个 “里程碑” 提交的历史记录的 tag 对象,tag 对象中存储着当前标签对应历史版本的 commit 对象。

查看 tags 文件夹:

$ ls -al .git/refs/tags
# drwxr-xr-x  6 systemname  staff  192  2 24 17:31 .
# drwxr-xr-x  5 systemname  staff  160  2 12 17:35 ..
# -rw-r--r--  1 systemname  staff   41  2 15 18:33 1.0.0

查看标签文件:

$ cat .git/refs/tags/1.0.0
# ef5aaed0707989ebc069efcd842424f6315ab4e2
$ git cat-file -p ef5aaed0707989ebc069efcd842424f6315ab4e2
# object bcadbfea5e937e9b5eaed113dd8149c86124d72a
# type commit
# tag 1.0.0
# tagger yourusername <youruseremail> 1550212832 +0800

$ git cat-file -t bcadbfea5e937e9b5eaed113dd8149c86124d72a
# commit

可以使用 git cat-file 命令查看文件对象类型。

objects 文件夹

查看 objects 内部

$ ls -al .git/objects
# drwxr-xr-x  72 systemname  staff  2304  2 24 17:31 .
# drwxr-xr-x  13 systemname  staff   416  3 14 15:43 ..
# drwxr-xr-x   4 systemname  staff   128  2 24 17:29 00
# ...
# drwxr-xr-x   3 systemname  staff    96  2 24 17:29 f9
# drwxr-xr-x   2 systemname  staff    64  2 12 16:59 info
# drwxr-xr-x   2 systemname  staff    64  2 12 16:59 pack

objects 文件夹中,除了 infopack 存储的都是十六进制命名的文件夹,在文件夹内部存储着以哈希值命名的文件,在 Git 中的策略是将十六进制文件名和哈希值的文件名进行组合,使用 git cat-file 可以查看该完整哈希值的对象类型,肯能为 treeblobcommit

Git 对象:

  • tree:树对象,存储内容为 blob 对象的哈希值和对应的文件名称;
  • blob:存储文件内容,只要文件内容相同,则始终生成唯一一个 blob 对象;
  • commit:存储提交的相关信息。

tree、commit、blob 对象的关系

Git 中最重要的就是这三个对象以及它们之间的关系,这对于理解 Git 的原理非常有帮助,下面有一张关系图。


tree、commit、blob 关系图
tree、commit、blob 关系图


commit 对象中,tree 代表提交时所在的树,一个 commit 对象只会对应一棵树,tree 对象存储的只是当前 commit 时,所有文件目录的一个 “快照”,tree 对象中的 tree 对象代表该文件夹中还有文件夹,tree 中的 blob 对象代表文件,blob 对象中存储的是文件内容,Git 在这里存储时忽略文件名,只要文件内容一样就只会存储一份,大大的节约了存储空间。

blob 对象是在将文件增加到暂存区后创建的,committree 对象在进行提交操作后创建。

hooks 文件夹

hooks 文件夹,默认存储了一系列的 hook 文件,用于在执行某些特定的 Git 命令时,在某个生命周期执行,内部可以编写 shell 脚本,也可以通过 haskynpm 包来介入。

例如下面文件:

# ...
pre-commit.sample # 提交前执行
pre-push.sample # 推送前执行
pre-rebase.sample # 变基前执行
# ...

上面的文件默认扩展名为 sample,即默认不生效,要想在某个 Git 操作时可以执行对应的 hook 文件,只需要去掉对应 hook 文件的扩展名即可。