GIT使用 rebase 和 merge 的正确姿势(转)

GIT使用 rebase 和 merge 的正确姿势

背景

使用GIT这么久了从来没有深层次的研究过,一般情况下,只要会用pull,commit,push等几个基本提交命令就可以了,公司的项目分支管理这部分操作一直都是我负责,对于分支的合并我一直都使用merge操作,也知道还有一个rebase,但是一直不会用,百度了很多,说的基本都差不多,按照步骤在公司项目里操作,简直就是噩梦,只要rebase就出现噩梦般的冲突,所以一直不敢用,今天自己捣腾了一番终于领略到一些,不多说直接进入干货。

先来两张合理使用rebasemerge和只使用merge的对比图

使用 rebase

image

使用 merge

image

使用 rebasemerge 的基本原则:

  1. 下游分支更新上游分支内容的时候使用 rebase
  2. 上游分支合并下游分支内容的时候使用 merge
  3. 更新当前分支的内容时一定要使用 --rebase 参数

例如现有上游分支 master,基于 master 分支拉出来一个开发分支 dev,在 dev 上开发了一段时间后要把 master 分支提交的新内容更新到 dev 分支,此时切换到 dev 分支,使用 git rebase master

等 dev 分支开发完成了之后,要合并到上游分支 master 上的时候,切换到 master 分支,使用 git merge dev

一、创建两个GIT项目,project1project2,同时分别加入三个文件并提交master分支

$ git clone git@gitlab.xpaas.lenovo.com:baiyl3/project1.git
$ cd project1
$ touch file1 file2 file3
$ git add .
$ git commit -m '在项目一中初始化三个代码文件'
$ git push -u origin master

$ git clone git@gitlab.xpaas.lenovo.com:baiyl3/project2.git
$ cd project2
$ touch file1 file2 file3
$ git add .
$ git commit -m '在项目二中初始化三个代码文件'
$ git push -u origin master

从代码提交时间轴图看两个项目现在的状态都一致

image
image

二、分别给两个项目创建三个分支 B1,B2,B3 (* 对应数字)

$ git checkout -b B*
$ git push origin B*
$ git branch -a
  B1
  B2
* B3
  master
  remotes/origin/B1
  remotes/origin/B2
  remotes/origin/B3
  remotes/origin/master
$ git logs --graph
* 891d1ed<baiyl3> - (HEAD -> B3, origin/master, origin/B3, origin/B2, origin/B1, master, B2, B1) 在项目二中初始化三个代码文件 (23 minutes ago)  
  file1
  file2
  file3

git log 中我们看到 B1,B2,B3 都基于master最新提交点拉出来的三个新分支,如下图从时间轴也可以看出

image
image

三、现在我们分别切换分支到 B1,B2,B3,并修改对应的文件 file1,file2,file3,最后切换到 mastet 分支添加一个 README.md 文件,然后再看时间轴:

image

从上图的结果可以看出,B1, B2, B3, master 四个分支分别在不同的时间点做了代码提交,那么最后一次 master 上做的修改在 B1, B2, B3 三个分支上肯定没有

四、此时在这三个分支上开发的同学发现他们要做的功能要在 master 最新的基础上开发,或者他们也想要 master 上最新的内容,重点来了,现在我们怎么办?方法有两种,一种是使用 rebase ,另一种是使用 merge,我们分别在 project1 和 project2 两个项目上使用这两种方式解决这个问题

在项目 project1 使用 rebase

$ cd project1
$ git checkout B1
$ git pull origin B1 --rebase
From gitlab.xpaas.lenovo.com:baiyl3/project1
 * branch            B1         -> FETCH_HEAD
Already up-to-date.
Current branch B1 is up to date.

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: FILE1 第一次修改

$ git push origin B1
To gitlab.xpaas.lenovo.com:baiyl3/project1.git
 ! [rejected]        B1 -> B1 (non-fast-forward)
error: failed to push some refs to 'git@gitlab.xpaas.lenovo.com:baiyl3/project1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

$ git push origin B1 --force
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 328 bytes | 328.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: 
remote: To create a merge request for B1, visit:
remote:   http://gitlab.xpaas.lenovo.com/baiyl3/project1/merge_requests/new?merge_request%5Bsource_branch%5D=B1
remote: 
To gitlab.xpaas.lenovo.com:baiyl3/project1.git
 + b3052a0...00032a7 B1 -> B1 (forced update)

$ ls
README.md   file1       file2       file3

image

在上面的过程中,更新代码我使用的是 git pull origin B1 --rebase 而不是 git pull origin B1 这也是平时使用 rebase 注意的一点,git pull 这条命令默认使用了 --merge 的方式更新代码,如果你不指定用 --rebase,有的时候就会发现日志里有这样的一次提交 Merge branch 'dev' of gitlab.xpaas.lenovo.com:liuyy23/lenovo-mbg into dev 什么?自己分支合并到了自己分支,显然这个没有什么必要,而且在时间轴上也不好看,平白无故多了一条线出来,对于强迫症的我来说看着就难受。。。

