Git 必知必会:开发者实战手册
古董级程序员,大厂出来后一直在创业公司,现在仍活跃在一线做 AI 相关开发。更完整的更新写在微信公众号「字与码」:工作经历、技术实践、工具使用和这些年走弯路的记录,会不定期发在那里。若这篇文章对你有用,欢迎顺手关注。
Git 不只是“提交代码”的工具。它更像开发过程的黑匣子:你改过什么、为什么改、谁看过、怎么上线、出了问题怎么退回来,最后都会落到 Git 里。
这篇不是 Git 教材,而是一份能放在手边的参考手册。目标很简单:你看完后,知道日常开发该用哪些命令,团队协作还要补哪些动作,遇到 AI 生成代码时怎么让 Git 保护你。
Git 到底解决什么问题
从版本备份到分布式协作
早期写代码,最朴素的版本管理是复制目录:project-final、project-final2、project-final-really-final。它能保留一点历史,但不能回答三个关键问题:
- 这一行是谁改的?
- 为什么这样改?
- 两个人同时改同一个文件,怎么合起来?
集中式版本控制把代码放在服务器上,开发者从服务器检出、提交。它解决了多人协作,但服务器是中心。网络断了,很多操作就受限;中心挂了,团队就停。
Git 的核心变化是分布式。每个开发者本地都有完整历史,不只是当前文件。你可以离线提交、查看历史、切分分支、回滚实验,等网络恢复再同步到远端。
Git 诞生于 2005 年,由 Linus Torvalds 为 Linux 内核开发创建。当时 Linux 内核社区需要一个速度快、能支撑大量分支和补丁协作、同时不依赖单一中心的版本控制系统。这个背景决定了 Git 的性格:快、强、偏底层,也有一点不迁就新手。
Git 的优势
Git 的优势可以压成四句话:
- 本地完整历史:大多数查询、分支、提交都不需要联网。
- 分支很轻:开分支几乎没有心理负担,适合实验。
- 内容可追溯:提交、作者、时间、差异、关联 issue 都能留下证据。
- 协作可自动化:PR、CI、部署、代码审查、Release 都能围绕 Git 事件运行。
它也有代价:概念多,命令多,错误操作看起来吓人。解决办法不是背完所有命令,而是建立正确心智模型。

Git 心智模型:四个区域
先记住四个区域,Git 的大部分命令都是在它们之间移动内容。
| 区域 | 含义 | 典型动作 |
|---|---|---|
| 工作区 | 你正在编辑的文件 | 修改、删除、创建文件 |
| 暂存区 | 下一次提交要包含的内容 | git add |
| 本地仓库 | 已经形成历史的提交 | git commit、git log |
| 远端仓库 | 团队共享的仓库 | git push、git pull |
工作区:你正在编辑的地方
工作区就是项目目录里当前能看到的文件。你新建、修改、删除文件,最先都发生在这里。
判断工作区状态,主要看两件事:
git status:哪些文件被修改、删除或新增。git diff:这些修改具体是什么。
工作区的内容还不是历史,也不会自动进入下一次提交。它只是你的当前现场。
暂存区:下一次提交的清单
暂存区可以理解为“本次提交购物车”。你通过 git add 把工作区里的一部分改动放进去,下一次 git commit 只会提交暂存区里的内容。
它的价值是让你拆分提交:
- 一个文件里有两类改动,可以用
git add -p只提交其中一类。 - 一次开发里既改逻辑又改文档,可以拆成两个提交。
- AI 一次改了很多文件,可以先挑最确定的一部分入库。
有一个容易让新人迷惑的点:Git 也允许你不显式经过 git add,直接提交某个已经被 Git 跟踪的文件。
git commit src/auth/login.py -m "fix(auth): handle expired token"
这条命令的意思是:把 src/auth/login.py 当前工作区里的改动直接放进本次提交。它确实“绕过了你手动 add 的步骤”,但不是绕过了 Git 的提交模型,Git 内部仍然会为这次提交准备一份快照。
这个写法适合非常确定只提交一个已跟踪文件的场景。它有两个限制:
- 对新文件无效。新文件还没被 Git 跟踪,仍然要先
git add new_file.py。 - 容易漏看 diff。新人最好先用
git add、git diff --staged建立习惯,再考虑这种快捷写法。
本地仓库:已经形成历史的提交
本地仓库存放你机器上的完整提交历史。git commit 之后,改动才真正变成一条可追溯记录。
本地仓库能做很多离线操作:
git log看历史。git switch切分支。git revert或git restore恢复问题。git rebase -i整理尚未共享的提交。
只要还没 push,本地提交通常仍是你的个人工作区,可以适度整理。
远端仓库:团队共享的历史
远端仓库是团队协作的公共位置,例如 GitHub、GitLab、Gitea 上的仓库。git push 后别人才能看到你的提交,git fetch 或 git pull 后你才能看到别人的提交。
一旦进入远端,历史就不再只属于你。公共分支上要更谨慎:
- 不随意强推。
- 不改写别人依赖的提交。
- 通过 PR、CI、review 让改动可检查。
很多 Git 问题都来自没分清这四层。比如文件还在工作区,就不能指望别人看到;文件只 add 了,还没 commit,就还不是历史;本地 commit 了但没 push,别人也拿不到。
基本命令:够你独立完成开发
这组命令不花哨,但够你从拉代码、建分支、改文件、提交、同步到恢复误改,独立完成绝大多数开发工作。

