如何理解git rebase?
在merge PR的过程中,rebase and merge会产生冲突,因此需要补充一下Git rebase的知识点。
- Understanding Rebase (And Merge) in Git
- Merging vs. Rebasing
- webstorm 的 Rebase Current onto selected什么操作?
- rebase and merge 一个Pull request做了什么操作?
Understanding Rebase (And Merge) in Git
merge 是Git中最简单也是最常用的集成change的方法,但是这并不是唯一的一种方式。
Rebase是另外一种可选的但是略微高级的集成方式。
合并提交的case
通常情况下,一个由人类认真创建的commit,是一个有意义的单元:它仅仅包含相关的change并且每个commit都伴随着一个comment。
有一种merge commit可以让所有comments丢失:Git自动创建的commit,并且由Git填充所有的differ change。没有语义,没有主题。当然,这些独立commits的内容是保留的。但是history分成了注释的,分离的有意义的commit,由于这个原因,在一次merge commit中不会保留。
这就是为什么有些人不喜欢merge,喜欢rebase的原因。
Rebase之美
与一笼统把commits塞到一个commit不同,一个rebase会保留原始的commits。
项目的历史是一条单的,直线。没有任何迹象表明它在某个时候拆分出一个分支来。
在一次rebase后,看起来就像development从来没有被拆分成不同的分支。
我们来一步一步拆分一个rebase操作。方案很简单:我们想用rebase集成branch-B到branch-A。
一个rebase之前的方案。
命令很简单:
git rebase branch-B
首先,线条开始分支后,Git将"undo"所有的branch-A上的commits(在共同的祖提交后)。当然,它不会丢弃它们,而是临时将它们存了起来。
其次,它会应用我们想集成的来自branch-B的commits。此使,两个分支是相同的。
最后,branch-A的新commits重新被应用,但是在一个新的位置,在branch-B的后面。(they are rebased)。
结果就是development在一条直线上开发。不是一个commit包含了所有的改变,而是让原始commit结构保持原样。
下面尝试开BranchA,BranchB两个分支,然后基于webstorm的Version Control,观察git rebase操作会不会有上述的变化。
BranchA
BranchB
Rebase BranchA onto branchB
其实就是将branchB的母分支branchA进行了integrate changes,也就是把branchB的2次commit,放在共同的起点与branchA的新commit之间,或者也可以理解成将branchA的新commit,移动到了branchB的2次commits之后。
rebase的是谁,就修改的是谁
onto的是谁,谁就是被rebase的分支的新commits
其实,rebase只做了一件事:更新base branch!(重点!重点!重点!)
而想将谁的更新内容作为新的base branch的提交,就将作为topicBranch。
非常重要的命令。
git checkout baseBranch git rebase topicBranch
再说的通俗一点,其实就是:挑了一个branch,把它的特性拿过来,放在我的新特性之前。
Merging vs. Rebasing
看完上面这篇文章后,并没有搞清楚rebase做了什么操作,所以还是需要多读一些文章。
- 对于初学者来说,git rebase命令就像一个magic voodoo
- merge和rebase都是用来从一个分支到另一个分支integrate changes的,只是方式不同
Merge Option
git checkout feature git merge master
git merge master feature
会有一个'merge commit', 但是merge是非常安全的,不会像rebase有很多陷阱。
但是若master非常活跃,每次merge都会有要给'merge commit',会导致feature的commit history很脏。
Rebase Option
git checkout feature git rebase master
- feature从master tip处开始合并master上的commits
- 重写project的history
- rebase后,project的history更加干净了。没了多余的'merge commit',并且成了一条线。
- rebase 需要遵循Golden Rule of Rebasing,否则会导致灾难性的合作workflow。
- rebase 会丢失掉merge commit,导致看不到之后合并到feature的commit。
灵活一点的Rebasing
- 选择特定的commits移动到新分支,加一个i选项
- fixup某一个提交
git checkout feature git rebase -i master
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Golden Rule of Rebasing
永远不要在public 分支上使用git rebase!
每次使用git rebase前,问自己"有没有人也正在基于这个branch写代码?"若是的话,就老老实实用merge,不要尝试rebase。
若有gitflow的经验,其实就是当你开了一个feature/foo时,若同事也开了一个feature/bar,而且你们是同时基于develop checkout出来的分支,那么当develop有hotfix merge进去时,若你想拉去最新的develop代码,就不能用git rebase,只能用merge,否则会导致同事的develop分支与我们的develop分支不同,而此时她想再与我们保持同步是很复杂的。
Force-Pushing
若想将rebased的master分支推到远程仓库,Git 将会阻止你,因为它与远程的master分支冲突了。但是,你可以force push。
# 这个命令一定要小心使用 git push --force
- 只有100%确定自己在做什么时再force,否则会让团队的人很困惑
- 若是想将某个feature远程分支彻底替换掉,可以这样做。
下面尝试开master,feture两个分支,然后基于webstorm的Version Control,观察git rebase操作会不会有上述的变化。
master
feature
git checkout feature git rebase master # resolve conflict1 git rebase --continue # resolve confict2 git rebase --continue
Rebase feature onto master
webstorm 的 Rebase Current onto selected什么操作?
可以理解成下图这样。
Rebase feature onto master
Current在WebStorm中指右下角的branch,selected一般指的original branch。
rebase and merge 一个Pull request做了什么操作?
相当于:
git checkout feature git rebase master
像下图这样: