FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

如何理解git rebase? #112

Open FrankKai opened 5 years ago

FrankKai commented 5 years ago

在merge PR的过程中,rebase and merge会产生冲突,因此需要补充一下Git rebase的知识点。

FrankKai commented 5 years ago

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。 项目的历史是一条单的,直线。没有任何迹象表明它在某个时候拆分出一个分支来。 image 在一次rebase后,看起来就像development从来没有被拆分成不同的分支。

我们来一步一步拆分一个rebase操作。方案很简单:我们想用rebase集成branch-B到branch-A。 image 一个rebase之前的方案。

命令很简单:

git rebase branch-B

首先,线条开始分支后,Git将"undo"所有的branch-A上的commits(在共同的祖提交后)。当然,它不会丢弃它们,而是临时将它们存了起来。 image

其次,它会应用我们想集成的来自branch-B的commits。此使,两个分支是相同的。 image

最后,branch-A的新commits重新被应用,但是在一个新的位置,在branch-B的后面。(they are rebased)。 结果就是development在一条直线上开发。不是一个commit包含了所有的改变,而是让原始commit结构保持原样。 image

下面尝试开BranchA,BranchB两个分支,然后基于webstorm的Version Control,观察git rebase操作会不会有上述的变化。

BranchA image

BranchB image

Rebase BranchA onto branchB image 其实就是将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,把它的特性拿过来,放在我的新特性之前。

FrankKai commented 5 years ago

Merging vs. Rebasing

看完上面这篇文章后,并没有搞清楚rebase做了什么操作,所以还是需要多读一些文章。

image

Merge Option

git checkout feature
git merge master
git merge master feature

会有一个'merge commit', 但是merge是非常安全的,不会像rebase有很多陷阱。 但是若master非常活跃,每次merge都会有要给'merge commit',会导致feature的commit history很脏。 image

Rebase Option

git checkout feature
git rebase master

灵活一点的Rebasing

Golden Rule of Rebasing

永远不要在public 分支上使用git rebase! image 每次使用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

下面尝试开master,feture两个分支,然后基于webstorm的Version Control,观察git rebase操作会不会有上述的变化。

master image

feature image

git checkout feature
git rebase master
# resolve conflict1
git rebase --continue
# resolve confict2
git rebase --continue

Rebase feature onto master image

FrankKai commented 5 years ago

webstorm 的 Rebase Current onto selected什么操作?

可以理解成下图这样。

Rebase feature onto master

image

image

Current在WebStorm中指右下角的branch,selected一般指的original branch。

FrankKai commented 5 years ago

rebase and merge 一个Pull request做了什么操作?

image 相当于:

git checkout feature
git rebase master

像下图这样: image

image

FrankKai commented 4 years ago

实际工作中的rebase

一次完整的rebase流程

  1. git checkout feature
  2. git rebase master
  3. resolve conflicts
  4. git add .
  5. git rebase --continue

如果rebase中途出现问题,可以使用git rebase --abort恢复。

git rebase的目的是保持当前feature分支与master主分支同步的另一种方式,虽然与merge的效果相同,但是比merge更加简洁高效。

为什么说rebase比merge更加简洁高效呢?

实际工作中的一个常见场景:在我们的feature开发期间,master上可能发布了很多release,修复了很多hotfix。而且可能刚好影响到了我们目前开发的feature。此时我们需要将feature的代码与master保持同步。

同步有几种方法:自己写一遍;merge;rebase。