初始化和获取代码
| 场景 | 命令 | 示例 |
|---|---|---|
| 第一次拿到远端项目 | git clone <url> | git clone git@github.com:example/shop-api.git |
| 把本地目录变成仓库 | git init | mkdir notes && cd notes && git init |
| 查看远端地址 | git remote -v | git remote -v |
日常开发里,clone 用得更多。init 通常用于新项目。
看状态和差异
| 场景 | 命令 | 示例 |
|---|---|---|
| 看哪些文件改了、哪些已暂存 | git status | git status |
| 看工作区还没暂存的差异 | git diff | git diff src/auth/login.py |
| 看已经暂存、准备提交的差异 | git diff --staged | git diff --staged |
| 用一行图形看提交历史 | git log --oneline --graph --decorate | git log --oneline --graph --decorate -10 |
好习惯是:提交前一定看 status 和 diff。不要把“我以为改了这些文件”当成事实。
提交一组完整改动
| 场景 | 命令 | 示例 |
|---|---|---|
| 把文件放进暂存区 | git add <file> | git add src/auth/login.py tests/test_login.py |
| 按小块选择要提交的内容 | git add -p | git add -p src/auth/login.py |
| 形成一次提交 | git commit -m "type(scope): message" | git commit -m "fix(auth): handle expired token" |
| 直接提交一个已跟踪文件 | git commit <file> -m "message" | git commit src/auth/login.py -m "fix(auth): validate session" |
一次提交最好只做一件事。修 bug、改样式、重构、更新文档,能分就分。以后回滚、review、定位问题都会轻很多。
提交信息也要像人话:
| 不好 | 更好 |
|---|---|
fix | fix(auth): handle expired token on profile page |
update | docs(git): add rebase workflow notes |
bug | fix(payment): ignore failed recharge rows |
分支切换
| 场景 | 命令 | 示例 |
|---|---|---|
| 查看本地分支 | git branch | git branch |
| 切换分支 | git switch <branch> | git switch release/test |
| 新建并切换分支 | git switch -c <branch> | git switch -c feat/invitation-report |
新功能、新 bug、新实验都建议单独分支。分支名要让别人看懂,例如:
feat/invitation-recharge-dashboardfix/login-token-expirydocs/git-best-practices
同步远端
| 场景 | 命令 | 示例 |
|---|---|---|
| 只拉远端历史,不改当前工作区 | git fetch | git fetch origin |
| 拉远端历史并合入当前分支 | git pull | git pull origin main |
| 推送本地提交 | git push | git push |
| 第一次推送新分支 | git push -u origin <branch> | git push -u origin feat/invitation-report |
如果你不确定远端发生了什么,先 fetch,再看日志。pull 更方便,但它会直接改当前分支。
恢复误改
| 场景 | 命令 | 示例 |
|---|---|---|
| 丢弃工作区某个文件的修改 | git restore <file> | git restore src/auth/login.py |
| 把文件从暂存区拿出来 | git restore --staged <file> | git restore --staged src/auth/login.py |
老写法,能用但不如 restore 清楚 | git checkout -- <file> | git checkout -- src/auth/login.py |
恢复命令要谨慎。丢弃工作区修改通常不可逆。先 git diff,再 restore。
扩展命令:开始和别人协作
一个人写代码,只要能提交和推送。和别人协作,还要知道远端历史、冲突、PR、标签和责任边界。
查看远端和分支关系
| 场景 | 命令 | 示例 |
|---|---|---|
| 看本地分支追踪哪个远端分支 | git branch -vv | git branch -vv |
| 看远端详情 | git remote show origin | git remote show origin |
| 拉远端并清理已删除分支引用 | git fetch --prune | git fetch --prune origin |
多人仓库里,分支会不断创建和删除。fetch --prune 能减少“远端早没了,本地还显示”的噪音。
处理冲突
冲突不是 Git 坏了,而是两个改动同时碰到了同一块内容。处理顺序建议固定:
- 先确认冲突文件:
git status - 打开文件,搜索冲突标记:
<<<<<<< - 保留正确内容,删除冲突标记
- 运行测试或至少构建
git add <file>- 继续 merge 或 rebase
不要只让编辑器“一键接受当前”或“一键接受传入”。那适合很明确的配置文件,不适合业务代码。
临时保存现场
| 场景 | 命令 | 示例 |
|---|---|---|
| 临时保存未提交改动 | git stash push -m "message" | git stash push -m "wip: login page half done" |
| 查看 stash | git stash list | git stash list |
| 恢复并删除最近 stash | git stash pop | git stash pop |
| 恢复但保留 stash | git stash apply | git stash apply stash@{1} |
stash 适合被打断:手上改了一半,突然要切分支修线上 bug。长期工作不要靠 stash 存,应该提交到分支。
cherry-pick 和 revert
| 场景 | 命令 | 示例 |
|---|---|---|
| 把某个提交搬到当前分支 | git cherry-pick <commit> | git cherry-pick a1b2c3d |
| 生成一个反向提交,撤销某次公开提交 | git revert <commit> | git revert a1b2c3d |
cherry-pick 适合 hotfix 回补、跨分支拿一个小修复。revert 适合已经推到公共分支的提交,历史保留,风险更低。
高级命令:提高效率,但别炫技
高级命令的价值不是显得熟,而是让你更快定位问题、更小范围提交、更安全地修历史。
只提交一部分改动
一个文件里同时改了两件事,可以只把其中一部分放进当前提交。
git add -p src/order/service.py
git diff --staged
git commit -m "fix(order): validate empty cart"
常见场景:
- 一边修 bug,一边顺手改了注释。
- 一个文件里有功能改动,也有格式化改动。
- AI 一次生成太多,想拆成几次提交。
找谁改坏了
| 场景 | 命令 | 示例 |
|---|---|---|
| 看每一行最后是谁改的 | git blame <file> | git blame src/order/service.py |
| 看某个文件的历史差异 | git log -p <file> | git log -p -- src/order/service.py |
| 用二分法定位哪次提交引入 bug | git bisect | git bisect start && git bisect bad && git bisect good v1.8.0 |
blame 不该用来甩锅。它的正确用途是找到上下文:为什么当时这么改,关联了哪个需求,应该问谁。
修改最近一次提交
| 场景 | 命令 | 示例 |
|---|---|---|
| 补一个漏掉的文件,或改提交信息 | git commit --amend | git add tests/test_login.py && git commit --amend |
| 整理最近几个本地提交 | git rebase -i HEAD~3 | git rebase -i HEAD~3 |
原则很简单:没推到公共分支之前,可以整理历史;已经给别人用了,就别随便改历史。
看更清楚的历史
常用别名可以少敲很多字:
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.st "status --short"
git config --global alias.ds "diff --staged"
别名不要太神秘。团队里如果每个人都依赖自己看不懂的别名,排障会变麻烦。
协作模式:主干开发还是分支开发
Git 没有唯一正确流程。团队规模、发布频率、测试能力、代码所有权,都会影响分支策略。

