Git 必知必会:开发者实战手册
原创 · 约 52 分钟阅读 · 阅读 --

Git 必知必会:开发者实战手册

作者: 字与码


古董级程序员,大厂出来后一直在创业公司,现在仍活跃在一线做 AI 相关开发。更完整的更新写在微信公众号「字与码」:工作经历、技术实践、工具使用和这些年走弯路的记录,会不定期发在那里。若这篇文章对你有用,欢迎顺手关注。

Git 不只是“提交代码”的工具。它更像开发过程的黑匣子:你改过什么、为什么改、谁看过、怎么上线、出了问题怎么退回来,最后都会落到 Git 里。

这篇不是 Git 教材,而是一份能放在手边的参考手册。目标很简单:你看完后,知道日常开发该用哪些命令,团队协作还要补哪些动作,遇到 AI 生成代码时怎么让 Git 保护你。

Git 到底解决什么问题

从版本备份到分布式协作

早期写代码,最朴素的版本管理是复制目录:project-finalproject-final2project-final-really-final。它能保留一点历史,但不能回答三个关键问题:

  • 这一行是谁改的?
  • 为什么这样改?
  • 两个人同时改同一个文件,怎么合起来?

集中式版本控制把代码放在服务器上,开发者从服务器检出、提交。它解决了多人协作,但服务器是中心。网络断了,很多操作就受限;中心挂了,团队就停。

Git 的核心变化是分布式。每个开发者本地都有完整历史,不只是当前文件。你可以离线提交、查看历史、切分分支、回滚实验,等网络恢复再同步到远端。

Git 诞生于 2005 年,由 Linus Torvalds 为 Linux 内核开发创建。当时 Linux 内核社区需要一个速度快、能支撑大量分支和补丁协作、同时不依赖单一中心的版本控制系统。这个背景决定了 Git 的性格:快、强、偏底层,也有一点不迁就新手。

Git 的优势

Git 的优势可以压成四句话:

  • 本地完整历史:大多数查询、分支、提交都不需要联网。
  • 分支很轻:开分支几乎没有心理负担,适合实验。
  • 内容可追溯:提交、作者、时间、差异、关联 issue 都能留下证据。
  • 协作可自动化:PR、CI、部署、代码审查、Release 都能围绕 Git 事件运行。

它也有代价:概念多,命令多,错误操作看起来吓人。解决办法不是背完所有命令,而是建立正确心智模型。

Git 心智模型:工作区、暂存区、本地仓库、远端仓库

Git 心智模型:四个区域

先记住四个区域,Git 的大部分命令都是在它们之间移动内容。

区域含义典型动作
工作区你正在编辑的文件修改、删除、创建文件
暂存区下一次提交要包含的内容git add
本地仓库已经形成历史的提交git commitgit log
远端仓库团队共享的仓库git pushgit 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 addgit diff --staged 建立习惯,再考虑这种快捷写法。

本地仓库:已经形成历史的提交

本地仓库存放你机器上的完整提交历史。git commit 之后,改动才真正变成一条可追溯记录。

本地仓库能做很多离线操作:

  • git log 看历史。
  • git switch 切分支。
  • git revertgit restore 恢复问题。
  • git rebase -i 整理尚未共享的提交。

只要还没 push,本地提交通常仍是你的个人工作区,可以适度整理。

远端仓库:团队共享的历史

远端仓库是团队协作的公共位置,例如 GitHub、GitLab、Gitea 上的仓库。git push 后别人才能看到你的提交,git fetchgit pull 后你才能看到别人的提交。

一旦进入远端,历史就不再只属于你。公共分支上要更谨慎:

  • 不随意强推。
  • 不改写别人依赖的提交。
  • 通过 PR、CI、review 让改动可检查。

很多 Git 问题都来自没分清这四层。比如文件还在工作区,就不能指望别人看到;文件只 add 了,还没 commit,就还不是历史;本地 commit 了但没 push,别人也拿不到。

基本命令:够你独立完成开发

