#Vibe Coding#DevOps

Git 高级工作流:rebase、cherry-pick、reflog

会 add/commit/push 不算会用 Git。本文讲清进阶工作流:rebase(变基,让历史线性)vs merge、interactive rebase(改写历史)、cherry-pick(挑提交)、reflog(救命稻草)、stash、以及怎么从"搞砸了"里救回来,让你不再怕 Git。

本文要点

  • 主题聚焦:Vibe Coding、DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年6月14日·更新于 2026年7月23日

Git 高级工作流:rebase、cherry-pick、reflog

如果你对 Git 的认知停在 add / commit / push / pull,那你对 Git 的恐惧大概率来自两次体验:一是历史乱成一团、合并冲突看不懂;二是误操作之后以为代码"没了"连夜重写。

会基础命令 ≠ 会用 Git。真正区分新手和老手的,是 rebase、cherry-pick、reflog 这套进阶工作流。它们做两件事:让协作历史更顺,以及在你搞砸时把你捞回来。这篇文章按"先讲清楚是什么、再讲什么时候用、最后讲怎么救"的顺序展开,读完你应该能在生产仓库上放心地用它们。


一、merge vs rebase:两条哲学

把分支合并回主干,有两条路。

merge:保留双方原始历史,生成一个新的"合并提交"(merge commit)。

bash
git switch main
git merge feature/login

历史长这样:

text
*   Merge branch 'feature/login'   (merge commit)
|\
| * add login API                   (feature)
| * add login UI
* | fix typo in readme              (main)
|/
* initial commit

优点是真实——你能看出哪条分支做了什么、何时合的。缺点是历史是一张网,人多了会变脏。

rebase:把 feature 分支的提交"摘下来",逐个重新放到 main 最新提交之上,历史被线性化。

bash
git switch feature/login
git rebase main

结果:

text
* add login API                    (重放后)
* add login UI
* fix typo in readme
* initial commit

干净、线性、像一条时间线。代价是:这些提交被"重写"了,它们的 commit hash 已经不是原来的。

什么时候用哪个?

  • 团队公共分支(main / release):用 merge 或 squash merge,保留"何时合入"的可追溯性。
  • 个人 feature 分支同步主干:git rebase main 拉平最新代码,PR 里历史清爽。
  • 发版前的整理:interactive rebase 把十几个 WIP 提交压成两三个有意义的。

rebase 的黄金规则:永远不要 rebase 已经推送到公共分支的提交。因为 rebase 重写了 hash,别人 pull 下来会出现重复提交和地狱级冲突。一旦共享,就当作"已固化"的历史。


二、interactive rebase:改写历史的瑞士军刀

git rebase -i 是 Git 里最被低估的命令。它打开一个编辑器,列出指定范围内的每个提交,让你逐个决定命运:

bash
git rebase -i HEAD~5     # 整理最近 5 个提交
git rebase -i main       # 整理当前分支相对 main 的所有提交

你会看到:

text
pick   a1b2c3   wip
pick   d4e5f6   add login form
pick   g7h8i9   fix typo
pick   j0k1l2   wip
pick   m3n4o5   finalize login

把 pick 改成动作词就行:

  • squash / s:把这个提交合并进上一个,提交信息也合在一起。WIP 提交通常这么处理。
  • fixup / f:同 squash,但丢弃这个提交的信息(纯垃圾提交用)。
  • reword / r:保留提交,但改提交信息。把 "fix bug" 改成有意义的描述。
  • reorder:直接调整行顺序,提交会被重排。
  • drop / d:删除这个提交。
  • edit / e:到这个提交时暂停,允许你当场修改文件内容(用来拆分提交、补改动)。

整理完一个 feature 分支,把 12 个杂乱提交压成 3 个清晰提交,PR review 体验直接拉满。记住:interactive rebase 也是重写历史,只在自己的分支上做。


三、cherry-pick:精确投递一个提交

有时候你不想合并整条分支,只想把某个提交"搬"过来。典型场景:

  • 线上出 bug,你在 main 上修了,但要把这个修复单独同步到 release 分支。
  • 同事在一个分支里顺手修了一个跟你相关的问题,你只要那一行。
bash
git switch release-1.2
git cherry-pick a1b2c3d           # 把这个提交应用到当前分支
git cherry-pick a1b2c3d d4e5f6g   # 挑多个
git cherry-pick a1b2c3d..e5f6g7   # 挑一个范围(左开右闭)

cherry-pick 会生成一个新的提交(因为 hash 变了,内容相同)。如果冲突,Git 会停下让你解决,然后 git cherry-pick --continue(或 --abort 放弃)。

注意:cherry-pick 后如果将来再 merge 那条源分支,Git 通常能识别重复提交不会重复应用,但跨上下文的 cherry-pick 有时还是会制造冲突,别滥用。


四、reflog:Git 里的"后悔药"

这是本文最值钱的一节。

