起因
为了避免分支过多导致git提交历史复杂,往往需要使用rebase
而非merge
来合并自己与他人的代码。
而rebase之后如果发现操作错误,又该如何撤回呢?
git rebase
日常开发往往是这样提交代码:在本地master分支开发,提交几个版本之后,然后使用git fetch
命令获取当前远程分支最新版本,再用git rebase
命令,将自己的提交与远程分支合并,然后在push到远程。
git中提交的每一个版本都是不会发生变化的,而git rebase却可以将自己的版本移动到远程版本之后,就好像是从最新的远程版本开发的一样,这又是怎么做到的呢?
答案其实很简单,git rebase
命令其实是重新创建了新的commit。那么原来的提交为什么消失了呢?因为git已经将原来的分支切到了rebase之后的新提交上,原来的版本没有分支指向它,自然就被隐藏了。很多git可视化工具都只显示有分支的提交。
有没有办法能保留原先的提交呢?
其实很简单,只要保证rebase之后仍然有分支指向原来的提交就可以了。
上面两张图进行了演示,rebase之前,将分支1和分支2都指向e9e0c7d这个提交,然后将分支1rebase到master。
结果可以看到:分支1在master分支之后重新生成了一个提交b49dbef,而分支2仍然指向原来的e9e0c7d。因此,在rebase之前,只要再新建一个分支指向当前提交节点即可。
那么问题来了,如果执行rebase之前没有其他分支指向这个提交,而rebase之后又发现错误想回退,却不记得版本号该怎么办呢?
git reflog
与git log
不同,git reflog
展示的不是提交的历史,而是分支的历史,即一个分支曾经指向过哪些commit。
如上图,
git reflog 分支1
命令可以看到, 分支1@{0}
代表当前分支1指向的提交,而分支1@{1}
则代表了分支1上一次指向的版本,同时还带上了rebase的操作记录。(绿色的"分支2"代表当前分支2指向这个节点)
而git reflog
命令则默认展示HEAD指针的历史:
这样,我们就可以从reflog中找到我们的分支原来指向的版本,然后checkout到这个版本就可以了。