---
title: "11.1 为什么需要 Git"
description: "从'最终版'困境出发，理解 Git 解决了什么问题"
---

# 11.1 为什么需要 Git

> **本节目标**：理解 Git 的核心价值——它解决了什么问题，以及你可能已经在用它了。

---

## "最终版"困境

你一定经历过这样的场景。

写论文的时候，桌面上出现了这些文件：

```
毕业论文.docx
毕业论文-修改版.docx
毕业论文-最终版.docx
毕业论文-最终版2.docx
毕业论文-打死不改版.docx
毕业论文-导师又让改了.docx
```

做 PPT、改设计稿、写策划案——只要涉及"反复修改"的工作，你都会本能地用文件名来标记版本。这个方法能用，但问题很明显：哪个才是最新的？文件名骗人。想回到三天前的版本？可能已经被覆盖了。两个版本之间到底改了什么？只能肉眼对比。多个人同时改同一份文件？彻底乱套。

你可能觉得"我记性好，我知道哪个是最新的"。但这种信心通常只能维持三天。三天前你改了什么、为什么改、改之前是什么样——这些信息在你脑子里的保质期非常短。文件名能承载的信息量也极其有限，"最终版2"到底比"最终版"多了什么？你得打开两个文件逐行对比才知道。

而当你真的需要回到某个历史版本时——比如导师说"还是上周那版好"——你会发现上周那版可能已经被覆盖了，或者你根本不确定哪个文件对应上周的状态。

这些问题在写论文的时候还能忍——毕竟论文就那么几个文件，最多几十页。但写代码完全不同。

一个 Web 项目可能有几百个文件，改一个功能可能涉及十几个文件的联动修改。你给首页加了一个搜索框，可能同时改了页面组件、API 接口、路由配置、样式文件。用文件名管版本？你得复制整个项目文件夹。肉眼对比差异？几百个文件你对比到什么时候？

更要命的是，代码文件之间有依赖关系——A 文件调用了 B 文件的函数，B 文件又读取了 C 文件的配置。你不能只回滚其中一个文件，必须把所有相关文件一起回到同一个时间点的状态，否则程序就跑不起来。

小明的项目文件夹里曾经出现过 `project-backup`、`project-v2`、`project-能跑的版本`。他改了一天代码，发现方向错了，想回到昨天的状态——但昨天的代码已经被今天的覆盖了。

更惨的是，他不确定"昨天的状态"到底是什么样——因为他在改的过程中没有记录每一步的变化，只记得"昨天还能跑，今天跑不了了"。他试着从记忆中还原昨天的代码，改了两个小时，结果引入了新的 bug。如果他当时有一种工具，能像游戏存档一样记录每一步的状态，他只需要"读档"就能回到昨天——而不是凭记忆手动还原。

Git 解决的就是这个问题：**用一种可靠的方式记录每一次修改，让你随时能回到任何历史版本。**

不用文件名标记版本，不用手动复制备份，不用肉眼对比差异。Git 帮你记住所有这些事。它记录的不是"某个文件的某个版本"，而是"整个项目在某个时间点的完整快照"——所有文件的状态被一起保存，一起回滚，不会出现"A 文件回到了昨天但 B 文件还是今天"的错位。

---

## 你可能已经在用 Git 了

这里有个有趣的事实：如果你一直跟着本书用 Claude Code 开发项目，你的项目目录里很可能已经有一个 `.git` 文件夹了。

Claude Code 在创建新项目时，通常会自动执行 `git init` 初始化仓库，并在开发过程中帮你提交代码。换句话说，你一直在用 Git，只是没意识到。

打开终端，进入你的项目目录，运行 `git log --oneline -5`。如果看到一串提交记录，那就对了——Claude Code 一直在帮你"存档"。这个发现本身就是一个觉醒时刻：**你一直在用的工具，现在该理解它了。**

这就像你一直在用手机的自动备份功能，照片默认同步到了云端，但你从来没打开过云相册看一眼。某天手机摔坏了，你才发现——原来照片都在云端，一张没丢。Git 对你的代码做的就是类似的事，只不过它记录的不只是"最新版本"，而是每一次修改的完整历史。

如果你的项目还没有 Git 仓库也没关系，告诉 Claude Code 帮你初始化一个就行。Windows 用户注意：`.git` 是隐藏文件夹，如果在文件管理器里看不到，直接运行 `git status`，有输出就说明仓库存在。

---

## commit：给代码存档

Git 的核心操作是 **commit（提交）**。每次 commit 就像游戏里的手动存档——它记录了当前所有文件的状态，附带一条描述和时间戳。存档就是 commit，读档就是回滚，存档列表就是 git log。

