编者按:本文讲变基操作(rebase),非git基础知识的解说,所以要想有所收获,至少对git基础操作有比较深刻的认识。
背景
自从Linus大神创建git,已经过去了十几年。git已经发展为了分布式版本管理软件的翘楚,而基于git的github则被圈内人士戏称为“全球最大的同性交友网站”,可见其在全球程序员心中的地位和作用。
个人来说,从2015年开始参与第一个创业项目,第一次接触git以来,逐步摸索和试验,渐渐的掌握,并熟练掌握了使用git的基础功能进行版本管理的方法和技巧。在这个过程中,对git的使用,及其衍生出来的gitflow文化都有了比较深刻的认识。
但是,rebase这个概念,因为平时工作用不到,又多多少少听到了一些“误用rebase”的惨痛教训,一直没有搞懂并应用于实践中。最近无意中读到了一篇很不错的讲解rebase的公号文章,从理论上理解了rebase的含义,并大胆的在测试项目中实践了一把。所以也就有了这篇文章。
可以毫不夸张的讲,不会rebase,也可以把git玩得转;但会了rebase,对git的本质—提交对象的哈希、存储原理—会有更深刻的理解,才能基本算一个高级giter吧。
对比merge理解rebase
要进行rebase操作,先要理解rebase操作;要理解rebase操作,要先理解操作为什么叫rebase操作。有时候,“望文生义”是个好方法,为什么此操作不叫commit,不叫push,也不叫pull?这是因为这个操作,涉及到base,也涉及到了re!
中文翻译rebase为“变基”,其实应该明确的称之为“变更基础”。这个基础(base)就是当前我们所处的这个分支,所以变基就是变更我们所处的这个分支。尽管这不好理解,但是需要记下来!
为了理解这个“变基”的含义,我们需要用合并(merge)来对比理解。如果对merge熟悉,对rebase理解就不难了。merge是一个git常用命令,在主干分支上,我们经常会进行这样的操作:把某个hotfix分支(改动非常少,通常只修复一个bug),merge到主干分支上;或者在merge request中,通过点击“merge”按钮,由系统将需要merge的分支合并(merge)到主干分支,或所谓的“目标分支”。
以第一个场景为例,在merge这个过程中,由于我们“正在”主干分支上,我们的base就是主干分支。merge操作,实际是将hotfix分支上的提交逐一的“合并”到base分支,也就是主干分支上。
理解了这一点,我们考虑另外一个场景。现在需要有个newFeature分支写一些新的feature。为了保证在这个分支开发时,还能hold住主干分支的变化,我会经常性的merge主干分支的改动,这样可以尽早的,小部分的处理冲突。
以上做法当然没有错,但是会在提交结构上有很多的merge操作,使得主干看起来“支离破碎”。有没有一种方式,使得提交后,主干没有这些merge呢?显然有,只要我们的newFeature里的提交,是一条条的加入到主干中就可以了。但是我们现在又在newFeature分支,怎么办呢?
变基!变基!!变基!!!
尽管我们正在newFeature分支,但我们可以通过变基操作,使得newFeature的每个提交,像我们正在主干分支一样,进行提交。所以,
变基操作允许我们将当前所在分支(例如newFeature分支),变为要rebase的分支(主干分支),即在后者上添加前者的提交,最后在返回到当前分支。
实际场景操作
在本节中我们模拟做一个rebase操作。在这之前,强烈推荐看一下这篇文章,以更加系统的了解rebase操作的步骤(上一节我只是通过我的理解阐述了一下rebase操作)
以前我们会这样做merge操作
git checkout newFeature
# some commit on newFeature and master
git merge master
同样的,brebase也是如此的操作(仅仅是换了个命令)
git checkout newFeature
# some commit on newFeature and master
git rebase master
rebase是一个一入一出的完整过程,即
- 变基为master
- 逐条拷贝提交到新基
- 遇到冲突则暂停,等待人工介入
- 重复#2,直到所有提交都合并入新基
- 变回原基为newFeature
在变基过程中,我们只需要关注在提交拷贝过程中产生的冲突,并修改(如果存在的话),然后继续看下一个提交拷贝会不会产生冲突
# Resolve code conflict
git add --all
git rebase --continue
直到冲突全部解决,这个过程并不需要用commit,push等命令。当成功变回原基时(上面的item #3),就达到了我们想要的效果。
到此为止,网上绝大多数关于rebase的文章也就结束了。其实还有两个问题需要深入讨论一下:
-
能否反复变基操作?答案是肯定的。每次变基其实是一个封闭过程,这个一变一回的操作可以执行多次。但要注意的是,每次做变基,所有原基的提交都要进行重新拷贝。这是因为新基还是原来的分支(例如主干分支),其并不是和原基做比较后,只拷贝不同的提交。
例如,newFeature分支有C1,C2两个提交,做变基时合并到了新的newFeature分支,又有了一个提交C3后,如果再做变基的话,还是会依次拷贝C1,C2和C3的。 -
向远端提交的问题。这是因为在开发newFeature分支时,极有可能会提交到远端的某个位置,用于防止本地丢失。这种情况下,很自然的考虑在变基操作后,用push推到远端。不过很遗憾这会失败,原因是远端和本地的提交历史完全变了,具体可以看这篇文章。
解决办法其实非常简单,就是强推。如果该分支没有别人使用,则强推是安全的,否则的话,下面的命令会给出别人也有提交的提示。所以多人协作的分支上做rebase,实在不是明智之举。
git push --force-with-lease
总结
文中提供的两篇文章,让我初步搞懂了rebase的意思,结合自己的实操,总算理解了变基操作的意义和结果。
变基操作和合并操作极其类似,且有自己各自的应用场景。通过变基操作,在当前分支上可以按主干分支的方式排列提交,好像我们从来没有离开过主干分支。妙用rebase,使得代码管理更加得心应手。
变基操作是从中级giter到高级giter的梯子。不过如果误用或乱用的话(在多人协作分支,随意变基导致历史改变和代码覆盖),就会变成项目开发中的陷阱。
网友评论