主干开发
主干开发的核心是:大家频繁把小改动合进主干,分支活得很短。它适合自动化测试强、发布节奏快、团队纪律好的项目。
优点:
- 集成早,冲突少积压。
- 主干长期接近可发布状态。
- 分支不容易变成“另一个项目”。
代价:
- 需要可靠 CI。
- 需要 Feature Flag 或灰度开关。
- 提交必须小,review 要快。
一个典型例子:给订单页增加“导出 CSV”按钮。不要在分支里憋三天,而是拆成几步很小的提交:
- 先加后端导出接口,但默认不暴露入口。
- 再加前端按钮,受 Feature Flag 控制。
- 再补权限和埋点。
- CI 通过后快速合入主干。
- 在测试环境打开 Feature Flag 验证。
- 线上灰度打开,再逐步放量。
对应的本地动作可能是:
git switch main
git pull origin main
git switch -c feat/order-export
git add src/order/export.py tests/test_order_export.py
git commit -m "feat(order): add csv export endpoint"
git push -u origin feat/order-export
主干开发并不是“不建分支”,而是分支很短、改动很小、集成很快。
功能分支开发
功能分支开发更常见:每个需求一个分支,开发完成后通过 PR 合并。
优点:
- 边界清楚,适合 review。
- 未完成需求不影响主干。
- 适合多团队、多模块协作。
风险:
- 分支太久会产生大冲突。
- PR 太大没人愿意认真看。
- 分支之间互相依赖会变乱。
一个典型例子:重做邀请页面,涉及后端接口、前端列表、支付明细弹窗、测试数据。这种改动天然适合功能分支:
git switch release/test
git pull origin release/test
git switch -c feat/invitation-recharge-report
开发时不要把所有东西塞进一个提交,可以拆成:
feat(invitation): add recharge summary api
feat(invitation): render distributor recharge columns
feat(invitation): add payment detail drawer
test(invitation): cover date range filtering
然后推送分支并创建 PR。review 通过、CI 通过,再合入目标分支。
我的建议很朴素:大多数团队用“短功能分支 + PR + 强 CI”就够了。分支可以存在,但不要让它活太久;PR 可以承载讨论,但不要让它变成几千行大包。
合并方式:rebase 还是 merge
这个选择通常出现在三个时刻:
- 你在 feature 分支开发了几天,主干已经往前走了,需要把主干的新代码合进来。
- PR 准备合并,平台让你选择 merge commit、squash merge、rebase merge。
- 你想整理本地提交历史,让 PR 更容易 review。
所以它不是抽象争论,而是每天都会遇到的协作决策。merge 和 rebase 解决的是不同问题。