和游戏存档不同的是，Git 的存档是**增量**的——它只记录变化的部分，不会复制整个项目。一个项目可能有几百次提交，但 `.git` 文件夹通常只有几十 MB，远比你复制几百份项目文件夹小得多。所以你可以放心地频繁提交，不用担心占用太多空间。

这也是 Git 比"复制文件夹"高明的地方——它用极小的存储代价，换来了完整的历史记录。

小明让 Claude Code 帮他提交代码。他不需要记住 `git add` 和 `git commit` 的语法——他只需要在完成一个功能或修复一个 bug 后，告诉 AI "提交一下"。Claude Code 会自动暂存修改、生成提交信息、完成提交。整个过程对小明来说就是一句话的事，但背后 Git 帮他保存了一个完整的项目快照，以后随时可以回来。

什么时候该提交？没有硬性规定，但有个简单的判断标准：**每当你完成了一件"可以用一句话描述"的事情，就值得提交一次。**

比如"添加了评分组件""修复了搜索为空的 bug""调整了首页布局"。不要攒一整天的修改一次性提交——那样回滚的时候粒度太粗，等于存档只存了"第一关"和"最终关"，中间全丢了。想象一下你打一个很难的游戏关卡，中间一次都没存档，死了就得从头来——你一定会骂自己为什么不多存几次。代码也是一样的道理。

---

## 三区模型：你的修改在哪里

Git 管理代码有三个区域，理解它们能帮你回答一个关键问题：**"我的修改现在在哪里？"**

<GitFlowDiagram />

打个比方：**工作区**是你的书桌——文件摊开在上面，你正在改。**暂存区**是你整理好的一摞纸——"这些是我要交的"。**本地仓库**是档案柜——交上去的东西永久保存。你在书桌上涂涂改改（工作区），觉得满意了就把改好的纸整理成一摞放到旁边（暂存区），最后把这摞纸归档到柜子里（本地仓库）。这三步对应的就是"编辑文件 → git add → git commit"。

日常开发中，Claude Code 会帮你处理这些步骤，你不需要手动执行 `git add` 和 `git commit`。但当你遇到"我刚才的修改去哪了"或"为什么提交里没有这个文件"的困惑时，三区模型能帮你定位问题。

文件改了但 `git status` 显示"未暂存"，说明修改还在工作区，没有 `add`。`add` 了但没 `commit`，说明修改在暂存区，还没存档。`commit` 了但没 `push`，说明存档在本地，还没同步到云端——这个下一节会讲。

你不需要死记这些命令。重要的是建立一个心智模型：修改从工作区出发，经过暂存区，最终进入本地仓库。每一步都是可控的，你随时可以问 Claude Code "我的修改现在在哪个阶段"。

就像你出门前下意识看一眼手机电量——不是每次都需要充电，但看一眼心里有底。`git status` 就是这一眼：它告诉你哪些文件被修改了但还没暂存，哪些已暂存但还没提交，当前在哪个分支上。Claude Code 在执行 Git 操作前通常会自动检查状态，但当你感到困惑时，知道有这个工具能帮你定位问题。

<ThreeZoneModel />

---

## 回滚：无限的 Ctrl+Z

你用 Ctrl+Z 撤销操作时，有个隐含的限制：**关掉文件再打开，Ctrl+Z 就失效了。** 它只能撤销当前编辑会话的操作。你在 VS Code 里改了一个文件，保存了，关掉了，第二天打开——Ctrl+Z 回不去了。更别说你想回到三天前、一周前的状态。Git 没有这个限制。只要你提交过，就能回到那个时间点的状态——哪怕是一个月前的版本。它是一个不会过期的、无限层数的 Ctrl+Z。

三个常见的回滚场景，对应三种不同的处理方式。

**改了半天，发现方向错了，还没提交。** 这是最简单的情况。告诉 Claude Code 你想撤销哪些文件的修改，它会帮你恢复到上次提交的状态。工作区的修改会被丢弃，暂存区和本地仓库不受影响。就像你在书桌上涂涂改改了半天，最后把草稿纸揉了扔掉，档案柜里的存档纹丝不动。这个操作没有任何风险，因为你丢弃的只是还没保存的草稿。

**已经提交了，想回到上一个版本。** 告诉 Claude Code "回到上一个能跑的版本"。它会用 `git reset` 把本地仓库的指针移回去。这会改写提交历史——那次"错误的提交"会从历史中消失。如果代码还没推送到远程，这是最干净的做法，就像你从档案柜里抽出最新的那份存档撕掉，假装它从没存在过。但要注意，被撕掉的存档真的就没了——如果你后来又想要那次提交的内容，就找不回来了。所以在 reset 之前，确认你真的不需要那些代码。

