Git变基压缩提交后本地代码回退至历史版本问题排查
1. 为什么git rebase -i HEAD~3会出现旧代码冲突?
你已经通过合并提交9387a5c把功能分支合并到了master,此时在master分支上执行git rebase -i HEAD~3,Git会沿着合并提交的主父链(即master分支的默认历史路径)回溯3个节点,最终定位到合并前的旧master版本(也就是你提到的8次提交前的节点)。
当你尝试压缩f7624c3、f68a001这两个提交时,Git会把这两个提交的变更重新应用到这个旧master节点上——但当前master已经包含了合并后的最新代码,这就导致旧master的代码和当前master的新代码产生冲突,所以你看到了冲突标记里的新旧代码对比。
本质是:你在已经合并后的master上做rebase,相当于要把已经合并过的提交重新“重演”一次到旧版本上,必然会和当前master的新代码冲突。
2. git reset HEAD~2 --soft为什么没效果?
从你的提交图来看,9387a5c是一个合并提交,它有两个父节点:master分支的8a1d144和功能分支的f7624c3。当你在master分支执行git reset HEAD~2 --soft时,Git是沿着master的主父链移动HEAD:
HEAD指向9387a5cHEAD~1指向主父节点8a1d144HEAD~2指向8a1d144的父节点b2e4905
这个操作只是把HEAD移到了更早的master节点,但并没有触及功能分支的那三个提交(f7624c3、f68a001、d7729a5),所以git log看不到提交被压缩的效果——因为你根本没对那些提交做任何修改。
正确的压缩方案
既然你想把功能分支的三个提交(f7624c3、f68a001、d7729a5)压缩成一个,正确的步骤应该是:
- 直接基于
d7729a5的父节点创建临时分支:git checkout -b temp-branch d7729a5^ - 执行交互式rebase来压缩提交:
在弹出的编辑器里,把git rebase -i d7729a5^f7624c3和f68a001前面的pick改成squash(或缩写s),保存退出后编辑合并后的提交信息。 - 回到master分支,重置到合并前的状态(如果master已推送到远程,谨慎操作,避免影响协作的其他开发者):
git checkout master git reset --hard 8a1d144 - 把压缩后的临时分支合并到master:
git merge temp-branch
如果master已经推送到远程仓库,不建议直接修改历史,此时可以考虑用git revert撤销之前的合并提交,再合并压缩后的分支。
内容的提问来源于stack exchange,提问作者Kevin2566