merge 保留真实分叉
merge 会保留分支历史,并产生一个 merge commit。它的好处是诚实:历史上确实发生过一个分支,最后合回来了。
适合:
- 公共分支合并。
- 需要保留 PR 上下文。
- 发布分支、长期分支。
缺点:
- 历史可能有点乱。
- 小分支很多时,图会变复杂。
例子:你在 feat/report-export 上开发,main 已经有别人合入的新提交。你想把 main 合进当前分支:
git switch feat/report-export
git fetch origin
git merge origin/main
如果没有冲突,Git 会生成一个合并结果。若有冲突,修完后执行:
git add src/report/export.py
git commit
如果是 PR 合入主干,平台上的 “Create a merge commit” 本质上也是 merge。它会保留“这个 PR 曾经作为一个分支存在过”的边界。
rebase 让历史变直
rebase 会把你的提交“搬到”目标分支最新提交之后,看起来像从最新主干直接开发。
适合:
- 整理自己本地未共享的提交。
- 合并前让 feature 分支跟上主干。
- 保持线性历史。
风险:
- 会改写提交哈希。
- 已共享分支随便 rebase,别人会很痛苦。
例子:你自己的分支还没人基于它继续开发,只是想让 PR 历史更干净:
git switch feat/report-export
git fetch origin
git rebase origin/main
如果出现冲突,解决后继续:
git add src/report/export.py
git rebase --continue
如果发现 rebase 过程中越修越乱,可以中止:
git rebase --abort
rebase 后提交哈希会变。如果这个分支已经推到远端,再推送通常需要:
git push --force-with-lease
注意是 --force-with-lease,不是裸 --force。它会先确认远端没有别人新增的提交,风险更低。
一个实用规则
可以这么记:
| 场景 | 建议 |
|---|---|
| 自己本地分支,没推给别人 | 可以 rebase |
| 已推给别人协作的分支 | 谨慎 rebase |
| 合入主干,想保留 PR 边界 | merge commit |
| 团队要求线性历史 | squash 或 rebase merge |
| 线上 hotfix 回滚 | revert |
不要把 rebase 当成高级,merge 当成低级。能让团队看懂、能安全回滚,就是好历史。
gh:把 GitHub 操作带回命令行
Git 管本地和远端仓库历史,gh 管 GitHub 上的协作对象:issue、PR、review、checks、release。

