码迷,mamicode.com
首页 > 其他好文 > 详细

Merge与Rebase比较

时间:2019-09-28 17:43:27      阅读:119      评论:0      收藏:0      [点我收藏+]

标签:MTA   odk   The   除了   pemu   线性   pki   情况下   远程   

 

看到国外论坛上这篇文章讲的很好,翻译过来学习学习

 
  关于git rebase的指令一直有个说法,这是一个神奇的git指令,但是新手要远离它。实际上当小心使用它的时候,可以让一个开发团队的工作简单得多。在这篇文章中,我们将比较git rebase与git merge指令并寻找一切可以将rebasing整合到典型的git工作流的机会。

概念性回顾

  关于git rebase需要理解的第一点是它解决的是和git merge一样的问题。这两个指令都是设计来将代码的变化从一个分支整合到另一个分支,但是两者用了非常不同的方法来做这件事。
  回想一下当你在一个独立的feature分支上时,另一个团队成员更新了master分支,这时会发生什么。对于任何将git作为一个协作工作的人来说,很显然这件事会导致分岔的history出现。
技术图片
  现在,假设master分支中的新commits与你正在做的需求息息相关。想把新的commits整合到你的feature分支上,你有两种选择:merging或者rebasing。
 

Merge 选项

  最简单的选择就是把master分支merge到feature分支,指令如下:  
git checkout feature
git merge master

  或者可以将上面两条整合到一条指令:

git merge feature master

  这会在feature分支创建一个新的merge commit,将所有分支的历史都连结到一起,会给你一个如下所示的分支结构:

技术图片

  Merging很优秀,因为它是一种非破坏性的操作。现存的分支不会以任何形式被改变,这一点避免了rebasing所有的潜在陷阱。

  在另一方面,这同样意味着,每次当你需要整合上游的改变时,feature分支会有一个额外的merge commit。如果master分支很活跃,这会将你feature分支的历史污染的很严重。当需要用到git log的时候,这些历史会使其他开发者很难理解项目的历史。

Rebase 选项

  作为merging的替代品,你也可以以下指令在master分支上rebase feature分支:
git checkout feature
git rebase master

  这段指令将整个feature分支挪到了master分支的后面,有效地将所有新commits整合到master分支中。但是,和使用merge commit不同的是,rebasing通过为每个原始分支中的commit创建全新的commit,重写了项目的历史。

技术图片

  rebasing的主要好处是你可以得到一个更干净的项目历史。首先,它消除了git merge引发的不必要的merge commit。其次,你可以从上面的图像中看出,rebasing还造就了完美的线性项目历史。你可以避开任何的分岔顺着feature的流程走到项目的开端。这使你用git log,git bisect,gitk来导航项目变的极其简单。
  但是对于这个崭新的commit历史来说有两件需要权衡的事:安全性和可追溯性。如果你不遵循rebasing的黄金定律,重写项目历史可能会是你协作工作流程的潜在灾难。另外不那么重要的是,rebasing失去了merge commit提供的context,你不能看到上游改变时什么时候被合并到feature分支中。

交互性 Rebasing

  当commits被移到新的分支时,交互性Rebasing给了你机会去改变它们。这一点甚至比自动化的rebase更加给力,因为它提供了对于分支commit历史的完全控制。很典型的情景是,这一点被用于在将一个feature分支合并到master分支前,将混乱的历史清理干净。
  开始一个交互性的rebasing,加入一个i的字段
git checkout feature
git rebase -i master
  这会打开一个文本编辑器,将所有需要移动的commit都列出来:
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
  这个列表就决定了之后合并后的branch的log会长什么样。改变pick指令或者重新排列这些条目,你就可以将分支的历史做成你想要的样子。举个例子,如果第二个commit修改了第一个commit中的一个小问题,你可以通过fixup指令将他们合并到一个单独的commit:
pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
  当你保存并关掉这个文件时,git会根据你的指令来执行rebase。然后项目历史就会看起来如下:

      技术图片

  像上面这样忽略掉一些非必要的commit可以使你的分支历史看起来好理解的多。这是git merge不太能做到的。

Rebasing的黄金法则

  当你了解rebasing是什么之后,最重要的事情就是去了解什么时候不能用它。git rebase的黄金法则就是永远不要在公共分支上用这个指令。
  举个例子,想一下当你将master分支rebase到你的feature分支上,会发生什么:
技术图片
  rebase指令会将所有master分支的commit接到feature分支的末端。问题是这件事仅仅出现在你的仓库中。其他的所有开发者仍然在原来的master分支上工作。因为rebasing创造了新的commit,git会认为你的master分支的历史与其他人的会发生分岔。
  唯一使两个master分支同步的办法是merge,这会导致一个额外的merge commit和两堆含有相同改变的commit(一堆是原始的,一堆是从rebase分支合并过来的)。这真的是个很令人费解的情景。
  所以,在你执行git rebase前,多考虑考虑团队其他人。尽量使用非破坏性的方式去改变,比如git revert。
 

强制push

  如果你试图将rebase过的master分支push到远程仓库,git将会阻止你这么做。因为这会和远程的master分支发生冲突。但是,你可以通过传递一个--force指令强制push:
