用AI写代码这件事,我入坑不算早,但踩的坑一个没少。最让我印象深刻的不是AI写不出代码,而是它“太能改了”:你说一句“帮我优化一下登录逻辑”,它可能顺手把你的路由配置、鉴权中间件、甚至数据库连接串都改了一遍。等你发现的时候,已经说不清到底哪些改动是想要的,哪些是AI“自作主张”加戏的。经历过几次这种“拆盲盒”式的尴尬之后,我给自己定了一条规矩:凡是让AI动手改代码之前,必须先把当前状态提交到git。这个习惯救了我很多次,今天就把这套思路和背后的操作细节完整梳理一遍。
1. AI改代码的“波及面”,远比你想的更不可控
1.1 AI不像人,它会跨越多个文件做隐形改动
人工改代码的时候,我们通常会下意识地控制改动范围:这个需求只动service层,那个Bug只改某一行逻辑。但AI没有这个“分寸感”。你给Cursor或其它AI编程工具下达一个看起来很小的指令,它可能会顺藤摸瓜改掉好几个相关文件,甚至新建辅助文件。
有一次我让AI帮我“给接口加个超时重试”,结果它不但改了接口调用处的代码,还顺手把整个HTTP客户端的全局配置推翻了,甚至连单元测试文件里的mock逻辑都被重写。最麻烦的是,它不会主动告诉你改了这么多。等你运行测试发现一片飘红时,才意识到问题的严重性。
这种跨文件、隐式的改动,是AI编程场景下最大的不确定性来源。如果你手头没有一份“改动前的快照”,你根本没法快速回到安全状态。而git commit就是成本最低、最可靠的快照手段。
1.2 “先提交再改代码”本质上是在给AI操作加回滚点
我们做数据库事务的时候讲究原子性,要么全成功,要么全回滚。AI改代码也应该有类似的思想:在动手之前建立一个明确的基线,操作完如果结果不满意,就整个退回基线重新来。
所谓的“先提交再改代码”,就是把这个基线固化到git历史里。操作顺序很简单:
- 把所有未提交的改动整理好,做一次提交。
- 确保工作区是干净的(
git status看不到未提交的变更)。 - 然后再向AI下达修改指令。
- 如果AI改出来的结果不满意,直接用
git checkout或git reset把代码恢复到提交时点的状态。
很多人觉得这多此一举,“我自己改代码也没这么讲究”。但人改代码是一步步来的,思路连贯,改错了最多Ctrl+Z撤销几步。AI改代码是一个“黑盒过程”,它在几秒内可能完成几十处替换,你无法靠IDE的撤销功能回到AI动手之前。唯一可靠的方式就是git这个“巨型撤销键”。
1.3 我自己遇到过的“AI改崩现场”
有一次,我在一个分支上调试一个比较复杂的状态机逻辑,调了半天没调通,于是把当前半成品提交了一下,然后让AI帮我重构状态机的状态流转部分。AI给了一版看起来很合理的重构方案,我也没细看就应用了。结果编译一跑,不仅状态机逻辑没对,连带着把事件分发器的初始化顺序都打乱了。
当时我下意识点IDE的撤销,发现撤销记录已经被AI的大批量文本替换冲掉了。幸好我在让AI动手之前,做了一次git commit。我直接执行了git reset --hard HEAD,工作区的代码瞬间回到了AI介入之前的状态。那种感觉,真的就是捡回一条命。
如果没有那个提交点,我可能得花几个小时去反向甄别哪些代码是AI改的、哪些是我自己原本的调试逻辑。所以说,“先提交”不是形式主义,它是一种真正能把不确定性转化成确定性的工程习惯。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. “先提交再改”操作链:完整的落地流程
2.1 提交前先看一眼当前工作区
实际操作中,很多人让AI帮忙改代码时,工作区里往往还残留着一堆自己改到一半的东西。这时候如果直接让AI介入,新旧代码混在一起,AI的上下文会变得极其混乱:它分不清哪些是你的意图,哪些是待完善的残局。它给出的改动建议很可能是基于错误前提的。
我给自己定了一个检查项:每次请AI改代码前,先看一眼git status。如果工作区有未提交的改动,我会先做一个WIP提交。哪怕这个WIP提交的代码是不完整的、甚至编译不过的,也没关系,关键是给AI一个明确的起点。
这个WIP提交的粒度可以很粗,比如“WIP: 调试状态机逻辑”,关键是让它成为一个独立的git节点。这样做还有一个好处:如果AI改砸了,你git reset --hard回到WIP点,还可以接着改自己原来的半成品。
2.2 向AI下达任务前,把需求拆成“可验证的小步”
有了干净基线之后,下一步就是控制AI的修改幅度。经验是,别让AI一口气做一个很大的任务。比如“帮我完善整个用户模块”,这种指令等于把方向盘完全交给AI。更安全的做法是拆成多个小步骤:
- 第一步:帮我在用户模块中增加一个邮箱格式校验函数。
- 第二步:把注册接口里的邮箱校验逻辑替换成上面这个新函数。
- 第三步:补充对应的单元测试。
每完成一步,我都会跑一下测试,检查git diff,看改动是否在预期范围内。如果某一步的改动不满意,我就直接回滚到该步骤之前的提交点,重新给AI更明确的提示词。
这里要特别注意:所谓“小步”不是指代码量小,而是指改动目标清晰、边界明确。哪怕一个改动涉及10个文件,只要它是内聚的,就算合理;反过来,一个看似简单的一句话需求,如果可能牵扯到全局配置、第三方依赖变更,那就应该拆出来单独评估。
2.3 改完代码之后,先看diff再提交
很多人的习惯是让AI改完代码后,直接跑一遍程序,功能看起来正常就直接提交。这个习惯在AI编程时代恰恰是最危险的。AI的修改可能引入隐藏的副作用:比如它删除了一个看似无关的import,导致某个工具函数在另一处变成了undefined;又比如它“好心”帮你在测试配置里加了一个本地写死的Token,你浑然不知就提交上去了。
所以我的流程是:AI改完 -> 逐文件查看git diff -> 理解每处改动 -> 觉得没问题后才提交。这个过程不能省,而且查看diff时的关注点不是语法正不正确,而是“这处改动是不是我要求的”、“有没有额外改动”。
如果是用IDE集成的git工具,直接点击Diff视图逐行看效率最高。如果改动涉及很多文件,可以先看文件列表,优先排查那些你没预料到会变动的文件。我通常会问自己一个问题:“AI为什么要动这个文件?”如果答不上来,就要警惕,要么回滚该文件,要么向AI追问原因。
2.4 把AI生成代码产生的新产物纳入版本管理
AI编程过程中还有一个容易被忽略的坑:AI可能会生成一些新的文件,比如配置文件、工具脚本、甚至是临时数据文件。你要么明确告诉AI不要新建文件,要么在AI完成任务后仔细检查新增文件的清单。
如果AI新建了无用的辅助文件,要果断删除;如果它新建的是项目运行必需的文件,那就要走正常的代码评审和提交流程。千万别出现“AI改完代码后,新增了一个配置文件,你却没注意,几天后被推送到远端才发现那个文件里写死了本地路径”这种事。这种经历我用过一次就记住了,代价是同事帮忙排查了一下午。
3. 改出一堆问题之后,回滚的几种实战处置
3.1 AI改动还没提交想后悔,用checkout快速回到基线
最理想的止损时机,是AI改完你还没提交的时候。发现改动不满意,直接丢弃工作区的所有改动,回到上一次提交的干净状态:
bash复制git checkout -- .
# 或者用
git restore .
如果你只想丢弃某个特定文件的改动,不想把全部改动都冲掉:
bash复制git restore src/views/Login.vue
这个操作非常符合“先提交再改代码”的逻辑:因为有提交基线在,你可以毫无心理负担地让AI尝试各种方案,不合心意就直接扔掉,重来一遍成本极低。没有基线的话,你只能在AI改坏的代码里抓耳挠腮地手动修补。
不过要特别提醒,git restore会直接丢弃未提交的改动,执行前确认一下你确实不想要这些改动了。如果你只是想暂时把AI的改动放到一边,但又不想丢失,更稳妥的方式是先git stash暂存起来,而不是直接丢弃。
3.2 AI改动已经提交但还没推送远端,用reset做软回退
另一种常见情况是:AI改完的代码你觉得逻辑上没问题,就给提交了,然后越看越不对,或者测试跑挂了。这种时候,如果这个提交还没有推到远端,最简单的方式是把HEAD指回上一次提交。
如果你的核心诉求是“撤销提交,但保留AI改动的这些代码”,方便你再看看或继续修改,用--soft模式:
bash复制git reset --soft HEAD~1
执行完之后,git会撤销最近一次提交,但会把这次提交涉及的所有文件改动保留在工作区,状态是“已暂存”。你可以重新审查、重新修改、然后再次提交。这样既保留了AI辛苦写的代码,又给了你重新决策的机会。
如果你不仅想撤销提交,还想把工作区里的改动整个丢掉,彻底回到上次提交的干净状态,那就用--hard:
bash复制git reset --hard HEAD~1
3.3 提交信息写错了想改,用amend或者rebase
还有一种情况,AI代码本身没问题,但这个提交的名字起得不合适。我用AI写的提交信息经常遇到这种情况,它生成的信息虽然规范,但总感觉没覆盖掉我想表达的重点。改最近一次提交的信息:
bash复制git commit --amend -m "feat: 优化登录接口的鉴权逻辑"
也可以用IDE的Git工具直接右键Commit -> Amend Commit,在界面里改。
如果是要改的不是最后一次提交,而是再往前几条,就要用到交互式rebase了。但这里我要泼一盆冷水:如果提交已经推送到了远端,且不止你在用这个分支,尽量不要用rebase去改写历史,因为rebase会改变commit的哈希,一旦别的同事拉了这个分支,会带来一堆冲突和混乱。规则很简单:没推远端,随便改;推了远端且是共享分支,就别硬改历史了,追加一个修复提交更符合协作习惯。
3.4 AI改动已经提交且推到了远端,用revert做安全回滚
AI改的代码已经推到远端了,或者你并不想改写历史,只想把上次AI提交造成的效果“逆转”过来,那git revert是最安全的选择。它会生成一个新的提交,把上一个提交的改动反向覆盖掉,历史不会被破坏:
bash复制git revert HEAD
多个提交需要回滚的话,就连续执行git revert,指定对应commit的哈希值。revert最大的优点是可追溯,谁什么时候撤销了哪个提交,看git log一目了然。
3.5 不小心把AI生成的调试文件提交进去了,怎么把它移出版本管理
用AI写代码,有时候会产生不少临时文件,比如调试用的日志目录、本地的环境变量文件、AI生成的过程稿等。如果手一抖把这些提交了,直接删除然后提交一个修复也不算迟,但如果是包含密钥、Token、数据库连接串这类敏感信息的文件,光删除是不够的。
这类文件如果已经进入了提交历史,哪怕你后面删掉它再提交新版本,它依然存在于历史记录中,别人只要翻git log就能看到。理想状态下,应该用filter-repo这类工具把所有历史中的敏感文件彻底抹除。如果分支是自己独自维护的,也可以在有敏感文件提交之前的分支点做一次reset --hard,然后重新提交,让敏感信息不进历史。
但对大多数场景来说,最省心的办法其实是防患于未然:把.env、*.pem、config/private.*这类敏感文件名提前写进.gitignore,并且让AI改代码时明确“禁止改配置文件、禁止造调试文件”。
4. 敏感信息防泄露:AI辅助编程里那条最容易被突破的防线
4.1 AI的上下文窗口会把敏感信息“吸进去”
AI编程工具要想对项目做出更准确的修改,往往会依赖上下文,比如索引项目文件、读取当前打开的文件。这就带来一个隐患:如果项目里有未提交的密钥文件、本地数据库密码、生产环境地址等信息,它们很可能被AI读取,并可能在生成代码时“顺手”写进某个新文件里。
我身边真实发生过类似的案例,有人让AI帮忙生成部署脚本,AI参考了他项目里的一个配置文件,把里面的服务器地址和登录凭据直接编到了脚本中。万幸的是这人在提交前多看了一眼diff,发现了问题,否则这些信息一旦进入git托管仓库,哪怕只是一个私有仓库,风险也非常大。
4.2 在提交前做一次敏感信息“雷达扫描”
我的习惯是,不只是启用diff审查,还会在准备提交时用命令或者工具扫一遍即将提交的内容,重点看这几类模式:
- 形如
sk-...、AKIA...、ghp_...的密钥前缀; - 硬编码的数据库连接串(
jdbc:mysql://、postgres://等); - IP地址、内网域名、邮箱地址批量出现;
- 版本管理中出现新增的.env、.pem、.key等文件类型。
有人可能觉得,这些凭据不提交到远端不就行了?可问题是,私有仓库也可能被分享,也可能在未来改权限配置时意外变成公开仓库。更重要的是,Key一旦被快照进git,就应当视为已泄露,即使删除历史,也可能已经被爬虫或第三方工具抓取过了。
4.3 把AI提示词也当成敏感信息管理
除了代码里的密钥,还有一个容易忽略的点:AI提示词本身就是一种敏感信息。很多项目里,团队会沉淀一些针对特定业务场景的提示词模板,里面很可能包含业务逻辑细节、内部系统名称、甚至数据结构的描述。如果这些提示词被提交到公开仓库或复制到公开平台,等于变相泄露了业务设计。
所以我在团队里会强调:凡是被AI使用过的核心提示词、包含内部业务信息的上下文片段,尽量不提交到公开仓库。如果一定要共享,就写成脱敏版,把真实业务名词替换成占位符。
4.4 用pre-commit钩子给git提交加一道自动闸门
如果团队比较容易在敏感信息上翻车,建议直接从机制上堵住。在git仓库里配置一个pre-commit钩子,在每次提交前自动扫描暂存区的文件内容,一旦发现疑似密钥内容就中断提交。
用husky或者lint-staged也行,只要能在提交动作发生前拦截即可。脚本并不复杂,核心逻辑就是读取git diff --cached输出的内容,用正则匹配常见的密钥格式。这个机制加好之后,AI生成的代码同样会被检查,不会再出现“AI把密钥带进来,人没注意到”的尴尬局面。
我自己踩过一次坑之后,把所有常见密钥规则都写进了钩子。现在哪怕AI生成的代码里藏了个AWS Key,提交时也会被自动拦截。那种“机器守住机器”的感觉,比单纯靠人眼去翻diff踏实多了。
5. AI编程项目里,需要重新设计的git工作流细节
5.1 给AI任务单独开一条分支,别在主分支上直接“试验AI”
如果AI只是帮你补几行代码、修个小型Bug,在主分支上做完提交影响不大。但只要AI参与的是一次涉及多文件的改动,比如模块重构、依赖升级或者样式体系调整,我就会单独拉一条分支出来,专门供AI折腾。
这样做的逻辑很简单:主分支永远是稳定可靠的,AI产生的任何不稳定改动都被隔离在功能分支里。分支里可以随便提交、随便重置、随便做试验,甚至整个分支推翻重来都不影响主线。等AI生成的代码经过完整测试和review后,再把它合并回主分支。
这个习惯尤其适合团队协作,因为AI生成的代码质量不稳定,如果直接提交到共享主分支,等于把所有人的脑袋都别在AI的裤腰带上。
5.2 用“会话提交”思路管理AI相关的多次提交
现在的AI编程工具普遍带“会话”功能,一次对话里可能会进行多轮任务。但git并不理解什么是“会话”,它只认提交。如果你一次会话里AI帮你完成了多个不相关的小改动,而且你只提交了一次,未来想定位某一个功能改动时,会变得很困难。
我会在每次AI会话开始时记一下这个会话要解决的问题,然后在会话过程中,每当AI完成一个逻辑独立的改动点,就立刻做一次提交,提交信息里写清楚“AI session: 修复xx接口的超时重试”。这样做的好处是,之后的git log看起来就像一份“AI会话操作清单”,哪次会话做了什么、涉及哪些改动,一目了然。
5.3 AI引发的“提交混乱”:如何恢复到一个可理解的提交序列
AI生成代码过程中还有个常见问题:提交次数异常多,且信息质量参差不齐。有时候AI很积极地帮你把改动拆成好几个提交,但每个提交信息都是“Update file”之类的废话,根本看不出门道。
遇到这种情况,如果只是在你的功能分支上,我会在合并到主分支前用squash把这些乱糟糟的提交压缩成一个语义清晰的提交。这样既保留了完整改动内容,又让主分支的历史干净可读:
bash复制git merge --squash feature/ai-auth-refactor
git commit -m "feat: AI辅助重构认证模块"
这一步的价值在于:提交历史是给人读的。AI可以帮你写代码,但帮你维护一个清晰、可信、可回溯的项目历史,仍然需要人来做最终把关。
5.4 branch protection和code review依然是AI代码的必经关卡
用AI编程越久,我越觉得,它更像是增加了一个效率极高的“编码同事”,而不是一个可以完全信任的自动提交器。即便是最顺滑的AI改动,我也会安排一次人工review。如果是团队项目,还会给关键分支开branch protection规则,要求合并前必须有至少一个review、必须通过CI检查。
这套约束看起来降低了效率,但它恰恰是把“AI编程安全”从个人自觉上升到制度保障的关键一步。没有这些关卡,AI引入的问题会在合并之后大面积暴露,届时再想定位是哪次AI会话引入的,代价会成倍增加。
说到底,“先提交再改代码”只是AI编程安全体系里最基础的一环。它解决的是“改坏了怎么快速退回去”的问题,而敏感性扫描、分支隔离、code review这些环节,解决的是“不让问题进库”的问题。两套机制叠在一起,AI才能真正变成那个让你如虎添翼的助手,而不是时不时制造惊喜的“熊队友”。
如果你还没养成这个习惯,我强烈建议从今天开始,每次打开AI编程工具准备进行较大幅度改动前,第一时间做一个提交。相信我,等哪天真靠一个commit救回你几个小时的排查时间时,你就会明白,这个操作比任何花哨的提示词技巧都值钱。
