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 管理代码有三个区域,理解它们能帮你回答一个关键问题:"我的修改现在在哪里?"
┌─────────────┐ git add ┌─────────────┐ git commit ┌─────────────┐
│ 工作区 │ ──────────→ │ 暂存区 │ ──────────→ │ 本地仓库 │
│ Working Dir │ │ Staging Area │ │ Local Repo │
└─────────────┘ └─────────────┘ └─────────────┘
你正在编辑的文件 准备提交的修改 已保存的版本历史打个比方:工作区是你的书桌——文件摊开在上面,你正在改。暂存区是你整理好的一摞纸——"这些是我要交的"。本地仓库是档案柜——交上去的东西永久保存。你在书桌上涂涂改改(工作区),觉得满意了就把改好的纸整理成一摞放到旁边(暂存区),最后把这摞纸归档到柜子里(本地仓库)。这三步对应的就是"编辑文件 → git add → git commit"。
日常开发中,Claude Code 会帮你处理这些步骤,你不需要手动执行 git add 和 git commit。但当你遇到"我刚才的修改去哪了"或"为什么提交里没有这个文件"的困惑时,三区模型能帮你定位问题。
文件改了但 git status 显示"未暂存",说明修改还在工作区,没有 add。add 了但没 commit,说明修改在暂存区,还没存档。commit 了但没 push,说明存档在本地,还没同步到云端——这个下一节会讲。
你不需要死记这些命令。重要的是建立一个心智模型:修改从工作区出发,经过暂存区,最终进入本地仓库。每一步都是可控的,你随时可以问 Claude Code "我的修改现在在哪个阶段"。
就像你出门前下意识看一眼手机电量——不是每次都需要充电,但看一眼心里有底。git status 就是这一眼:它告诉你哪些文件被修改了但还没暂存,哪些已暂存但还没提交,当前在哪个分支上。Claude Code 在执行 Git 操作前通常会自动检查状态,但当你感到困惑时,知道有这个工具能帮你定位问题。
回滚:无限的 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"但你觉得应该更具体——可以告诉它调整。好的提交信息不需要很长,一句话说清楚"做了什么"就够了。
本节核心要点
Git 解决的核心问题是版本管理——不再用文件名标记版本,而是用 commit 记录每一次有意义的修改。你可以随时查看历史、对比差异、回到任何版本。如果你的项目已经有 .git 目录,说明你已经在用了——现在你理解了它在做什么。
下一节:代码只在你的电脑上,朋友拿不到,硬盘坏了就全没了。11.2 推上云端,开始协作 解决这个问题。