这组命令不花哨,但够你从拉代码、建分支、改文件、提交、同步到恢复误改,独立完成绝大多数开发工作。

独立开发需要掌握的 Git 基本命令

初始化和获取代码

场景命令示例
第一次拿到远端项目git clone <url>git clone git@github.com:example/shop-api.git
把本地目录变成仓库git initmkdir notes && cd notes && git init
查看远端地址git remote -vgit remote -v

日常开发里,clone 用得更多。init 通常用于新项目。

看状态和差异

场景命令示例
看哪些文件改了、哪些已暂存git statusgit status
看工作区还没暂存的差异git diffgit diff src/auth/login.py
看已经暂存、准备提交的差异git diff --stagedgit diff --staged
用一行图形看提交历史git log --oneline --graph --decorategit log --oneline --graph --decorate -10

好习惯是:提交前一定看 statusdiff。不要把“我以为改了这些文件”当成事实。

提交一组完整改动

场景命令示例
把文件放进暂存区git add <file>git add src/auth/login.py tests/test_login.py
按小块选择要提交的内容git add -pgit 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、定位问题都会轻很多。

提交信息也要像人话:

不好更好
fixfix(auth): handle expired token on profile page
updatedocs(git): add rebase workflow notes
bugfix(payment): ignore failed recharge rows

分支切换

场景命令示例
查看本地分支git branchgit branch
切换分支git switch <branch>git switch release/test
新建并切换分支git switch -c <branch>git switch -c feat/invitation-report

新功能、新 bug、新实验都建议单独分支。分支名要让别人看懂,例如:

  • feat/invitation-recharge-dashboard
  • fix/login-token-expiry
  • docs/git-best-practices

同步远端

场景命令示例
只拉远端历史,不改当前工作区git fetchgit fetch origin
拉远端历史并合入当前分支git pullgit pull origin main
推送本地提交git pushgit 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 -vvgit branch -vv
看远端详情git remote show origingit remote show origin
拉远端并清理已删除分支引用git fetch --prunegit fetch --prune origin

多人仓库里,分支会不断创建和删除。fetch --prune 能减少“远端早没了,本地还显示”的噪音。

处理冲突

冲突不是 Git 坏了,而是两个改动同时碰到了同一块内容。处理顺序建议固定:

  1. 先确认冲突文件:git status
  2. 打开文件,搜索冲突标记:<<<<<<<
  3. 保留正确内容,删除冲突标记
  4. 运行测试或至少构建
  5. git add <file>
  6. 继续 merge 或 rebase

不要只让编辑器“一键接受当前”或“一键接受传入”。那适合很明确的配置文件,不适合业务代码。

临时保存现场

场景命令示例
临时保存未提交改动git stash push -m "message"git stash push -m "wip: login page half done"
查看 stashgit stash listgit stash list
恢复并删除最近 stashgit stash popgit stash pop
恢复但保留 stashgit stash applygit 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
用二分法定位哪次提交引入 buggit bisectgit bisect start && git bisect bad && git bisect good v1.8.0

blame 不该用来甩锅。它的正确用途是找到上下文:为什么当时这么改,关联了哪个需求,应该问谁。

修改最近一次提交

场景命令示例
补一个漏掉的文件,或改提交信息git commit --amendgit add tests/test_login.py && git commit --amend
整理最近几个本地提交git rebase -i HEAD~3git 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”按钮。不要在分支里憋三天,而是拆成几步很小的提交:

  1. 先加后端导出接口,但默认不暴露入口。
  2. 再加前端按钮,受 Feature Flag 控制。
  3. 再补权限和埋点。
  4. CI 通过后快速合入主干。
  5. 在测试环境打开 Feature Flag 验证。
  6. 线上灰度打开,再逐步放量。

对应的本地动作可能是:

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。

所以它不是抽象争论,而是每天都会遇到的协作决策。mergerebase 解决的是不同问题。

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 CLI gh 的日常协作流程

常用命令:

