🚎 探秘 .git 目录

305 阅读7分钟

Git 具有优秀的存储能力, 如果没有远端服务器,本地也能做一个版本管控的系统。为什么呢?那我们今天通过 .git目录一探究竟

执行 git init 后会在当前目录 生成一个 .git 文件夹,这里面就是 Git 最核心的东西。 进入 Git 目录可以看到类似的如下结构

├── HEAD
├── cofnig
├── refs
├── objects

HEAD 文件

$ cat .git/HEAD

打印出  ref: refs/heads/当前分支名称 , 说明HEAD 是一个引用, 指向的是当前工作分支

# 打印出:  本地所有分支, 当前分支高亮并有一个星号
$ git branch -v

# 切换到master分支
$ git checkout master
$ cat .git/HEAD

打印出  ref: refs/heads/master , 说明HEAD 是一个引用, 指向的是当前工作分支

总结: 切换分支的时候, HEAD 的内容会发生变化,以便告诉我们,现在我们工作在哪个分支上

config 文件

存放与本地仓库相关的配置信息

$ cat .git/config

打印出 当前仓库的配置信息

通过 git config 验证一下是否会影响config文件的内容

 $ git config --local user.name 'guyue'


$ git config --local --list
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
......
......
#  打印出 user.name=guyue  表示配置已生效
user.name=guyue


# 查看config文件中的变更
$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
        precomposeunicode = true
[remote "origin"]
......
......
[branch "master"]
        remote = origin
        merge = refs/heads/master
[remote "upstream"]
......
......
[user]
# ------------------可以看到用户名这里已经被修改了
        name = guyue
[branch "patch-1"]
        remote = origin
        merge = refs/heads/patch-1

refs 文件夹

$ cd .git/refs
$ ls
# 打印出
heads   remotes tags

出现了 headstags, remotes

tags 文件夹

tags 意味着我们的 git 仓库可以有很多标签,标签也叫里程碑: 项目开发到一定程度,有一个关键的成果,比如 v1.0.0 版本, 我们就可以针对 v1.0.0 版本打上标签。 标识 v1.0.0 是一个里程碑

参考官方文档如何打标签

# 在git仓库根目录创建新标签
$ git tag -a v1.4 -m "my version 1.4"

# 查看是否创建成功, 如果成功,则会打印出 v1.4
$ git tag

# ---------------------------- 练习开始

# 进入 .git/refs/tags 目录
$ cd .git/refs/tags

# 列出所有文件夹及文件, 这里会打印出 v1.4
$ ls

# 查看v1.4的内容 ,这里会出现一串哈希值(代表v1.4的版本号)
$ cat v1.4
60fbe5e04909252140c5c6c7bdc2581949828303


#  查看哈希值的类型   打印出:  tag(意思是此版本号表示一个标签)
# git cat-file -t 从头截取的一段哈希值
$ git cat-file -t 60fbe5e049092

heads 文件夹

heads 对应的就是我们的分支, 分支说白了就是一个独立的开发空间,

# 进入 .git/refs/heads 目录
$ cd .git/refs/heads

$ ls
# 会打印出当前git仓库的所有本地分支名称

总结: **heads 文件夹存储了 当前 git 项目的所有本地分支 。 ** **这里可以得出 ** HEAD文件 是指针, 指向了 refs/heads/当前的工作分支

master 文件

$ cat master

# 打印出一串哈希值(不同git项目生成结果不同),
#打印出的结果是: 0b1a98929cce8eea918c160571a046586f1b033f

# 任意复制一部分编码,执行以下操作
# 查看哈希值的类型
$ git cat-file -t  0b1a98929cce

打印出 commit(表示一个提交记录)

# 查看 文件内容
$ git cat-file -p  0b1a98929cce
# ----------------------------------打印出此提交记录的内容
tree 614f56d82cde7939c1f7c1443cbece9ebc20a730
parent be7ec654102a478e812f23728542fbca72ae164c
author hh <guyue@jjjj.local> 1607502637 +0800
committer hh <guyue@jjjj.local> 1607502637 +0800

fix:修改不部分翻译
$ git branch -v
# 打印出 所有本地分支
* master  0b1a989 fix:修改不部分翻译
  patch-1 b054a05 feat:ts高级类型清单
  patch-2 ac6ed3a fix:合并冲突
  patch-3 ac6ed3a fix:合并冲突
  patch-4 ac6ed3a fix:合并冲突
  patch-5 ac6ed3a fix:合并冲突

可以看到 master 指向 0b1a98929cce 这个提交记录, 因此 cat .git/refs/heads/master 指向的就是 某一次提交记录

cat .git/refs/heads/分支名称 同理

objects 文件夹

objects:存放对象 .git/objects/ 文件夹中的子文件夹都是以哈希值的前两位字符命名 每个 object 由 40 位字符组成,前两位字符用来当文件夹,后 38 位做文件。

