Skip to content
Chapter 01

概念认知

理解 Git 的核心概念与底层机制——从"是什么"到"怎么存",建立正确的心智模型。

Git 基础

Git 是什么?和 GitHub / GitLab 有什么区别?

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 的关键:

工作区Working Directorygit add暂存区Staging Areagit commit本地仓库Local Repositorygit push远程仓库Remote Repositorygit checkout
  • 工作区——你能看到的实际文件目录,编辑代码就在这里
  • 暂存区——git add 后文件进入的中间态,是下次提交的"快照清单"
  • 本地仓库——git commit 后快照永久保存在 .git/objects/ 中
  • 远程仓库——git push 后同步到服务器(如 GitHub)
为什么需要暂存区?

暂存区让你可以精确控制哪些修改进入下一次提交。你可以只 add 部分文件,甚至只 add 一个文件的部分行(git add -p),而不是一股脑提交所有改动。这让每次提交都是逻辑完整的。

提交与分支

什么是 commit?包含哪些信息?

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 指针不动,于是两条线就"分叉"了。

对比其他 VCS

在 SVN 等集中式系统中,创建分支是复制整个目录,又慢又占空间。Git 的分支只是一个 40 字节的文件(存 SHA-1),创建和删除几乎没有成本。这鼓励你频繁使用分支来隔离不同的工作。

什么是 HEAD?detached HEAD?

HEAD是一个特殊的指针,指向当前所在分支的最新提交。通常 HEAD 指向一个分支名,分支名再指向某个 commit:

HEAD → main → a1b2c3d(HEAD 通过分支名间接指向 commit)

detached HEAD(分离头指针)是指 HEAD 直接指向一个 commit,而不是通过分支名间接指向。这通常发生在:

  • git checkout <commit-hash>——直接切到某个 commit
  • git checkout <tag>——切到某个标签
  • git rebase -i——变基过程中
detached HEAD 的风险

在 detached HEAD 状态下做的新 commit,不会属于任何分支。当你切走后,这些 commit 可能被垃圾回收(git gc)永久丢失。如果需要保留,应该先 git checkout -b <new-branch> 创建一个分支来保存它们。

底层机制

ref 和 reflog 是什么?

ref(引用)是 Git 中指向 commit 对象的指针名称,包括分支和标签。它们存储在 .git/refs/ 目录下,文件内容就是 40 位 SHA-1 哈希。

.git/refs/
├── heads/               ← 分支引用
│   ├── main             ← 内容: a1b2c3d4e5f6...
│   └── feature-login
└── tags/                ← 标签引用
    └── v1.0.0

reflog(引用日志)记录了 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 是你的安全网

几乎所有"误操作"都可以通过 reflog 找回——git reset --hard 了?没关系,git reflog 找到之前的 commit hash,再 git reset --hard <hash> 即可恢复。只要还没过期,历史就在那里。

SHA-1 哈希的作用?

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 / tree / commit)?

Git 底层只有三种核心对象类型,它们组合起来存储了项目的完整历史:

  • blob——文件内容。只存内容,不存文件名。相同内容 = 同一个 blob。
  • tree——目录结构。记录了目录下每个条目的名称、类型(blob 或 tree)和 SHA-1。
  • commit——提交快照。指向一个 tree,包含作者、时间、message 和 parent。
commita1b2c3d...tree / parent / author / msgtree (root)src/ | README.mdREADME.mdblob"Hello World"src/tree (src)main.py | utils.pymain.pyblob"print('hi')"utils.pyblob"def help():"
理解要点

每个 commit 指向一个 tree(根目录),tree 记录目录结构(文件名 → blob/tree 的映射),blob 存实际文件内容。这是一棵从 commit 到 tree 再到 blob 的有向无环图(DAG)。相同内容的文件共享同一个 blob,这就是 Git 高效存储的秘诀。

动手实践

Git 对象探索
欢迎来到 Git 对象探索终端!
输入 help 查看可用命令,探索 Git 内部对象结构。
$