.git文件夹解析
这篇笔记围绕 .git 目录的可见方式、核心文件结构和分支切换机制,解释 Git 仓库状态并不只存在于工作区文件,而是由 HEAD、refs 和 objects 共同维护。内容先给出在 macOS 终端、Finder 与 VS Code 中查看隐藏 .git 文件夹的方法,再梳理 HEAD、config、refs、objects、hooks、logs 等条目的作用,其中 objects 被定位为 Git 存储提交、文件内容和目录结构的对象数据库。文章重点拆解 blob、tree、commit、tag 四类对象,说明对象文件经过压缩和哈希命名,不能直接用 cat 阅读,而应通过 git cat-file -t 和 -p 查看类型与内容。通过 commit hash 追踪到 tree,再从 tree 找到 blob 的示例,笔记解释了为什么 git cat-file -p commit 只能看到提交元信息、父提交和 tree 指针,而不会直接显示代码内容;若目标是查看一次提交的改动,应使用 git show 获取提交信息、diff 和文件变更。后半部分把 git switch 或 checkout 切换分支还原为 HEAD 指向变化、refs/heads 中提交哈希读取,以及对应 tree/blob 快照写入工作区的过程,并用 HEAD → refs → commit → tree → blob 的链路串起内部模型。文章也区分了 git switch 与 git checkout 的命令边界:前者语义更专注于分支切换,后者还能回滚文件或进入 detached HEAD,适合已经了解风险的场景。适合想从文件系统层面理解 Git 工作原理、排查提交对象疑惑,或希望更安全地区分分支切换与文件回滚命令的开发者阅读。
目录#
[[toc]]
文件夹介绍#
🍎 在 macOS 上如何查看 .git 文件夹?
✅ 方法一:终端查看(最推荐)
cd /你的项目路径/
ls -a
-a 参数表示“显示所有文件(包括隐藏的)”,你就能看到:
.git
.gitignore
README.md
...
✅ 方法二:在 Finder 中显示隐藏文件
⌘ Command + Shift + .(句号)
✅ 方法三:用 VS Code 直接看
1. 打开设置:⌘ + ,
2. 搜索:files.exclude
3. 把其中的 .git 相关条目注释掉或者取消选中(VS Code 会把某些目录默认隐藏)
📦 .git 目录里有什么?
| 文件/目录 | 作用 |
|---|---|
| HEAD | 当前指向的分支 |
| config | Git 仓库的本地配置 |
| refs/ | 各种分支、标签引用 |
| objects/ | 所有提交、文件内容、树结构的哈希存储(Git 的“数据库”) |
| hooks/ | 提交前/后可以触发的脚本 |
| logs/ | 操作日志(比如 reflog) |
没错,.git/objects/ 里那些奇奇怪怪的哈希文件,其实就是 Git 真正存储所有数据的地方,它们不是普通的“文件”,而是 Git 自己的 压缩 + 哈希命名的对象数据库。
📁 .git/objects/ 目录结构长这样:
.git/objects/
├── 1a/
│ └── 2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9
├── 2f/
│ └── e4d3a6...
├── info/
├── pack/
这些文件夹名(如 1a)和文件名(如 2b3c...)拼起来就是一个完整的 Git 对象的哈希 ID(SHA-1 / SHA-256):
1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9
Git 有 4 种核心对象类型:
| 类型 | 说明 |
|---|---|
| blob | 文件的实际内容 |
| tree | 目录结构,记录了哪些文件/子目录 |
| commit | 一次提交(指向一个 tree 和父提交) |
| tag | 标签对象(可选) |
• 它们不是普通文本文件,是 zlib 压缩过的二进制格式。
• 你如果直接 cat 它们,会看到乱码。
• 要查看内容,得用 Git 自己的工具来“解码”。
git cat-file -t <对象哈希> # 查看类型
git cat-file -p <对象哈希> # 查看内容
git cat-file -t 1a2b3c... # 会返回 commit
git cat-file -p 1a2b3c... # 会显示提交信息、tree、parent 等
❓ 为什么 git cat-file -p <commit hash> 没有我改的代码内容?
你查看的是一个 commit 对象,它只是一个元信息结构,不直接包含代码内容,而是指向一个 tree 对象,那个 tree 才是“这次提交的文件结构”。
~git cat-file -p 2128b10ad973b63050220008f4f829ca7cf1c91d
tree be01ffd3ae95fa8ddcea01d1c192763dbcb09067
parent 68e3ee2223934ac2286090a0af7dffc31a7a14f8
author jacinli <poplar_ripest80@icloud.com> 1742742852 +0800
committer jacinli <poplar_ripest80@icloud.com> 1742742852 +0800
Add async and threading examples, along with a tool factory implementation for weather and time functions. Enhance AsyncOpenAIOut to support tool calls and integrate with the new factory structure.
(ai_tools_show) ➜ ai_tools_show git:(main)
先使用commit的hash 进行查看,找到了这个tree 的东西
然后:
🧩 第一步:查看 tree 的结构
git cat-file -p be01ffd3ae95fa8ddcea01d1c192763dbcb09067
100644 blob 4d0b7ff0d8ff5a79dc93760360bc6d2f08a5b16f .env.example
100644 blob 467ecf380409fdafed743e40ec61b1d2ce995f4c .gitignore
100644 blob e4fba2183587225f216eeada4c78dfab6b2e65f5 .python-version
100644 blob 261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 LICENSE
100644 blob 5f55fdc35d072fc8541908b3ddea28cbd363dcd3 README.md
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 main.py
040000 tree 14c520d1ba7e2871f6a85a83c28f790043eaaf02 opensource
100644 blob 8dcd957ad7bc913282d710d1387fe45c9879aeeb pyproject.toml
040000 tree 852d4ade89a36f56b3140fc7a7d3308222b425ae python_base
100644 blob b38a7182879346d08727dc60c243774cade67314 requirements.txt
040000 tree c3217f601f44899455e6933b807970160d550ebd routers
040000 tree 1c56dc17f0e051a7253e67be29106d81e6d4aa4b services
100644 blob 5069490541ffdc73cc5605f0f96822dfafce0273 uv.lock
再次查看:
git cat-file -p b38a7182879346d08727dc60c243774cade67314
openai
python_dotenv
fastapi
uvicorn
itsdangerous
sqladmin
sqlalchemy
langfuse%
(ai_tools_show) ➜ ai_tools_show git:(main)
输出的就是你 main.py 的真实内容!✨
🧩 如果遇到 tree(目录),继续查
你想快速查看这次提交的改动文件内容,其实可以直接:
git show 2128b10ad973b63050220008f4f829ca7cf1c91d
这会自动帮你: • 展示提交信息 ✅ • 展示 diff ✅ • 展示改了哪些文件 ✅ • 展示文件内容改了哪几行 ✅
切换分支#
Git 切换分支本质上就是更新 .git/HEAD 和 .git/refs/ 的指向!
🧠 问题核心:
❓ 我每次 git checkout 或 git switch 切换分支,Git 到底做了啥?
✅ 一句话回答:
Git 本质上只是修改了 .git/HEAD 文件的内容,让它指向你要切换的分支(分支指向的是某个 commit 对象),然后把那个提交的快照内容 checkout 到工作区。
1. .git/HEAD 是当前分支的“指针”
cat .git/HEAD
ref: refs/heads/main
说明 HEAD 当前指向的是 main 分支。
2. .git/refs/heads/main 存的是这个分支的 最新 commit 哈希
cat .git/refs/heads/main
2128b10ad973b63050220008f4f829ca7cf1c91d
这就是 main 分支的“最新提交 ID”。
HEAD → refs/heads/main → commit 哈希 → tree → blob → 文件快照
3. 当你执行 git switch feature-x 时,发生了这些变化:
• .git/HEAD 从指向 main 改为指向 refs/heads/feature-x
• Git 读取 feature-x 分支对应的 commit
• 提取它指向的 tree 和 blob
• 替换你工作目录中的文件,使其反映这个 commit 的快照内容
| 命令 | 用途 | 特点 |
|---|---|---|
| git switch | 专注于“切换分支” | 更安全、更清晰,推荐使用 |
| git checkout | “万能命令”:切分支、切文件、切提交都能干 | 功能强,但容易误操作(如误切文件) |
| 特性 | git switch | git checkout |
|---|---|---|
| 语义 | 只用于“切换分支” | 又能切分支又能切文件又能切提交 |
| 推荐 | ✅ Git 官方推荐 | ⚠️ 功能强但容易误用 |
| 是否 stash | ❌ 默认不会自动 stash | ❌ 一样不会 |
| 是否检查冲突 | ✅ 是的 | ✅ 也是 |
| 交互提示 | ✅ 更人性化(会提示 use --discard-changes) | ❌ 更容易误删 |
日常开发中,推荐你使用:
git switch <branch> # 切已有分支
git switch -c <branch> # 创建并切换分支
git checkout -- main.py # 回滚某个文件
git checkout 123abc # 暂时进入某个提交(detached HEAD)
评论
还没有评论,来发第一个吧