# Be very careful with this command!
git push --force
  这会重写远程的master分支来匹配你仓库中rebase过的分支,这会让团队的其他人非常的迷惑。所以,只有当你真的知道自己在做什么的时候,再小心使用这条指令。
  为数不多你必须使用这条指令的情景是当你在push了一个私有的feature分支到远程仓库上,你需要做一个本地的清理并将还原提交到远程仓库。同样要注意的是,没有人在feature分支的原始版本上改动是很重要的。

工作流预排

  rebasing可以以尽可能小影响的方式合成到你已有的git工作流中,这样会让你的团队很舒服。这这一部分,我们会看看rebasing在多种情景下提供的优势。
  任何工作流中利用git rebase的第一步是去为每个feature创建一个独立的分支。这会给你必要的分支结构来安全地利用rebasing:
技术图片

本地清理

  将rebasing合成到工作流中最好的方式之一是清除本地正在运行中的feature。定期执行交互性的rebase,你可以确信你的feature中每个commit都是专一且有意义的。这会让你不用担心在写代码的时候会破坏掉commit的结构。
  当使用git rebase的时候,你有两个新的base可以选择:一个是feature的父分支,或者你的feature中一个早前的commit。我们在交互性rebasing章节有展示过一个第一种选择的例子。后面的一种选择当你只需要修改最后的几个commit时是很不错的。举个例子,下面的指令开启了一个只有三个commit的可交互性rebase:
git checkout feature
git rebase -i HEAD~3
  通过指定HEAD~3作为新的base,你实际上并没有移动分支,而是交互性的重写了3个随后的commit。注意这不会将上游的改变融入到feature分支中。
技术图片
  如果你想用这个方法重写整个feature,git merge指令在找到feature分支的原始base上是有效的。接下来会返回原始base的commit ID,你可以利用这些ID来git rebase:
git merge-base feature master
  这种交互性的使用方式是一种将git rebase融入到你的工作流很好的方式,因为它只会改变本地的分支。其他的开发者只会看到你完成了一个有着清爽,简单易懂的feature分支历史的项目。
  再一次说明的是,这只会在私有的feature分支上起作用,如果你通过相同的feature分支与其他人协作开发。公共的feature分支历史改写是不被允许的。对于使用交互性rebase清理本地commit,git merge的选项是行不通的。

将上游的改变融入一个feature

  在概念回顾部分,我们讲述了如果利用git merge或git rebase将上游master分支中的变化融入到feature分支中。merging是一个安全的选择,它保留了你仓库完整的历史,而rebasing通过将feature分支移动到master的末端来创造一个线性的历史。
  git rebase的这种用法类似于本地的清理,但是在这个过程中,它将这些上游的commit融合了。要记得在远程分支上rebase而非是在master分支上rebase,当你与其他的开发者在相同的feature分支上开发时,你需要将他们的变化融入到你的仓库中。
技术图片
  你可以解决这个分岔正如同你可以将master上游的变化融入进来,或者将john/feature merge到你的本地feature分支,或者将你的本地feature分支rebase到john/feature的末端。
技术图片
技术图片
  注意这个rebase没有违反rebasing的黄金法则,因为只有你本地的feature的commit被移动了,前面任何事都没有被改变。说起来就是把你的改变加在John已经做好的部分上。在大多数情况下,这比用merge commit来同步远程分支要直观的多。
  git pull默认使用了一个merge指令,但是你可以强行将远程分支利用--rebase选项进行强行融合。

利用Pull Request复审一个feature

  如果你将pull request作为你代码复审流程的一部分,你需要避免在创建PR之后使用git rebase指令。当你一开始用PR,其他的开发者就会看到你的commit,意味着这是一个公共的分支。重写它的历史对于git来说是不可能的,而且你的团队成员可以追踪到任何加在feature后的commit。
  将其他开发者的代码改变必须利用git merge而非git rebase融入。因此,在提交PR之前经常使用交互性rebase清理你的代码是一个好主意。

与审批过的feature交互

  当一个feature被你的团队审批过后,你可以在使用git merge将feature融入到主代码base前,选择将feature rebase到master分支的末端。
  这和将上游改变融入到feature分支有些相似,但是你不被允许重写master分支中的commit,你必须使用git merge去融入feature。然后通过在merge前使用rebase,你可以确信merge是快速推进的,创建一个完美的线性历史。这也给了你在PR的时候将后续的commit去除的机会。
技术图片
技术图片
技术图片
  如果你对git rebase感觉不是很舒服,你也可以在暂时的分支中使用git rebase。在这种方式下,如果你意外弄乱了你的feature的历史,你可以切换到原始分支再试一次,如下:
git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch

总结

  以上就是在开始使用rebase分支之前你需要了解的东西。如果你更倾向于一个干净,线性的历史,不被非必要的merge commit拖累,你必须在从另一个分支融入变化时采用git rebase而非git merge。
  在另一方面,如果你想保存你项目的完整历史并且避免重写公共commit的风险,你可以依旧坚持使用git merge。两种选择都是完美有效的,但是至少你现在可以选择去衡量git rebase的好处。
 

Merge与Rebase比较

标签:MTA   odk   The   除了   pemu   线性   pki   情况下   远程   

原文地址:https://www.cnblogs.com/dieFreiheit/p/11603711.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!