译:Git rebase VS. Git merge

原文:Merging vs. Rebasing
git rebase一直是初级程序员想远离的黑魔法,但事实上在开发团队小心的使用该命令可以令我们的工作更轻松。本文将git rebasegit merge做对比,来看看在典型的Git工作流中引入git rebase可以带来什么好处。

概念纵览

首先我们要了解git rebasegit merge作用是相同的,都可以用来将一个分支上的代码合并到另一个分支——只是实现方式不同。
当你使用专门的分支开发feature时,其他团队成员如果继续向master分支提交。就会产生一个分叉的历史,任何使用Git作为协同工具的开发人员应该都很熟悉这样的场景。

一次分叉历史

假设提交到master的代码跟你正在开发的feature相关,为了将新的提交合并到feature分支,你有两个选项:git mergegit rebase

Git merge

最简单的方法就是使用以下命令将master分支合并到feature分支:

git checkout feature
git merge master

或者简化成一行命令:

git merge master feature

该操作会在feature分支上产生一个新的“合并提交”,将两个分支的历史合并在一起,如图示这样:

将master合并到feature分支

git merge很好用因为它是一个非破坏性的操作。当前分支不会被改变。因此避免了git rebase操作可能带来的所有隐患(详见下文)。
然而另一方面,这意味着每一次合并操作都会在feature分支上产生一个额外的“合并提交”。如果master分支更新频繁的话会导致feature分支的历史记录被污染,令其他开发者难以理解项目的历史记录。

Git rebase

合并操作的另一个选择是使用git rebase,例如如下命令,将feature分支rebase到master分支:

git checkout feature
git rebase master

该操作将整个feature分支的记录移到了master分支的头部,将所有的新提交都并入了master分支。但是不同于"合并提交",git rebase通过对原分支上的提交做标记重写了项目的提交记录。

将feature分支rebase到master分支

使用git rebase的最大好处是可以得到更干净的项目记录。首先,它消除了git merge带来的无用的"合并提交"。其次,对比上面的图我们看到,git rebase可以产生完美的线性历史记录——可以方便的使用git loggit bisectgitk追踪提交记录。
但是,在追求干净的提交记录的同时还要权衡两个方面:安全性和可追溯性。如果没有遵循git rebase的最佳实践,重写项目的历史记录可能会毁灭合作工作流。另外,git rebase丢失了合并操作的上下文信息——我们无法得知某一次合并操作具体发生在何时。

交互式rebase

交互式的rebase可以对要移到新分支上的提交记录进行修改,它能够完全控制分支的提交记录,因此比自动rebase功能更强大。通常,在将一个功能分支合并到master分支前,可以用其来整理混乱的提交历史。

-i传入git rebase来进入交互式rebase会话

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

这个列表展现了rebase执行之后分支的提交记录。可以通过修改pick命令或调整条目顺序来改变分支的提交历史。例如,假设第二次提交是为了修复第一次提交中的一个小问题,我们可以通过fixup将这两次提交合并:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

保存并退出该文件后,Git会通过该文件的内容执行rebase。最终项目的提交记录如图所示:

修改提交记录

通过交互式rebase可以忽略不重要的提交令分支历史更容易理解,这一点是git merge无法比拟的。

Git rebase的金律

现在我们理解了什么是git rebase,但最重要的是要知道什么时候不要用它。Git rebase的金律是:永远不要在公有分支上用它。
例如,试想一下如果将master分支rebase到feature分支会发生什么:

对master进行rebase

这个rebase操作将master分支的所有提交移动到了feature分支的头部。但问题是这一切都发生在你本地。其他开发者依然在原master分支上继续工作。因为rebase会导致新的提交,Git会认为你的master分支和其他开发者的出现了分叉。
唯一同步这两个版本master的方式是将它们git merge,但是这样会导致一个额外的合并提交记录和包含相同修改的两次提交(原始master分支和被rebase的master分支)。不用说,这是一个令人困惑的情况。
所以,在执行git rebase之前,总是问问自己,“其他人用这个分支吗?”如果答案是yes,把手从键盘上拿开,考虑使用一种非破坏性的方法来达到同样的目的(例如git revert)。否则,放心的按自己的喜好去重写历史吧。(译注:修改历史提交记录)

Force-Pushing

Rebase过的master分支与远程仓库的master分支会存在冲突,所以Git不允许将rebase过的分支推送到远程仓库。但是可以通过传递--force标签进行强制推送:

# Be very careful with this command!
git push --force

该操作会将远程仓库的master分支替换为rebase过的master分支,这会给团队的其他成员带来困扰。所以,小心使用该命令,除非你知道自己在做什么。
少数几个需要使用强制推送的场景之一是将私有分支推送到远程仓库(例如:以备份为目的)后,对该分支上的提交进行了清理。这就像说,“我不想推之前那版feature分支了,用这个替换吧”。再强调一次,一定要确保没有其他开发人员在使用该分支。

工作流演练

git rebase可以根据团队的需要或多或少的与现存的Git工作流进行整合。这一章,我们来看看在一个feature分支的不同开发阶段,使用git rebase能带来什么好处。
对于任何工作流来说,要使用git rebase的第一步是为每个feature创建一个专门的分支。这种分支策略可以帮助我们安全的使用git rebase

在单独分支上开发功能

整理本地分支

rebase加入到工作流的一个好处是整理本地进行中的功能分支。通过定期执行交互式rebase,可以保证每一次提交(commit)都是有意义的。这可以让我们在写代码时不用担心每次提交是不是都有意义——可以之后通过交互式rebase修复。
调用git rebase时可以传入两种参数:父分支(例如master),或当前分支的前几次提交。在交互式rebase章节中我们看到了传入父分支的用法。当我们需要整理最后几次提交时后者比较有用。例如,以下命令为最后3次提交开启了交互式rebase。

git checkout feature
git rebase -i HEAD~3

通过指定HEAD~3,我们并没有移动分支——只是重写了最后三次提交。注意不包含其他提交。

Rebasing onto HEAD~3

如果想用这个方法重写整个分支,git merge-base命令可以用来追寻feature分支的original base。以下命令返回original base的commitID,可以将该commitID传给git rebase

git merge-base feature master

可以通过这种交互式rebase的用法将git rebase引入到你日常的工作流中,因为它只会影响本地分支。其他开发者仅仅会在你开发完成之后看到一个提交记录干净的,易于追溯的feature分支。
但是再一次强调,这种用法只针对本地私有分支。如果你在和其他开发者在某一个feature分支上进行合作开发,那么这个分支是共有的,请不要试图去重新提交历史。
git merge无法做到清理本地提交历史。

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

推荐阅读更多精彩内容

  • Git是目前最流行的版本管理系统,也是最先进的分布式版本控制系统(distributed version cont...
    pro648阅读 5,664评论 1 17
  • 天没有大亮的清晨总是让人莫名的感伤,街道清冷,小九一个人走在路上,看着路灯把影子拉得老长,一阵阵车子压马路的声音由...
    夜雪舞回风阅读 450评论 2 5
  • 1.Context本身是一个纯的abstract类,ContextWrapper是对Context的一个包装而已,...
    shannonxizhang阅读 853评论 0 0
  • 千万不要忘记 我们飞翔的越高 我们在那些 不能飞翔的人眼中的 形象越是渺小 其实人跟树是一样的 越是向往高处的阳光...
    君子温和阅读 259评论 0 0