还有就是使用 rebase 之后,如果直接使用 git push origin B1 发现是不好使的,提示也说明了提交失败的原因,我个人是这么理解的,使用 rebase 之后,master分支上比B1分支上多的修改,直接“插入”到了B1分支修改的内容之后,也就是 master 分支的修改在 B1 分支上重演了一遍,相对远程 B1 分支而言,本地仓库的B1分支的“基底”已经变化了,直接 push 是不行的,所以确保没有问题的情况下必须使用 --force 参数才能提交,这也就是官方对 rebase 作为 “变基” 的解释(个人观点)。

接下来我们接着在 project2 项目上使用 merge 操作

$ cd project
$ git pull origin B1
From gitlab.xpaas.lenovo.com:baiyl3/project2
 * branch            B1         -> FETCH_HEAD
Already up-to-date.

$ git merge master
Merge made by the 'recursive' strategy.
 README.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md

$ git push origin B1
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 278 bytes | 278.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: 
remote: To create a merge request for B1, visit:
remote:   http://gitlab.xpaas.lenovo.com/baiyl3/project2/merge_requests/new?merge_request%5Bsource_branch%5D=B1
remote: 
To gitlab.xpaas.lenovo.com:baiyl3/project2.git
   d3ea69c..e040c7b  B1 -> B1

ls
README.md   file1       file2       file3

image

可以看到 merge 之后,在 B1 分支上多出一条合并的log

此时,我们的 B1 分支开发完成了,要合并到 master 分支,根据基本原则,在 master 分支上都使用 git merge B1 就可以合并,看下图结果:

$ git checkout master
$ git merge B1
Updating c782e83..00032a7
Fast-forward
 file1 | 1 +
 1 file changed, 1 insertion(+)

$ git push origin master

image
image

接下来对 B2,B3 分别在 project1 和 project2 上做相同的操作,我们看结果:

image
image

再看看命令行下log的情况

$ git logs --graph
* 5826260<baiyl3> - (HEAD -> master, origin/master, origin/B3, B3) FILE3 第一次修改 (6 minutes ago)| 
| file3

* cffcc9a<baiyl3> - (origin/B2, B2) FILE2 第一次修改 (8 minutes ago)| 
| file2

* 00032a7<baiyl3> - (origin/B1, B1) FILE1 第一次修改 (87 minutes ago)| 
| file1

* c782e83<baiyl3> - 添加README.md文件 (2 hours ago)| 
| README.md

* b783e0a<baiyl3> - 在项目一中初始化三个代码文件 (3 hours ago)  
  file1
  file2
  file3
git logs --graph
*   bc3f385<baiyl3> - (HEAD -> master, origin/master, origin/B3, B3) Merge branch 'master' into B3 (4 minutes ago)
|\  
| *   64b4f3d<baiyl3> - (origin/B2, B2) Merge branch 'master' into B2 (5 minutes ago)
| |\  
| | *   e040c7b<baiyl3> - (origin/B1, B1) Merge branch 'master' into B1 (35 minutes ago)
| | |\  
| | | * 2cedfcb<baiyl3> - 添加README.md文件 (2 hours ago)| | | | 
| | | | README.md

| | * | d3ea69c<baiyl3> - FILE1 第一次修改 (2 hours ago)
| | |/  | | |   
| | |   file1

| * | 5975eae<baiyl3> - FILE2 第一次修改 (2 hours ago)
| |/  | |   
| |   file2

* | 37ec6de<baiyl3> - FILE3 第一次修改 (2 hours ago)
|/  |   
|   file3

* 891d1ed<baiyl3> - 在项目二中初始化三个代码文件 (3 hours ago)  
  file1
  file2
  file3

使用 rebase 就感觉所有人都在同一条直线上开发一样,很干净的log,看着很舒服,而一直使用 merge 的log看起来就很乱,我这只是4个分支的例子,我们项目每周基本都是十几个分支,真的是看起来乱入一团哇。。。

这个例子中的操作都没有出现不同分支修改同一个文件导致冲突的情况,实际开发中这种情况非常多,rebase 的时候出现冲突后 git 也会列出来哪些文件冲突了,等你解决冲突之后使用 git rebase --continue 就会继续处理,所以为了避免这种冲突太多,而且不好解决,我们项目中基本都是一个需求就一个分支,而且开发过程中要随时更新上游分支的内容下来,确保在最新的代码基础上开发,这也是 GIT 推荐的方式,尽可能多建分支开发,而不是在一个开发分支上很多人操作,如果是这种情况建议不要用 rebase,另外负责分支合并的人在合并下游分支代码的时候要确保你这个上游分支不要在这段时间内提交代码,有一个方法就是把上游分支 设置 protect

五、注意事项

  1. 更新当前分支代码的时候一定要使用 git pull origin xxx --rebase
  2. 合并代码的时候按照最新分支优先合并为原则
  3. 要经常从上游分支更新代码,如果长时间不更新上游分支代码容易出现大量冲突

Memo

本文转载至 https://zhuanlan.zhihu.com/p/34197548

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352