常用命令:
| 场景 | 命令 | 示例 |
|---|---|---|
| 登录 GitHub | gh auth login | gh auth login |
| 克隆仓库 | gh repo clone owner/repo | gh repo clone example/shop-api |
| 看 issue | gh issue list | gh issue list --assignee @me --state open |
| 看 issue 详情 | gh issue view <id> | gh issue view 123 --comments |
| 创建 PR | gh pr create | gh pr create --base main --head feat/order-export --fill |
| 浏览器打开 PR | gh pr view --web | gh pr view 456 --web |
| 看 CI 状态 | gh pr checks | gh pr checks 456 |
| 检出某个 PR | gh pr checkout <id> | gh pr checkout 456 |
| 在命令行 review | gh pr review | gh pr review 456 --comment -b "需要补一个空数据测试" |
| 合并 PR | gh pr merge | gh pr merge 456 --squash --delete-branch |
gh 的价值是把浏览器里的重复动作变成脚本和终端动作。比如日报、PR 状态检查、批量 issue 查询,都可以从 gh 开始自动化。
在 AI 编程场景里,gh 还有一个很实际的作用:把 GitHub 上的协作上下文交给 AI,而不是让 AI 只看本地代码。
比如让 AI 修一个 issue 前,可以先把 issue 内容、评论、关联 PR、失败检查都拉下来:
gh issue view 123 --comments
gh pr list --search "fix login token"
gh pr checks 456
AI 写完代码后,也可以用 gh 帮你生成 PR 草稿,但人要检查:
gh pr create --base main --head fix/login-token --title "fix(auth): refresh expired token" --body-file /tmp/pr-body.md
gh pr checks
更适合 AI 的工作方式是:
- 用
gh issue view获取需求和讨论。 - 让 AI 根据 issue 拆任务。
- 本地修改后用
git diff审查。 - 用
gh pr create创建 PR。 - CI 失败时用
gh pr checks和日志继续定位。
这样 AI 拿到的是“带上下文的问题”,不是孤立的一段代码。
Git 工作流能自动做什么
这里的“工作流”主要指 GitHub Actions、GitLab CI、Gitea Actions 等围绕 Git 事件触发的自动化。你不需要先背配置语法,先知道它能做什么。

代码质量
典型触发点:
- push 到分支。
- 创建或更新 PR。
- 打 tag。
能做的事:
- 跑单元测试、集成测试、端到端测试。
- 跑 lint、format check、类型检查。
- 生成覆盖率报告。
- 检查依赖安全漏洞。
- 检查 license、敏感信息、超大文件。
构建和发布
能做的事:
- 构建前端静态站点。
- 构建 Docker 镜像。
- 上传制品到对象存储或制品库。
- 打 release 包。
- 根据 tag 自动发布 PyPI、npm、容器镜像。
部署
能做的事:
- 部署测试环境。
- 部署生产环境。
- 灰度发布。
- 回滚到上一版本。
- 部署后跑 smoke test。
协作和治理
能做的事:
- 自动给 issue 打标签。
- 自动检查 PR 标题格式。
- 自动要求指定 reviewer。
- 自动生成 changelog。
- 自动同步 release 分支。
- 自动发送飞书、Slack、邮件通知。
- 自动做 AI Review,给出风险点和测试建议。
工作流的关键不是“自动化越多越好”。关键是把高频、重复、容易忘的检查放进机器里,把真正需要判断的部分留给人。
模板:让协作少靠记忆
模板不是形式主义。好的模板能让信息结构稳定,让 reviewer 少猜。

