概念认知
理解 Git 的核心概念与底层机制——从"是什么"到"怎么存",建立正确的心智模型。
Git 基础
Git是一个分布式版本控制系统(Distributed Version Control System),由 Linus Torvalds 在 2005 年创建,最初是为了管理 Linux 内核的开发。它运行在你的本地电脑上,是一个纯粹的命令行工具。
GitHub和GitLab是基于 Git 的代码托管平台,它们在 Git 之上提供了 Web 界面、协作管理、CI/CD、代码审查等附加功能。
Git = 快递公司(负责打包、追踪、分发包裹的底层系统)
GitHub / GitLab = 快递柜 + 社区平台(提供存放包裹的网点,以及评论、协作等社交功能)
关键区别:
- Git 是必须安装的本地工具,GitHub/GitLab 是可选使用的在线服务
- 没有 GitHub 你仍然可以完整使用 Git——只是少了远程协作的便利
- 类似的平台还有 Bitbucket、Gitee(码云)、Codeberg 等,底层都是 Git
版本控制(Version Control)是一种记录文件每一次变更的系统,让你可以:
- 回溯——随时回到任意历史版本
- 比较——查看任意两个版本之间的差异
- 协作——多人同时修改,自动合并或标记冲突
- 追责——每一行改动都有作者和时间记录
项目报告_v1.docx 项目报告_v2.docx 项目报告_最终版.docx 项目报告_最终版2.docx 项目报告_真的最终版.docx 项目报告_打死不改版.docx 项目报告_最终版_改格式.docx
用文件夹管理版本 → 改名混乱、无法比较差异、无法追踪谁改了什么、误删无法恢复。
版本控制的发展经历了三个阶段:
1. 本地版本控制(如 RCS) → 只在本机记录变更 2. 集中式版本控制(如 SVN) → 一个中央服务器,所有人联网操作 3. 分布式版本控制(如 Git) → 每个人拥有完整历史副本,离线也能工作
仓库与四层模型
仓库(Repository)= 项目的所有文件 + 完整的变更历史。当你执行 git init 时,Git 会在项目根目录下创建一个隐藏的 .git/ 目录,这个目录就是仓库的核心。
.git/ ├── HEAD # 当前指向的分支 ├── config # 仓库配置 ├── objects/ # 所有 Git 对象(blob/tree/commit)的存储 ├── refs/ # 引用(分支指针、标签指针) │ ├── heads/ # 分支 │ └── tags/ # 标签 └── logs/ # 操作日志(reflog)
本地仓库是你电脑上的完整副本,包含完整历史。远程仓库(如 GitHub 上的仓库)是托管在服务器上的副本。两者通过 git push(推送)和 git pull(拉取)同步。
因为每个本地仓库都有完整历史,所以即使远程服务器宕机,任何人的本地副本都可以恢复整个项目。
Git 的文件流转涉及四个区域,理解它们是掌握 Git 的关键:
- 工作区——你能看到的实际文件目录,编辑代码就在这里
- 暂存区——
git add后文件进入的中间态,是下次提交的"快照清单" - 本地仓库——
git commit后快照永久保存在.git/objects/中 - 远程仓库——
git push后同步到服务器(如 GitHub)
暂存区让你可以精确控制哪些修改进入下一次提交。你可以只 add 部分文件,甚至只 add 一个文件的部分行(git add -p),而不是一股脑提交所有改动。这让每次提交都是逻辑完整的。
提交与分支
commit(提交)是 Git 的核心概念,它是对项目某一时刻的完整快照(不是差异!),附带的元数据包括:
commit a1b2c3d4e5f6... ← SHA-1 哈希(唯一标识)
Author: 张三 <zhangsan@email.com>
Date: Mon May 26 20:00:00 2026 +0800
feat: 添加用户登录功能 ← 提交信息
tree 9f8e7d6c5b4a... ← 指向项目根目录的 tree 对象
parent b2c3d4e5f6a1... ← 指向上一个 commit(第一个 commit 无 parent)- tree 指针——指向此次快照的目录结构(tree 对象)
- parent 指针——指向前一个 commit,形成链表式历史
- author——作者姓名和邮箱
- message——提交说明,描述本次改动
- SHA-1——40 位十六进制哈希,作为此 commit 的唯一 ID
Git 的 commit 不是存差异(diff),而是存完整快照。未变化的文件不会重复存储,而是通过 tree 对象复用已有的 blob。这就是为什么 Git 既有完整历史又很高效。
分支(Branch)的本质是一个指向某个 commit 的可移动指针。当你创建一个新分支时,Git 只是创建了一个新的指针,指向当前 commit——这是一个 O(1) 操作,几乎瞬间完成。
# 创建分支的本质 main → a1b2c3d (主分支指针) git branch feature main → a1b2c3d (不动) feature → a1b2c3d (新指针,指向同一个 commit)
每次在新分支上 commit,分支指针就向前移动一步,而 main 指针不动,于是两条线就"分叉"了。
在 SVN 等集中式系统中,创建分支是复制整个目录,又慢又占空间。Git 的分支只是一个 40 字节的文件(存 SHA-1),创建和删除几乎没有成本。这鼓励你频繁使用分支来隔离不同的工作。
HEAD是一个特殊的指针,指向当前所在分支的最新提交。通常 HEAD 指向一个分支名,分支名再指向某个 commit:
HEAD → main → a1b2c3d(HEAD 通过分支名间接指向 commit)
detached HEAD(分离头指针)是指 HEAD 直接指向一个 commit,而不是通过分支名间接指向。这通常发生在:
git checkout <commit-hash>——直接切到某个 commitgit checkout <tag>——切到某个标签git rebase -i——变基过程中
在 detached HEAD 状态下做的新 commit,不会属于任何分支。当你切走后,这些 commit 可能被垃圾回收(git gc)永久丢失。如果需要保留,应该先 git checkout -b <new-branch> 创建一个分支来保存它们。
底层机制
ref(引用)是 Git 中指向 commit 对象的指针名称,包括分支和标签。它们存储在 .git/refs/ 目录下,文件内容就是 40 位 SHA-1 哈希。
.git/refs/
├── heads/ ← 分支引用
│ ├── main ← 内容: a1b2c3d4e5f6...
│ └── feature-login
└── tags/ ← 标签引用
└── v1.0.0reflog(引用日志)记录了 HEAD 指针的每一次移动历史,包括 commit、checkout、reset、rebase 等操作。即使你"删除"了分支或 reset 了提交,reflog 默认仍会保留 90 天。
$ git reflog
a1b2c3d HEAD@{0}: commit: feat: 添加登录页
e5f6a1b HEAD@{1}: checkout: moving from main to feature
b2c3d4e HEAD@{2}: commit: fix: 修复首页 bug
...几乎所有"误操作"都可以通过 reflog 找回——git reset --hard 了?没关系,git reflog 找到之前的 commit hash,再 git reset --hard <hash> 即可恢复。只要还没过期,历史就在那里。
Git 使用 SHA-1(Secure Hash Algorithm 1)为每个对象生成一个40 位十六进制的唯一标识符。它的作用有两个:
- 内容寻址——哈希值由对象内容决定,相同内容必然产生相同哈希。Git 用哈希值作为文件名来存储对象,天然去重。
- 完整性校验——任何内容的篡改都会导致哈希值变化,所以你不可能悄悄修改一个 commit 而不被发现。
# SHA-1 的生成方式(以 blob 为例) $ echo "hello" | git hash-object --stdin ce013625030ba8dba906f756967f9e9ca394464a # 内容变了,哈希就变 $ echo "hello!" | git hash-object --stdin f75baa6b59cc73c7363a4a96a5b5e9fdac45c3d2
Git 允许你只用前 7 位来引用 commit(如 a1b2c3d),因为在一个仓库中出现前 7 位碰撞的概率极低。如果发生碰撞,Git 会提示你使用更多位数。
Git 底层只有三种核心对象类型,它们组合起来存储了项目的完整历史:
- blob——文件内容。只存内容,不存文件名。相同内容 = 同一个 blob。
- tree——目录结构。记录了目录下每个条目的名称、类型(blob 或 tree)和 SHA-1。
- commit——提交快照。指向一个 tree,包含作者、时间、message 和 parent。
每个 commit 指向一个 tree(根目录),tree 记录目录结构(文件名 → blob/tree 的映射),blob 存实际文件内容。这是一棵从 commit 到 tree 再到 blob 的有向无环图(DAG)。相同内容的文件共享同一个 blob,这就是 Git 高效存储的秘诀。
