Skip to content

.git 目录结构

.git 目录是 Git 仓库的核心,包含了版本控制所需的所有数据。了解其结构有助于深入理解 Git 的工作原理。

objects/:对象存储

所有 Git 对象(Blob、Tree、Commit、Tag)都存储在 objects/ 目录:

.git/objects/
├── 1f/
│   └── 7a8b9c0d1e2f3456789012345678901234567  ← 松散对象
├── ab/
│   └── cdef0123456789012345678901234567890  ← 松散对象
├── info/                                        ← 包文件信息
└── pack/                                        ← 打包对象
    ├── pack-abc123.idx                          ← 索引文件
    └── pack-abc123.pack                         ← 打包数据

查看对象:

bash
# 列出所有对象
find .git/objects -type f

# 查看对象类型
git cat-file -t abc123

# 查看对象内容
git cat-file -p abc123

# 统计对象数量
git count-objects -v

refs/:引用存储

引用(分支、标签、远程追踪分支)存储在 refs/ 目录:

.git/refs/
├── heads/           ← 本地分支
│   ├── main
│   └── develop
├── remotes/         ← 远程追踪分支
│   └── origin/
│       ├── HEAD
│       └── main
└── tags/            ← 标签
    ├── v1.0.0
    └── v1.1.0

查看引用:

bash
# 查看分支引用内容(就是一个 SHA-1)
cat .git/refs/heads/main
# abc123456789...

# 列出所有引用
git show-ref

HEAD:当前分支指针

bash
cat .git/HEAD

# 正常状态(指向分支)
# ref: refs/heads/main

# 分离 HEAD 状态(指向 commit)
# abc123456789...

index:暂存区数据

.git/index 是二进制文件,存储暂存区的状态:

bash
# 查看暂存区内容(index 的文本表示)
git ls-files --stage
# 100644 abc123... 0  README.md
# 100644 def456... 0  src/app.js

index 文件包含:

  • 文件路径
  • 文件模式(权限)
  • 对应的 Blob SHA-1
  • 时间戳和文件大小(用于快速检测修改)

config:仓库级配置

bash
cat .git/config

# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
# [remote "origin"]
#     url = https://github.com/user/repo.git
#     fetch = +refs/heads/*:refs/remotes/origin/*
# [branch "main"]
#     remote = origin
#     merge = refs/heads/main

hooks/:钩子脚本

Git 钩子是在特定事件发生时自动执行的脚本:

.git/hooks/
├── pre-commit.sample      ← .sample 文件是示例,不会自动执行
├── prepare-commit-msg.sample
├── commit-msg.sample
├── post-commit.sample
├── pre-push.sample
├── pre-rebase.sample
└── ...

# 激活钩子:复制并去掉 .sample 后缀,赋予执行权限
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

logs/:引用日志

reflog 存储在 logs/ 目录:

.git/logs/
├── HEAD                   ← HEAD 的 reflog
└── refs/
    ├── heads/
    │   ├── main
    │   └── develop
    └── remotes/
        └── origin/
            └── main
bash
# 查看 HEAD reflog
cat .git/logs/HEAD
# (格式:旧SHA 新SHA 用户 时间 操作描述)

其他重要文件

.git/
├── COMMIT_EDITMSG     ← 最近一次提交的信息(供编辑器使用)
├── FETCH_HEAD         ← 最近一次 fetch 的结果
├── MERGE_HEAD         ← 合并过程中,被合并分支的 HEAD
├── MERGE_MSG          ← 自动生成的合并提交信息
├── ORIG_HEAD          ← 危险操作(reset、merge)前 HEAD 的位置
├── REBASE_HEAD        ← rebase 过程中的当前提交
├── packed-refs        ← 打包的引用(优化性能)
└── description        ← 仓库描述(GitWeb 使用)

packed-refs 文件:

bash
cat .git/packed-refs
# # pack-refs with: peeled fully-peeled sorted
# abc123... refs/heads/main
# def456... refs/remotes/origin/main
# ghi789... refs/tags/v1.0.0
# ^jkl012...    ← 附注标签指向的 commit(^ 前缀)

裸仓库的结构差异

裸仓库(git init --bare)没有工作区,.git/ 目录的内容直接在仓库根目录:

my-repo.git/
├── HEAD
├── config
├── objects/
├── refs/
└── ...

手动操作示例

bash
# 手动创建一个分支(底层原理)
echo "abc123456789..." > .git/refs/heads/my-branch
# 等价于:git branch my-branch abc123456789...

# 手动查看 HEAD 指向什么
git symbolic-ref HEAD
# refs/heads/main

# 手动移动 HEAD(切换分支的底层操作)
git symbolic-ref HEAD refs/heads/develop
# 等价于:git switch develop

总结

目录/文件内容
objects/所有 Git 对象(Blob、Tree、Commit、Tag)
refs/分支、标签、远程追踪分支的引用
HEAD当前位置的符号引用
index暂存区状态(二进制)
config仓库级配置
hooks/事件钩子脚本
logs/reflog 数据
packed-refs优化过的引用存储

理解 .git 目录结构,你就理解了 Git 所有概念的物理存储形式。当出现异常情况时,这些知识也能帮助你直接操作底层数据进行修复。