Issue 模板
常见类型:
- Bug 报告:现象、期望、复现步骤、环境、截图、日志。
- 功能请求:背景、目标、非目标、验收标准。
- 技术任务:范围、方案、风险、测试点。
一个好 issue 要回答:为什么要做、做到什么算完成、有没有边界。
PR 模板
推荐至少包含:
- Summary:改了什么。
- Test Plan:怎么验证。
- Risk:风险和回滚方式。
- Screenshots:UI 改动截图。
- Related Issues:关联 issue。
如果团队经常漏测试,就把 Test Plan 放到模板里;如果经常忘截图,就把截图项放进去。模板是对团队常犯错的补丁。
分支保护
常见规则:
- 主干禁止直接 push。
- PR 必须通过 CI。
- 至少一个 reviewer approve。
- 必须解决所有 review conversation。
- 必须线性历史或必须 merge commit。
- 必须签名提交。
规则不要一上来拉满。先保护最关键的分支和最关键的检查,再逐步收紧。
图形界面:不是低级,只是适合不同任务
命令行适合精确、可复制、可脚本化。图形界面适合看历史、拆冲突、比较复杂 diff。
常见选择:
- VS Code Source Control:日常改文件、看 diff、暂存部分改动。
- GitHub Desktop:简单项目、非纯开发角色参与协作。
- GitKraken / Fork / Sourcetree:看分支图、处理复杂历史。
- JetBrains IDE Git 工具:和重构、测试、代码导航结合紧。
我的习惯是:状态、提交、同步走命令行;复杂冲突和大 diff 用编辑器。不要因为“高手都用命令行”而拒绝 GUI。真正重要的是你知道每个按钮背后对应的 Git 动作。
反模式:Git 最常见的误用

大而混的提交
一个提交里同时改功能、格式、依赖、文档、配置。review 很难看,回滚也很难回。
改法:拆提交。一个提交只表达一个意图。
把密钥提交进仓库
一旦密钥进入 Git 历史,删除文件不等于安全。别人可能已经拉过,CI 日志可能留过。
改法:立即作废密钥,轮换新密钥,再清历史。预防上用 secret scan 和 .env 忽略规则。
公共分支强推
git push --force 会改写远端历史。对共享分支这么做,别人本地历史会乱。
改法:需要强推时用 --force-with-lease,并只对自己的 feature 分支使用。
长期不拉主干
一个分支开发两周不跟主干同步,最后合并时冲突爆炸。
改法:短分支、频繁同步、小 PR。
用 Git 存大二进制
大图片、视频、模型文件、压缩包进 Git,仓库会越来越沉。
改法:用对象存储、Release Asset、Git LFS,或者只提交生成脚本和小样本。
AI 生成代码一次性大提交
AI 一口气改几十个文件,然后直接 commit。看起来省时间,实际上把风险打包了。
改法:先看 diff,拆成小提交,每次提交都能解释、能测试、能回滚。
疑难杂症:复杂场景怎么处理
Git 真正让人紧张的,往往不是普通提交,而是现场已经乱了:冲突修一半、错合了分支、想撤销中间某次提交、本地和远端历史不一致。下面这些案例可以当作排障模板。