场景命令示例
登录 GitHubgh auth logingh auth login
克隆仓库gh repo clone owner/repogh repo clone example/shop-api
看 issuegh issue listgh issue list --assignee @me --state open
看 issue 详情gh issue view <id>gh issue view 123 --comments
创建 PRgh pr creategh pr create --base main --head feat/order-export --fill
浏览器打开 PRgh pr view --webgh pr view 456 --web
看 CI 状态gh pr checksgh pr checks 456
检出某个 PRgh pr checkout <id>gh pr checkout 456
在命令行 reviewgh pr reviewgh pr review 456 --comment -b "需要补一个空数据测试"
合并 PRgh pr mergegh 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 的工作方式是:

  1. gh issue view 获取需求和讨论。
  2. 让 AI 根据 issue 拆任务。
  3. 本地修改后用 git diff 审查。
  4. gh pr create 创建 PR。
  5. CI 失败时用 gh pr checks 和日志继续定位。

这样 AI 拿到的是“带上下文的问题”,不是孤立的一段代码。

Git 工作流能自动做什么

这里的“工作流”主要指 GitHub Actions、GitLab CI、Gitea Actions 等围绕 Git 事件触发的自动化。你不需要先背配置语法,先知道它能做什么。

从提交到测试、构建、AI Review、部署和监控的自动化工作流

代码质量

典型触发点:

  • push 到分支。
  • 创建或更新 PR。
  • 打 tag。

能做的事:

  • 跑单元测试、集成测试、端到端测试。
  • 跑 lint、format check、类型检查。
  • 生成覆盖率报告。
  • 检查依赖安全漏洞。
  • 检查 license、敏感信息、超大文件。

构建和发布

能做的事:

  • 构建前端静态站点。
  • 构建 Docker 镜像。
  • 上传制品到对象存储或制品库。
  • 打 release 包。
  • 根据 tag 自动发布 PyPI、npm、容器镜像。

部署

能做的事:

  • 部署测试环境。
  • 部署生产环境。
  • 灰度发布。
  • 回滚到上一版本。
  • 部署后跑 smoke test。

协作和治理

能做的事:

  • 自动给 issue 打标签。
  • 自动检查 PR 标题格式。
  • 自动要求指定 reviewer。
  • 自动生成 changelog。
  • 自动同步 release 分支。
  • 自动发送飞书、Slack、邮件通知。
  • 自动做 AI Review,给出风险点和测试建议。

工作流的关键不是“自动化越多越好”。关键是把高频、重复、容易忘的检查放进机器里,把真正需要判断的部分留给人。

模板:让协作少靠记忆

模板不是形式主义。好的模板能让信息结构稳定,让 reviewer 少猜。

Issue 模板、PR 模板、提交规范和分支规则

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 最常见的误用

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 真正让人紧张的,往往不是普通提交,而是现场已经乱了:冲突修一半、错合了分支、想撤销中间某次提交、本地和远端历史不一致。下面这些案例可以当作排障模板。

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 提交了。第一反应不应该是“删掉文件再提交”,而是先把密钥作废。

处理顺序:

  1. 立即在对应平台作废密钥。
  2. 生成新密钥。
  3. .env 加入 .gitignore
  4. 如果提交还没推送,可以用本地历史整理去掉。
  5. 如果已经推送,通知团队并按仓库规范清理历史。

还没推送时,可以这样改最后一次提交:

git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend

已经推送时,不要自己偷偷改历史。公共仓库清历史会影响所有人,需要团队统一处理。

AI 时代,更要用好 Git

AI 编程让代码生成更快,也让错误扩散更快。Git 在这里的价值不是变小,而是更大。

AI 编程时代的 Git 使用方式:隔离分支、小提交、diff、测试、回滚

给 AI 单独分支

每次让 AI 做较大改动,先开分支:

  • feat/ai-refactor-auth
  • fix/ai-payment-edge-case
  • spike/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 最好的状态,是它不再打断你,而是安静地帮你留下每一步可追溯的证据。