Git 301 - Pull with Rebase

一般情况下,我们用 git pull 完成 git fetch 从远端去获取数据,然后调用 git merge 来将远程的改变应用到本地的分支上。但有时,这样会带来一个问题。

如图,我们有一个远端分支,其提交记录如下,在 update history 之后, Nick做了多次提交:

git pull

而在本地,最近一次与服和器同步发生在 update history 之后,Nick 的所有提交都没有同步到本地。而本地也做了多次 commit,此时,历史记录看起来如图所示:

git pull

这时,我们使用 git pull 来将远端的内容同步到本地。

1
2
3
4
5
6
7
8
9
$ git pull origin master
$ git pull mojombo master
From git://github.com/mojombo/jekyll
* branch master -> FETCH_HEAD
Merge made by recursive.
VERSION.yml | 2 +-
jekyll.gemspec | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)

git pull

如图所示,这样会产生一个非线性的提交记录,一个从本地master分支到主master分支的线和一个新的合并记录,对于一个小的改动,该方式有时没有意义且会不利于以后的Review。

而我们更希望的是一个线性的提交记录,将自己的提交看起来像是在同一个分支里。这时可以使用rebase来做这些合并工作,这样会使得整个历史提交记录变成线性且更加清晰。当你pull代码时使用 --rebase参数,就像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git reset --hard origin/master
HEAD is now at 60709da Removing some bad formatting in the README
$ git pull --rebase origin master
From git://github.com/mojombo/jekyll
* branch master -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: Making rake test happy on 1.8.7
Applying: Starting on yaml tags
Applying: Adding support for setting post tags...
Applying: Added publish flag to posts, not preventing...
Applying: Making sure that posts flagged as published...
Applying: Adding explanations for new YAML front matter...
Applying: Removing some bad formatting in the README

git pull

图中可以看到,所有的本地提交都被rebase进了repo,并且SHA-1值也变化了,也就是说,rebase重新把之前的commits一个个的应用到了分支,之前本地所有的改动,都被放在远程分支之后。

rebase的作用是,把本地上所有从上次pull之后的commit先暂存起来,然后将状态发回到上次pull时的场景,然后从远程pull最新的代码,最后,将所有暂存的commit记录,一个个再应用回本地repo。

rebase时如果有confilct,则需要在每次apply时都支解决冲突。而merge只是用最后一次版本对比,所以只用解决冲突一次。

所以我们推荐的是从远端pull代码,同步分支时,永远记得加上rebase,为了方便,可以通过设置.git/config来使得git pull时,自动加上rebase。

1
2
3
4
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true

或者直接在 ~/.gitconfig 加入下面的配置,让所有已被 tracked 的分支都使用该配置:

1
2
[branch]
autosetuprebase = always

本文所有图片及源码都来自于 gitready.com