案例一:复杂冲突,不知道该保留哪边
场景:你在 feat/recharge-detail 改了邀请页支付明细,另一个同事在 main 改了同一个接口字段。你执行合并后出现冲突。
先不要急着点编辑器按钮。按这个顺序来:
git status
git diff
git status 会告诉你哪些文件冲突。打开文件后会看到类似结构:
<<<<<<< HEAD
const amount = row.totalPaidAmount
=======
const amount = row.successPaidAmount
>>>>>>> origin/main
这里的 HEAD 是你当前分支的内容,下面是被合进来的内容。正确做法不是简单二选一,而是回到业务含义:页面按成功订单统计,所以应该保留 successPaidAmount,但如果你当前分支还加了赠送积分字段,也要一起留下。
修完文件后:
git add src/pages/invitation.tsx
git diff --staged
git commit
如果你是在 rebase 中遇到冲突,最后一步不是 git commit,而是:
git add src/pages/invitation.tsx
git rebase --continue
判断自己是在 merge 还是 rebase 里,可以看 git status。它会明确提示。
案例二:只回滚之前某一次提交
场景:主干上已经有 5 个新提交,其中第三个提交引入了一个错误。你不能把主干退回到旧版本,因为后面的提交是好的。
先找到那次提交:
git log --oneline --decorate -20
假设坏提交是 a1b2c3d,用 revert 生成反向提交:
git revert a1b2c3d
如果 revert 没冲突,Git 会打开提交信息编辑器,保存即可。如果有冲突,修完后:
git add src/payment/callback.py
git revert --continue
这个方式适合已经公开的历史。它不会删除 a1b2c3d,而是新增一个“撤销 a1b2c3d 效果”的提交。团队成员拉代码时不会被历史改写影响。
案例三:本地版本错乱,不知道自己比远端多了什么
场景:你很久没同步,分支上有本地提交,也有远端提交。直接 pull 怕冲突,直接 push 又被拒。
先只拉远端信息,不改工作区:
git fetch origin
看本地比远端多了哪些提交:
git log --oneline origin/main..HEAD
看远端比本地多了哪些提交:
git log --oneline HEAD..origin/main
如果你的本地提交还没分享给别人,可以选择 rebase:
git rebase origin/main
如果这是多人共享分支,优先 merge:
git merge origin/main
这两个命令前都建议先保证工作区干净:
git status
如果有未提交改动,要么提交,要么 stash。
案例四:AI 改乱了几十个文件,想只保留一部分
场景:你让 AI 修一个按钮,结果它顺手改了样式、重排了 import、动了无关模块。不要直接提交。
先看总览:
git status --short
git diff --stat
只选择你认可的部分:
git add -p src/components/InviteButton.tsx
git diff --staged
git commit -m "fix(invitation): handle invite button loading state"
不需要的文件可以恢复:
git restore src/styles/global.css
git restore src/utils/date.ts
如果 AI 改动很大,但你还想留个备份,可以先建一个临时分支保存现场:
git switch -c spike/ai-invite-experiment
git add .
git commit -m "spike: keep ai generated invite experiment"
再切回原分支重新挑选可用改动。这样不会因为一次失败尝试污染正式分支。
案例五:不小心提交了密钥
场景:你把 .env 或 token 提交了。第一反应不应该是“删掉文件再提交”,而是先把密钥作废。
处理顺序:
- 立即在对应平台作废密钥。
- 生成新密钥。
- 把
.env加入.gitignore。 - 如果提交还没推送,可以用本地历史整理去掉。
- 如果已经推送,通知团队并按仓库规范清理历史。
还没推送时,可以这样改最后一次提交:
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend
已经推送时,不要自己偷偷改历史。公共仓库清历史会影响所有人,需要团队统一处理。
AI 时代,更要用好 Git
AI 编程让代码生成更快,也让错误扩散更快。Git 在这里的价值不是变小,而是更大。

给 AI 单独分支
每次让 AI 做较大改动,先开分支:
feat/ai-refactor-authfix/ai-payment-edge-casespike/ai-search-ranking
这样即使结果不好,也不会污染主线。
把 AI 改动拆小
AI 常见问题不是“写不出代码”,而是“一次写太多”。你要控制节奏:
- 先让它改一处。
- 看 diff。
- 跑测试。
- 提交。
- 再继续下一处。
每个提交都应该回答:这一步解决了什么问题?
用 Git 做验收边界
AI 交付后,不要只看它的解释。看这些证据:
git diff是否只改了应该改的文件。- 测试是否覆盖主要路径。
- 是否改了配置、依赖、迁移、权限。
- 是否引入密钥、日志泄漏、调试开关。
- 是否把无关格式化混进来。
让 AI 写 PR 说明,但人来确认
AI 很适合根据 diff 草拟 PR 描述:
- Summary
- Test Plan
- Risk
- Rollback
- Screenshots
但最终要由人确认。PR 描述不是给机器看的,是给未来接手这个问题的人看的。
保留可回滚点
AI 改动越多,越需要频繁提交。小提交就是保险丝。出错时可以 revert 某一步,而不是把半天工作全部推倒。
最后:Git 是协作习惯,不只是命令
Git 学到最后,不是记住更多冷门参数,而是养成几个稳定动作:
- 改之前先建分支。
- 提交前看 diff。
- 一个提交只做一件事。
- PR 里写清楚验证方式。
- 公共历史不要随便改。
- 自动化检查能前置就前置。
- AI 生成代码也必须经过 diff、测试和 review。
这些习惯听起来普通,但会让项目在几个月、几年后仍然可维护。Git 最好的状态,是它不再打断你,而是安静地帮你留下每一步可追溯的证据。
微信公众号
欢迎关注「字与码」
如果这篇文章对你有用,也欢迎在微信里继续关注后续更新。
X / Twitter
关注 @ax2_zicode
更即时的技术观察、新文章提醒和一些短想法会发在 X 上。