跳到内容

元数据卡: 前置知识:ch03-git-intro.md | 预计时间:10 分钟 | 完成标志:能理解工作区/暂存区/仓库的流转

三棵树的模型

上一节你经历了"改错了、想回去"的问题。但你可能没留意到:Git 的存档不是一步完成的。它分三步:

工作区 (Working Directory)    ← 你敲代码的地方
     │ git add

暂存区 (Staging Area)        ← 你确认"这些文件要存档"的地方
     │ git commit

仓库 (Repository / .git)     ← 存档永久存放的地方

这就是 Git 的三棵树模型。每棵树代表一个区域,文件在三个区域之间流转。

为什么非要分三步?

你在工坊里打磨一件兵器:工作区是你的打磨台,上面堆着各种材料。你选中一块精铁放到暂存区——也就是操作台旁边的"待加工框"里。最后你确定这批材料没问题,才把它放进成品仓库。

如果只有"工作区→仓库"两步:你改了一个文件的一半,因为着急存档,顺手 git commit 了——结果把只改了一半的文件也存进去了。有了暂存区,你可以精挑细选:这个文件改完了,放到暂存区;那个文件还在改,先不进去。


查看差异

你改了一个文件,但改了哪些行?Git 可以告诉你:

bash
# 查看工作区和暂存区的差异
git diff

# 查看暂存区和上次 commit 的差异
git diff --staged

语言: Bash 如何运行: 在已跟踪的文件上做修改后再执行 预期输出: 一个补丁格式的差异报告:

diff
diff --git a/main.py b/main.py
index e69de29..980a0d5 100644
--- a/main.py
+++ b/main.py
@@ -0,0 +1 @@
+print("hello")

红线(-)开头的是删除的行,绿线(+)开头的是新增的行。如果一行变成蓝色,那是修改的行(同时有 -+)。

试试看: 修改一个已 commit 的文件,先跑 git diff 看看变化,然后再 git add,再跑 git diff --staged 看看两次输出的区别。


撤销操作

场景 A:还没 add,想放弃修改

bash
# 文件改了一半,想放弃——恢复成上次 commit 的状态
git restore main.py

试试看: 修改 hello.txt 后执行 git restore hello.txt,用 cat 确认内容恢复。

场景 B:已经 add 了,想从暂存区拿掉

bash
# 不小心 add 了一个不该加的文件
git restore --staged main.py

这只会把文件从暂存区移除,不会动工作区里的文件。文件内容还在,只是下次 commit 不会包含它。

试试看: 执行 git add . 后跑 git status,再执行 git restore --staged main.py 再跑 git status,观察变化。

场景 C:commit 完了,发现漏了个文件

你刚 commit 了,突然想起还有一个重要文件没加进去。别慌——你不用再建一个新的 commit,可以修正上一次:

bash
# 把漏掉的文件加进去
git add 漏掉的文件.py
git commit --amend -m "修复:包含漏掉的文件"

语言: Bash 如何运行: 在最近一次 commit 之后、推送之前执行 预期输出:

[master b2c3d4e] 修复:包含漏掉的文件
 Date: Mon Jun 23 16:05:00 2026 +0800
 2 files changed, 10 insertions(+)

--amend 不是"修改上次 commit"——它是替换上次 commit。Git 会创建一个新的 commit 代替旧的,旧的就像没存在过一样。

警告: 如果上次 commit 已经推送到了远程仓库(git push 过了),就不要用 --amend。因为这会重写历史——别人已经拉过旧版本了,你再重写他们会精神错乱。如果确定是 solo 分支且没人依赖你,可以用 git push --force


常见陷阱一:忘了 .gitignore

你把 Python 项目 push 到 GitHub,发现 .pyc 文件和 __pycache__ 目录被一起推上去了。问题不大,但每次 git status 都看到一堆无意义的缓存文件。更麻烦的是——如果有人拉下去在别的操作系统上跑,这些缓存文件在你的 PR review 里会出现"增加了一堆二进制文件"的噪音。

解决方案:在项目根目录创建一个 .gitignore 文件:

__pycache__/
*.pyc
.env
node_modules/
.DS_Store

Git 会自动忽略这些文件。它们不会出现在 git status 中,也不会被 git add . 加进去。

试试看: 在你的练习项目里创建 .gitignore 文件,然后运行 git status 对比前后变化。


常见陷阱二:大文件灾难

有人把一个 500MB 的数据集 commit 进了 Git 仓库。从此每次 git pull 都要下载 500MB——哪怕后来删掉了,这 500MB 的历史记录永远存在于 .git 对象存储中。Git 不会偷懒只存"差异",它会存储每一次的快照。

如果不小心干了这种事,用 git filter-branchBFG Repo-Cleaner 才能从历史中彻底清除。或者更聪明的办法:从一开始就用 .gitignoreGit LFS(Git Large File Storage)管理大文件。

记住:不要把数据库文件、编译产物、依赖包、临时文件放到 Git 里。.gitignore 是你最好的防火墙。


下一步

你学会了在一条时间线上存档、撤销、修复。但只有一个"平行世界"——你只能在一根时间线上工作。如果你想同时尝试两个完全不同的方案呢?

下一节,我们开启 分支——Git 的分叉宇宙开关。

继续:分支与合并基础

Built with VitePress | Software Systems Atlas