$ cd .git/objects

$ ls
# 打印出下面的内容(不同git项目生成结果不同)
00      18      30      48      60      78      90      a8      c0      d8      f0
01      19      31      49      61      79      91      a9      c1      d9      f1
02      1a      32      4a      62      7a      92      aa      c2

17      2f      47      5f      77      8f      a7      bf      d7      ef

可以看出, 有 info 文件夹pack 文件夹两个字符组成的 N 个文件夹

练习一(blob 类型)

$ cd 00

$ ls
# 打印出下面的内容(不同git项目生成结果不同)
09659e7fbbc537bd66f76affd2e6cfb402fefb  a68a4f51e4071d9d9b50144e541d44edfc989c
23c5cdf5f2fd4254a11282c52fc1fd18f012ca  c85e5bfdf7308c1cc847f310a8a75c2d836ed5
4e7d16f5f24da35881e74435c0bdadd7d9dc75  cdf1634db1d9385ddb795e8814cd286cac43f6
539ec1d022549bb14aa82070ec603c6ee2595b  d44d5e1a271159dbe74bf60b4dd20e96bd36d6
61102f1e521d98247143e95c15c111475e349a  ee505947418c3aec9dc19a43ca772d12cfe57e
74ae975a3d60843dc8a5d12eb8f78aadb1cabe

复制 00 与 上面任意一组记录 组成一个 哈希值

# 打印出 类型是 commit   不同git项目生成结果不同
$ git cat-file -t 0009659e7fbbc537bd66f76affd2e6cfb402fefb
# 打印出下面的内容(不同git项目生成结果不同)
blob

** blob(表示是一个文件) **

打印出此文件记录的内容

$ git cat-file -p 0009659e7fbbc537bd66f76affd2e6cfb402fefb
# 可以打印出文件内容

练习二(commit 类型)

$ cd 3f

$ ls
# 打印出下面的内容(不同git项目生成结果不同)
09f7f770e2143b0a37c63339ea39bbc88a5b53  9a0fbdc8c98620f9bd38eb844635b2639a6e81
0d5f55d2ad3d788c1cb5f0811f15a1a94fc6ee  c7d74438891bccc4c99d0e4dff4343b97d5e7e
4f7e532ca16ad824440fdea11cf68b62ccd3b2  dcede941d04071581e0b4f345303289eac2af0
52e346f618f8916ec49a15ffbfbd206b7e93fa  f97336bbf2b943066c0497076d1731a3149fa7
72dff29a69612c14eaec559e2a11bcced48b5b

复制 3f 与 上面任意一组记录 组成一个 哈希值

$ git cat-file -t 3f0d5f55d2ad3d788c1cb5f0811f15a1a94fc6ee
#
commit

commit(表示一个提交记录)

打印出此文件的内容

$ git cat-file -p 3f0d5f55d2ad3d788c1cb5f0811f15a1a94fc6ee
# 会打印出 提交时的备注

练习三(tree 类型)

$ cd c2

$ ls
# 打印出下面的内容(不同git项目生成结果不同)
47b377ff5b1a2a6887724d3dc749a5555e5d18  8dd7896fb70a34308fac24e95ccef07e24f033
4cef38c2e64d9829e28cd94b5eef3c3a5776d3  be61fdc05dbacbe86bbd8739b273a3f48f4991
568048633b334dadfee8fd8a7f3c145432a2e6  c66fae969215048844e3720ac0aa62e6e58980
59a7f470d5e4fd82e2b62ca10e9dd32f7f96db  fc78f870f6854c145881ea4ae78d5e5f9c80e8

复制 c2 与 上面任意一组记录 组成一个 哈希值

$ git cat-file -t c2e81a1619c262ac1da37410eb2d100e0105d3db
#
tree

打印出 ** tree(表示一棵树) **

打印出此树的内容

$ git cat-file -p c2e81a1619c262ac1da37410eb2d100e0105d3db

image.png

总结

在 Git 中可以通过 哈希值取得所有的东西

Git 的几种对象

  • commit
  • blob
  • tree

跟文件查看的相关 Git 命令

# 查看文件类型
$ git cat-file -t 哈希值

# 查看文件内容
$ git cat-file -p 哈希值

# 查看文件大小
$ git cat-file -s  哈希值

.git 目录总结

  • HEAD文件:指向当前的工作路径
  • config文件:存放本地仓库(local)相关的配置信息。

refs [文件夹]       • heads (存放当前项目的所有分支)       • tags (存放的当前项目的所有标签,又叫做里程碑)

  • objects:存放对象 .git/objects/ 文件夹中的子文件夹都是以哈希值的前两位字符命名 每个 object 由 40 位字符组成,前两位字符用来当文件夹,后 38 位做文件。
  • description (仓库的描述信息文件)

参考文档

  1. 通过 .git 目录深入理解 Git!