下面的脚本重现了一个git分支合并的误操作,导致分支合并后,虽然在提交历史上可以看到每个commit,但是feature分支上提交的修改丢失了。
[ -a git-merge-head.git ] && rm -rf git-merge-head.git[ -a git-merge-head-logs ] && rm -rf git-merge-head-logsgit --bare init git-merge-head.gitgit clone git-merge-head.git git-merge-head-logscd git-merge-head-logs/echo "1" > v01-master.txtgit add -A && git commit -m "v01-master"git checkout -b featureecho "2" >> v01-master.txtgit add -A && git commit -m "v02-feature"git checkout masterecho "3" > v03-master.txtgit add -A && git commit -m "v03-master"#1git merge feature -m ""#2git reset .#3git checkout .git commit -m "commit version lost modification of v02-feature"echo "4" > v04-master.txtgit add -A && git commit -m "v04-master"echo "5" > v05-master.txtgit add -A && git commit -m "v05-master"cat v01-master.txt |
问题说明
可以看到上述命令最后cat v01-master.txt命令,其输出的文件内容如下,可以看到feature分支中的修改丢失掉了。
1
使用git log -- v01-master.txt命令查看文件提交历史,只有master分支的提交历史,feature分支的提交丢失了。
db8ba5a v01-master
错误分析
第一个出错的操作#1,在合并分支时,因为没有提交版本的说明,导致合并没完成,没有产生合并操作的commit,但这个时间合并的内容已经被加入了git的index暂存区,如果此时手工commit这2个分支的合并操作,完全是没问题的。此时的git status输出如下:
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: v01-master.txt
接下来第二个出错的操作#2,git reset .操作默认是mixed模式,会回滚index暂存区和本地仓库到指定的版本,所有的修改内容仍然是存在于当前git的工作空间的,分支中的内容可以git add之后再git commit,仍然不会丢失,此时的git status输出如下:
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes not staged for commit:
(use "git add
..." to update what will be committed) (use "git checkout --
..." to discard changes in working directory) modified: v01-master.txtno changes added to commit (use "git add" and/or "git commit -a")
最后第三步操作#3是版本修改丢失的关键原因,它通过git checkout .操作从index暂存区恢复代码到工作空间,将本地工作空间中的修改直接覆盖掉了,导致了此次合并中feature分支的修改丢失了。
其实上面#2和#3这2步操作接近于做了一次git reset --hard操作,只是因为之前的git merge操作没有完成,所以在.git目录里仍然存在MERGE_HEAD、MERGE_MODE和MERGE_MSG文件,可以继续提交此次合并操作,但实际上所有的代码已经被还原了。
错误修复
执行以下操作后,修复之前的错误操作。
#4git format-patch HEAD...HEAD~2#5git reset --hard HEAD~3#6git merge feature -m "FIX: merge branch feature into master"#7git am -3 000*-master.patchrm -f 000*-master.patchcat v01-master.txtecho git log -- v01-master.txtecho git log --graph --decorate --all |
首先由#4这个操作生成分支合并后的所有提交补丁文件。
其次使用#5操作回滚git版本到错误的合并操作的位置,这里测试脚本里使用HEAD来定位版本,实际中可以使用git提交时的hash版本号。
然后使用#6操作指令正确合并分支,然后将#4操作生成的补丁逐一应用,如果有冲突,则解决冲突并提交,然后应用下一个补丁,测试脚本里的2个补丁应用时,没有冲突,所以直接应用成功。