Files
zhuangang/topics/devops/git/02-merge-rebase-recovery.md
T

4.3 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
devops.git.merge-rebase-recovery 合并、变基和撤销:merge / rebase / cherry-pick、revert / reset、reflog 找回 lesson devops
git
merge
rebase
cherry-pick
revert
reset
reflog
stash
intermediate
devops.git.branch-commit
2
devops.git.scenario.hotfix-and-leaked-secret
devops.git.interview
8
15
2

合并、变基和撤销:merge / rebase / cherry-pick、revert / reset、reflog 找回

解释说明

merge vs rebase

  • git merge feat:把两个分支的历史合在一起,可能产生一个合并提交;历史真实反映“并行开发”,但图形会交错。--no-ff 强制生成合并提交,记录“这里合入了一个功能”;--squash 把功能分支压成一个提交。
  • git rebase main:把当前分支的提交“搬到” main 最新提交之后重新应用,历史是一条直线;但会生成新的提交(哈希变了)。
  • 原则:只 rebase 自己的、还没被别人基于它工作的分支;公共分支(main、已共享的分支)不要 rebase。
  • git rebase -i:交互式整理提交(合并、改信息、删除、调整顺序),合并请求提交前整理用。

cherry-pick:把某个提交复制到当前分支,常用于把热修复同时带到 release 和 main。

撤销

场景 命令 说明
工作区改乱了 git restore 文件 丢弃未暂存的修改
误 add 了 git restore --staged 文件 取消暂存,修改保留
最后一次提交信息写错 / 漏文件(未推送) git commit --amend 改写最后一个提交
撤销本地提交(未推送) git reset --soft HEAD~1(保留改动)/ --hard(丢弃改动) 改写历史
撤销已推送的提交 git revert 提交 生成一个反向提交,不改写历史,团队协作的安全做法

reflog:记录 HEAD 每次移动的位置(本地,默认保留约 90 天)。reset --hard、删分支、错误的 rebase 之后,都可以在 reflog 里找到原来的提交并恢复。只要提交过,就几乎丢不了。

stash:临时保存未提交的改动,git stash push -m "说明"、git stash pop。

案例

  • 合并请求前,用 rebase -i 把“WIP”“修正拼写”这类提交合并成两三个有意义的提交,再 rebase main 解决冲突,评审者看到的历史清晰。
  • 上线后发现某个提交引入 bug,main 已经被多人拉取:用 git revert,不要 reset 后强推。
  • reset --hard 后发现删掉了还需要的提交:git reflog 找到哈希,git reset --hard 哈希 或 git branch rescue 哈希 恢复。

代码示例

下面这些操作在 examples/git-lab/lab.py 里会在临时仓库中自动执行并验证:

# 功能分支同步最新 main(自己的分支,可以 rebase)
git switch feat/login
git fetch origin
git rebase origin/main            # 有冲突:解决后 git add 文件 && git rebase --continue;放弃:git rebase --abort
git push --force-with-lease       # 改写过历史的个人分支推送;比 --force 安全:远端有别人的新提交时会拒绝

# 撤销已推送的提交
git revert a1b2c3d

# 误 reset 之后找回
git reflog                        # 找到 “commit: feat: 重要改动” 那一行的哈希
git reset --hard 0ecd7f4

# 热修复带回 main
git switch main
git cherry-pick 9f8e7d6

易错点

  • 对公共分支 rebase 后强推:别人的历史和你的对不上,提交会“重复”或丢失。
  • 用 --force 而不是 --force-with-lease:可能覆盖同事刚推送的提交。
  • 已推送的提交用 reset 撤销:其他人再推送时又把它带回来。用 revert。
  • revert 一个合并提交:要用 -m 1 指定保留哪一边;之后想再合入同一分支,需要先 revert 这个 revert。
  • 以为 reflog 能找回从未提交过的改动:没提交(也没 stash)的工作区修改被 reset --hard 后是真的没了。
  • 解决冲突时直接全选“我的”或“他的”:可能丢掉对方的修改,冲突解决后要运行测试。

面试常问

见 Git 面试题:merge 和 rebase 的区别、revert 和 reset 的区别、误删提交怎么找回。