很多人以为 git reset --hard 或 git branch -D 之后,东西就"没了"。几乎从来不是。 Git 在背后维护一份 reflog——记录 HEAD 和所有分支指针的每一次移动,默认保留 90 天。

bash
git reflog

输出大概这样:

text
a1b2c3d HEAD@{0}: reset: moving to HEAD~3
e5f6g7h HEAD@{1}: commit: finalize login
d4e5f6g HEAD@{2}: commit: add tests
c3d4e5f HEAD@{3}: commit: wip
...

HEAD@{1} 就是"上一次 HEAD 在的地方"。这意味着——

误 reset --hard 了? 找到 reset 之前那个 hash(或 HEAD@{1}):

bash
git reset --hard HEAD@{1}

误删分支了?

bash
git switch -c recovered-branch HEAD@{2}
# 或直接用 hash
git switch -c recovered-branch a1b2c3d

误 amend 覆盖了原提交? amend 也是一次 HEAD 移动,原提交还在 reflog 里,直接捡回来。

rebase 把提交弄丢了? reflog 里 rebase 开始前的位置还在,git reset --hard <那个点> 一键穿越回去。

记住一句话:只要你 commit 过(哪怕没 push),reflog 几乎一定能帮你找回来。 真正丢的只有两种:从未提交过的改动(stash 和工作区被 git clean -fd 干掉),以及超过 reflog 保留期的对象。


五、stash:临时存档,切场救急

写到一半被叫去修线上紧急 bug,代码还没法提交,怎么办?git stash 把工作区和暂存区的改动临时存起来,工作区还原成干净状态,你就能切分支了。

bash
git stash push -m "wip: 登录表单校验"   # 存档并备注
git switch main
# ...修 hotfix、提 PR、合并...
git switch feature/login
git stash pop                          # 取回最近的存档并删除

进阶用法:

bash
git stash list                  # 看所有存档
git stash show -p stash@{0}     # 看某个存档的 diff
git stash apply stash@{1}       # 取回但不删除(可反复 apply)
git stash drop stash@{0}        # 删掉某个存档
git stash clear                 # 清空所有(谨慎)

pop 冲突时会保留存档,解决完冲突手动 drop。实战建议:每次 stash 都加 -m "备注",不然 stash list 里几个无名条目会让你完全想不起哪个是哪个,三天后只能 clear。


六、救援手册:从"搞砸了"里自救

把前面零散的救援场景集中成一份速查表。

场景 1:刚 commit 完发现漏改了一个文件 / 提交信息写错了

只要还没 push:

bash
# 改文件后
git add .
git commit --amend            # 修改提交,会打开编辑器改信息
git commit --amend --no-edit  # 保持原信息,只把新改动并进来

amend 本质是"用新提交替换旧提交",所以旧的那个在 reflog 里依然可找回。

场景 2:已经 push 了,但想改提交信息/合并提交

如果只有你一个人用这个分支,可以 amend 或 rebase 后 git push --force-with-lease。--force-with-lease 而不是 --force——前者会在远程有别人新提交时拒绝推送,避免覆盖同事的工作。

场景 3:误删了本地分支

bash
git reflog                  # 找到分支最后一次指向的 hash
git switch -c feature/login <hash>

场景 4:git reset --hard 把工作干没了

bash
git reflog
git reset --hard HEAD@{1}   # 回到 reset 之前

场景 5:rebase 中途改乱了,想全部放弃

bash
git rebase --abort

任何 rebase 进行中状态都能用 --abort 干净退回起点。

场景 6:merge 冲突搞不定了

bash
git merge --abort          # 放弃这次合并,回到合并前

场景 7:想撤销某个已经 push 的提交(不删历史,而是反向提交)

bash
git revert a1b2c3d         # 生成一个"反向"提交,抵消改动
git push

公共分支上永远用 revert 而不是 reset——revert 不改写历史,安全可推送。

把这套救援动作内化之后,你对 Git 的恐惧会大幅下降,因为你心里清楚:只要提交过,几乎都能救。


小结

  • merge 保留历史,rebase 线性化,公共分支用 merge / squash merge,个人分支用 rebase 同步主干。
  • rebase 的黄金规则:不 rebase 已推送的公共提交;私人分支随便改。
  • interactive rebase(rebase -i) 是整理历史的瑞士军刀:squash 合并、reword 改信息、reorder 调序、drop 删除。
  • cherry-pick 用来精确搬运单个提交,做 hotfix 同步、跨分支移植改动。
  • reflog 是 Git 的后悔药:reset --hard、删分支、误 amend 都能找回——只要你 commit 过。
  • stash 用来临时存档切场救急,养成 stash push -m 备注的习惯。
  • 公共分支用 revert,私有分支用 amend / rebase + push --force-with-lease。

金句一句:Git 之所以可怕,是因为你以为它"不可逆";学会 reflog 和 rebase 之后你会发现——Git 几乎没有真正的不可逆,只有你还不知道怎么逆转。

Related

相关推荐