**已经推送到远程了。** 这时候不能简单地"删掉"那次提交，因为朋友可能已经拉取了你的代码。如果你删了，朋友那边的历史和你的对不上，会产生混乱。正确的做法是用 `git revert`——它不删除历史，而是创建一个新的提交来"反向操作"，把那次修改的效果撤销掉。历史记录里能看到"第 10 次提交做了 X，第 11 次提交撤销了 X"，清清楚楚。这就像你在账本上写错了一笔，不是用涂改液抹掉，而是在下一行写一笔"冲正"——原始记录还在，但最终结果是对的。

小明就遇到过这种情况。他花了一下午给推荐功能加了个复杂的排序算法，提交并推送了。第二天发现排序逻辑有根本性问题，整个方向都错了。他告诉 Claude Code "回到加排序算法之前的状态"，Claude Code 用 `revert` 创建了一个新提交，干净利落地撤销了那次修改，朋友那边 pull 下来也不会受影响。

Git 回滚的核心认知是：**意识到你可以回去。** 很多人在代码改坏之后的第一反应是硬着头皮继续修，越修越乱。如果你知道 Git 随时能带你回到任何历史版本，你就敢大胆尝试——改坏了就回滚，没什么大不了的。

---

## 查看历史：AI 到底改了什么

用 Claude Code 开发时，AI 会频繁修改文件。有时候你想知道"它刚才到底改了什么"或者"昨天的代码长什么样"。Git 的提交历史就是你的答案。每条提交记录包含谁在什么时候做了修改、修改了哪些文件、每个文件具体改了哪几行。

你不需要记住 `git log`、`git diff`、`git show` 这些命令的区别。告诉 Claude Code 你想看什么——"最近改了什么""上次提交的内容""这个文件的修改历史"——它会选择合适的命令并把结果解释给你。

这在 AI 编程中尤其有用。有时候 Claude Code 一次性改了好几个文件，你想确认它改的对不对，看一眼 diff 比重新读整个文件快得多。Word 里的"修订模式"你大概用过——删掉的字划红线，新加的字标蓝色。diff 就是代码世界的修订模式，用绿色标出新增的行、红色标出删除的行，一目了然。养成"改完看 diff"的习惯，能帮你更好地理解 AI 做了什么，也能及时发现不符合预期的修改。

查看历史还有一个不那么明显但很重要的用途：**理解代码的演变过程**。当你看到一段代码不理解它为什么要这么写时，查看这个文件的提交历史，往往能找到答案。也许三次提交之前这段代码更简单，后来因为修了一个 bug 才变成现在这样。提交信息里可能写着"fix: 处理用户未登录时的空指针异常"——你立刻就明白了这段看起来多余的判断是在防什么。代码本身只告诉你"是什么"，提交历史告诉你"为什么"。

---

## 提交信息：给未来的自己留线索

你给文件夹起名的时候，会写"2024-税务-发票"而不是"新建文件夹(3)"。提交信息也是同一个道理——它是给未来的你（和你的协作者）看的。

好的提交信息有一个简单的格式：类型前缀加上简短描述。`feat: 添加电影评分功能` 表示新功能，`fix: 修复搜索结果为空的问题` 表示修 bug，`docs: 更新 API 接口文档` 表示改文档，`refactor: 重构用户认证模块` 表示重构。其他常用的还有 `style`（样式调整）和 `chore`（杂务，比如升级依赖）。

这不是死规矩，而是一种让提交历史**可扫描**的约定。当你回看几十条提交记录时，前缀能帮你快速定位"那次加评分功能的提交在哪"。没有前缀的提交历史就像一堆没有标签的文件夹——每个都要打开看一眼才知道里面是什么。想象一下你的邮箱收件箱，如果每封邮件的主题都是"更新"或者"修改"，你找一封特定的邮件得翻到什么时候？但如果主题写的是"[报销] 3月差旅费用""[会议] 周五产品评审"，你一眼就能找到。

Claude Code 在帮你提交时通常会自动生成规范的提交信息。它会分析你改了哪些文件、改了什么内容，然后生成一条准确的描述。大部分时候它生成的信息已经足够好了。如果你觉得描述不够准确——比如它写了"update components"但你觉得应该更具体——可以告诉它调整。好的提交信息不需要很长，一句话说清楚"做了什么"就够了。

---

::: tip 本节核心要点
Git 解决的核心问题是**版本管理**——不再用文件名标记版本，而是用 commit 记录每一次有意义的修改。你可以随时查看历史、对比差异、回到任何版本。如果你的项目已经有 `.git` 目录，说明你已经在用了——现在你理解了它在做什么。
:::

---

**下一节**：代码只在你的电脑上，朋友拿不到，硬盘坏了就全没了。[11.2 推上云端，开始协作](./02-remote-and-collaboration.md) 解决这个问题。
