⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

11.1 为什么需要 Git

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


"最终版"困境

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

写论文的时候,桌面上出现了这些文件:

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

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

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

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

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

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

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

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

更惨的是,他不确定"昨天的状态"到底是什么样——因为他在改的过程中没有记录每一步的变化,只记得"昨天还能跑,今天跑不了了"。他试着从记忆中还原昨天的代码,改了两个小时,结果引入了新的 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 addgit 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 addgit commit。但当你遇到"我刚才的修改去哪了"或"为什么提交里没有这个文件"的困惑时,三区模型能帮你定位问题。

文件改了但 git status 显示"未暂存",说明修改还在工作区,没有 addadd 了但没 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 loggit diffgit 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 推上云端,开始协作 解决这个问题。