Merge与Rebase比较

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

原文地址:https://www.atlassian.com/git/tutorials/merging-vs-rebasing

  关于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的好处。

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

时间: 2024-10-31 20:34:59

Merge与Rebase比较的相关文章

Git Step by Step – (8) Git的merge和rebase

前面一篇文章中提到了"git pull"等价于"git fetch"加上"git merge",然后还提到了pull命令支持rebase模式,这篇文章就介绍一下merge和rebase之间有什么差别. 由于我们主要是想看看merge跟rebase之间的区别,这里就是用本地仓库的分支进行演示了. merge 其实在介绍分支的那篇文章中已经介绍过了一些分支merge的内容,这里就进行一些补充和总结. 下面我们基于本地一个仓库开始介绍,当前仓库的分支情

merge vs rebase

git rebase和git merge设计的初衷是解决相同的一件事, 即把一个分支合并到另外一个分支--只是他们两个处理的方式非常不一样. 当你在一个特定的分支开发新功能, 团队的其它成员在master分支工作提交了新的commit. 这个项目的历史就会分叉. 现在假设master中的这个新的commit和你在其它分支中开发的新功能相关, 你想在你的feature分支中包含这个commit, 你有两种选择:merge和rebase. merge 最简单的办法就是merge master分支到你

git两种合并方法 比较merge和rebase

18:01 2015/11/18git两种合并方法 比较merge和rebase其实很简单,就是合并后每个commit提交的id记录的顺序而已注意:重要的是如果公司用了grrit,grrit不允许用merge,所以好像都是用rebase却别讲解,比如:在服务器上的develop分支有多人在开发,你们同时clone或pull下来最新代码,但是开发进度不一样,你在开发一个任务的时候其他人提交了编号为1,2的commit和push,你现在开发完了也要提交,你的提交编号是3,4(注意:编号不代表顺序现实

Merge与Rebase冲突的解决

出现冲突的一种场景是两个用户修改了同一文件的同一区域. 1.切换到分支chengdr,在newTxt1.txt中输入文字“Hello Word !!!”-> Add to Index -> Commit -> Push 2.切换到分支chengdr2,在newTxt1.txt中输入文字“hello word”-> Add to Index -> Commit 3.执行fetch操作 4.执行chengdr2分支与服务器上chengdr分支的Merge操作,然后出现以下错误 文

Merge、Rebase

一.Merge 1.在Git Repositories视图中,右键chengdr2->Merge...,在弹出的对话框中选择它是要与哪个分支合并(这里是origin/chengdr) 2.点击Merge按钮之后,出现下图则表示Merge成功 3.此时发现在chengd2分支下,不仅有newTxt2.txt,还有newTxt1.txt 4.再次对分支chengdr2执行Push操作,出现下图即表示Push成功 二.Rebase 1.切换到chengdr分支,新增newTxt3文件,同样是Add t

使用Git中的Merge与Rebase与开源项目同步代码

基于开源项目的开发有两种主要工作模式.模式1是在从开源项目中拉出一个分支,在这个分支中开发新feature,完成后合并到upstream中.适用于本身是开源项目的developer.模式2是从开源项目中拉出分支后独立发展,但定期从upstream拉更新(如重要版本升级时).无论是哪种,都会面临本地分支与upstream同步代码的问题.为此,git主要提供了两种方式:一种是merge, 一种是rebase.下面通过例子简单过一下它们的基本流程. 假设开源项目的git地址为git://xxx.org

git代码合并:Merge、Rebase的选择

代码合并:Merge.Rebase的选择 Zhongyi Tong edited this page on Dec 7, 2015 · 3 revisions Pages 19 Home 2.1 快速指南 2.2 创建代码仓库 2.3 保存你的更改 2.4 检查仓库状态 2.5 检出之前的提交 2.6 回滚错误的修改 2.7 重写项目历史 3.2 保持同步 3.3 创建Pull Request 3.4 使用分支 3.5 常见工作流比较 4.1 图解Git命令 5.1 代码合并:Merge.Reb

git merge 和 rebase 区别

git pull  超级不推荐使用git pull 有坑,谨慎使用,pull底层是merge git pull 是 git fetch + git merge FETCH_HEAD 的缩写.所以,默认情况下,git pull就是先fetch,然后执行merge 操作,如果加 —rebase 参数,就是使用git rebase 代替git merge. merge 和 rebase merge 是合并的意思,rebase是复位基底的意思. 现在我们有这样的两个分支,test和master,提交如下

Git知识总览(五) Git中的merge、rebase、cherry-pick以及交互式rebase

上篇博客聊了<git分支管理之rebase 以及 cherry-pick相关操作>本篇博客我们就以Learning Git中的关卡进行展开.下方列举了LearningGit中的 merge.rebase.reset.revert.cherry-pick 以及交互式rebase相关关卡的操作以及对应的解析.后边在聊交互式rebase操作是,不单单给出了LearningGit中的内容,而且给出了真正的Git分支在交互式rebase操作时的具体案例. learngitbranching的地址为:ht

git中的merge与rebase

之前一直对git的merge与rebase很困惑,而且一般也只使用merge而不是使用rebase.今天受高人指点理清了两者的区别. 首先对于两者而言,他们的结果是一样的,差异在于合并的方式(产生的结果就在于log中看起来会让人感觉到有问题,也就是两者的commit记录会有很大差异) merge的合并方式: 使用rebase的话: 补充点: pull/fetch的区别: fetch只是单纯的拉取代码. pull的实际操作:fetch-merge.所以当远程代码有更新时,本地pull后会可能需要处