【译文】Git merge 和 Git rebase比较

原创: 胡江华 胡同学和朋友们的成长日记 2017-03-22

git rebase 这个命令经常被人认为是一种Git巫术,初学者应该避而远之。但如果使用得当的话,它能给你的团队开发省去太多烦恼。在这篇文章中,我们会比较git rebase和类似的git merge命令,找到Git工作流中rebase的所有用法。

概述

你要知道的第一件事是,git rebase 和git merge 做的事其实是一样的。它们都被设计来将一个分支的更改并入另一个分支,只不过他们实现方式有些不同。

想象一下,你刚创建了一个专门的分支开发新功能,然后团队中另一个成员在master分支上添加了新的提交。这就会造成提交历史被分叉(Fork)了一份,对于用Git来协作的开发者来说这样的情况应该很常见。

现在,如果master中新的提交和你的工作功能开发是相关的。为了将新的提交并入你的分支,你有两个选项:merge或rebase。

Merge选项

将master分支合并到feature分支最简单的办法就是用下面这些命令:

git checkout feature
git merge master

或者,你也可以把它们压减成一行:

git merge master feature

这会在功能(feature)分支中创建一个新的合并提交(merge commit)将两个分支的历史链接一起。你会得到下面这样的分支结构:

Merge好在它是一个安全的操作。现有的分支不会被更改,避免了rebase潜在的缺点(后面会说)。

另一方面,这同样意味着每次合并上游更改时feature分支都会引入一个无关的合并提交。如果master非常活跃的话,这或多或少会污染你的分支历史。虽然高级的git log选项可以减轻这个问题,但对于开发者来说,还是会增加理解项目历史的难度。

Rebase 选项

作为merge的替代选择,你可以像下面这样将feature分支并入master分支:

git checkout feature
git rebase master

它会有效地把所有master分支上新的提交并入过来,并把整个feature分支移动到master分支的后面。但是,rebase会重写项目历史,它并不会带来合并提交,但会在原分支上为每一个提交创建一个新的提交。

rebase最大的好处是你的项目提交历史会非常整洁。首先,它不像git merge 那样引入不必要的合并提交。其次,如上图所示,rebase导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而没有任何的分叉。这让你更容易使用git log 、git bisect 和gitk 来查看项目历史。

不过,这种简单的提交历史会带来两个问题:安全性和可跟踪性。如果你违反了Rebase黄金法则,重写项目历史可能会给你的开发工作流带来灾难性的影响。此外,rebase操作不回增加合并提交——因此你看不到什么时候上游分支合进来feature分支的。

交互式的rebase

交互式的rebase允许你更改并入新分支的提交。这比自动的rebase更加强大,因为它提供了对分支上提交历史完整的控制。一般来说,这被用于将feature分支并入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,项目历史看上去会是这样:

忽略不重要的提交会让你的feature分支的历史更清晰易读。这是git merge 做不到的。

Rebase的黄金法则

当你理解rebase是什么的时候,最重要的就是什么时候 不能 用rebase。git rebase 的黄金法则:绝不要在公共的分支上使用它。

比如说,如果你把master分支rebase到你的feature分支上会发生什么:

这次rebase将master分支上的所有提交都移到了feature分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的master上工作。因为rebase引起了新的提交,Git会认为你的master分支和其他人的master已经分叉了。

同步两个master分支的唯一办法是把它们merge到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。

所以,在你运行git rebase 之前,一定要问问你自己“有没有别人正在这个分支上工作?”。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如git revert)来提交你的更改。不然的话,你可以随心所欲地重写历史。

强制推送

如果你想把rebase之后的master分支推送到远程仓库,Git会阻止你这么做,因为两个分支包含冲突。但你可以传入–force 标记来强行推送。就像下面一样:

# 小心使用这个命令!
git push --force

它会重写远程的master分支来匹配你仓库中rebase之后的master分支,对于团队中其他成员来说这看上去很诡异。所以,务必小心这个命令,只有当你知道你在做什么的时候再使用。

仅有的几个强制推送的使用场景之一是,当你在想向远程仓库推送了一个私有分支之后,执行了一个本地的清理(比如说为了回滚)。这就像是在说“哦,其实我并不想推送之前那个feature分支的。用我现在的版本替换掉吧。”同样,你要注意没有别人正在这个feature分支上工作。

应用到开发工作流

rebase可以或多或少应用在你们团队的Git工作流中。在这一节中,我们来看看在feature分支开发的各个阶段中,rebase有哪些好处。

在工作流中平衡git rebase负面作用的第一步是创建一个功能开发的专门feature分支。这让你拥有安全使用rebase的分支结构:

本地清理

在你工作流中使用rebase最好的用法之一就是清理本地正在开发的分支。隔一段时间执行一次交互式rebase,你可以保证你feature分支中的每一个提交都是具体而且有意义的。你在写代码时不用担心造成松散的提交——你可以使用rebase解决这个问题。

调用git rebase 的时候,你有两个选项要去考虑:上游分支(比如master)或者你feature分支中早先的一个提交。我们在“交互式rebase”一节看到了第一种的例子。后一种在当你只需要修改最新几次提交时也很有用。比如说,下面的命令对最新的3次提交进行了交互式rebase:

git checkout feature
git rebase -i HEAD~3   通过指定HEAD~3作为新的基提交,你实际上没有移动分支——你只是将之后的3次提交重写了。注意它不会把上游分支的更改并入到feature分支中。 ![Rebasing onto Head~3](https://wac-cdn.atlassian.com/dam/jcr:079532c4-2594-40ed-a5c4-0e3621b9edff/07.svg?cdnVersion=da)

如果你想用这个方法重写整个feature分支,git merge-base 命令非常方便地找出feature分支开始分叉的基。下面这段命令返回基提交的ID,你可以接下来将它传给git rebase:

git merge-base feature master

交互式rebase是在你工作流中引入git rebase 的的好办法,因为它只影响本地分支。其他开发者只能看到你已经完成的结果,那就是一个非常整洁、易于追踪的分支历史。

但同样的,这只能用在私有分支上。如果你在同一个feature分支和其他开发者合作的话,这个分支是公开的,你不能重写这个历史。

用带有交互式的rebase清理本地提交,这是无法用git merge 命令代替的。

将上游分支上的更改并入feature分支

在概览一节,我们看到了feature分支如何通过git merge 或git rebase 来并入上游分支。merge是保留你完整历史的安全选择,rebase将你的feature分支移动到master分支后面,创建一个线性的历史。

git rebase 的用法和本地清理非常类似(而且可以同时使用),但之间并入了master上的上游更改。

记住,rebase到远程分支而不是master也是完全合法的。当你和另一个开发者在同一个feature分之上协作的时候,你会用到这个用法,将他们的更改并入你的项目。

比如说,如果你和另一个开发者——John——往feature分支上添加了几个提交,在从John的仓库中fetch之后,你的仓库可能会像下面这样:

就和并入master上的上游更改一样,你可以这样解决这个Fork:要么merge你的本地分支和John的分支,要不把你的本地分支rebase到John的分支后面。

注意,这里的rebase没有违反Rebase黄金法则,因为只有你的本地分支上的commit被移动了,之前的所有东西都没有变。这就像是在说“把我的改动加到John的后面去”。在大多数情况下,这比通过合并提交来同步远程分支更符合直觉。

默认情况下,git pull 命令会执行一次merge,但你可以传入–rebase 来强制它通过rebase来整合远程分支。

用Pull Request进行审查

如果你将pull request作为你代码审查过程中的一环,你需要避免在创建pull request之后使用git rebase。只要你发起了pull request,其他开发者能看到你的代码,也就是说这个分支变成了公共分支。重写历史会造成Git和你的同事难以找到这个分支接下来的任何提交。

来自其他开发者的任何更改都应该用git merge 而不是git rebase 来并入。

因此,在提交pull request前用交互式的rebase进行代码清理通常是一个好的做法。

并入通过的功能分支

如果某个功能被你们团队通过了,你可以选择将这个分支rebase到master分支之后,或是使用git merge 来将这个功能并入主代码库中。

这和将上游改动并入feature分支很相似,但是你不可以在master分支重写提交,你最后需要用git merge 来并入这个feature。但是,在merge之前执行一次rebase,你可以确保merge是一直向前的,最后生成的是一个完全线性的提交历史。这样你还可以加入pull request之后的提交。

如果你还没有完全熟悉git rebase,你还可以在一个临时分支中执行rebase。这样的话,如果你意外地弄乱了你feature分支的历史,你还可以查看原来的分支然后重试。

比如说:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [清理目录]
git checkout master
git merge temporary-branch

总结

你使用rebase之前需要知道的知识点都在这了。如果你想要一个干净的、线性的提交历史,没有不必要的合并提交,你应该使用git rebase 而不是git merge 来并入其他分支上的更改。

另一方面,如果你想要保存项目完整的历史,并且避免重写公共分支上的commit, 你可以使用git merge。两种选项都很好用,但至少你现在多了git rebase这个选择。

原文出处

Merging vs. Rebasing

 
 

【译文】Git merge 和 Git rebase比较的更多相关文章

  1. Git merge 与 git rebase的区别

    Git merge的用法: git merge Dev // Dev表示某分支,表示在当前分支合并Dev分支 git merge -m  "Merge from Dev"  Dev ...

  2. git merge与 git rebase区别及实例

    接Git分支创建与合并,在分支合并时,有两种方式:git merge 和git rebase. git merge:将两个分支,合并提交为一个新提交,并且新提交有2个parent. git rebas ...

  3. git第七节---git merge和git rebase

    # git merge和git rebase 都可以进行分支合并 #git merge 合并后保留记录两个分支的记录 #git rebase合并后会展示成一个分支的记录,另一个分支的提交实际生成了一个 ...

  4. git merge和git rebase的区别

    git merge是用来合并两个分支的.# 将b分支合并到当前分支git merge b git cherry-pick可以选择某一个分支中的一个或几个commit(s)来进行操作.例如,假设我 们有 ...

  5. git merge和git rebase的区别和异同

    1.git  merge和git rebase作用差不多,都是将远程代码和本地代码合并 2.git  merge和git rebase作用差不多,都是将远程代码和本地代码合并 3.git  merge ...

  6. git merge 及 git rebase的区别

    Git上合并代码有git merge 及 git rebase 两种方式. 前置知识点 Master分支:首先,代码库应该有一个.且仅有一个主分支.所有提供给用户使用的正式版本,都在这个主分支上发布. ...

  7. git merge 与 git rebase的区别?

    一,git merge 与 git rebase的区别 1,git merge 例如: master分支合并dev分支,git将两个分支dev和master上的所有commit , 按照提交时间的先后 ...

  8. git pull、git fetch、git merge、git rebase的区别

    一.git pull与git fetch区别 1.两者的区别       两者都是更新远程仓库代码到本地. git fetch相当于是从远程获取最新版本到本地,不会自动merge. 只是将远程仓库最新 ...

  9. git merge 和 git rebase 小结

    Git merge是用来合并两个分支的. git merge b # 将b分支合并到当前分支 同样 git rebase b,也是把 b分支合并到当前分支 ---------------------- ...

随机推荐

  1. linux(centOS7)的基本操作(三) 用户、组、权限管理

    用户和组 1.用户.组.家目录的概念 linux系统支持多用户,除了管理员,其他用户一般不应该使用root,而是应该向管理员申请一个账号.组类似于角色,系统可以通过组对有共性的用户进行统一管理.每个用 ...

  2. MYSQL5.5二进制包的安装

    二进制1. 下载包 wget http://mirrors.sohu.com/mysql/MySQL-5.6/mysql-5.5.55-linux2.6-i686.tar.gz2. 解压 tar xx ...

  3. Jenkins持续集成环境部署

    一.下载Jenkins Jenkins下载地址:https://jenkins.io/download/ 这里我们下载的是jenkins.war 二.启动Jenkins 在Linux下启动Jenkin ...

  4. Python学习之==>数组(二)

    1.切片 # 切片:是list取值的一种方式 nums = ['段佳琳','陈伟良','王占宇','李波','韶钢'] print(nums[1:3]) # 顾头不顾尾,不包含后面下标的元素 prin ...

  5. Array数组对象

    1.数组方法: 1>字符串的连接: var myarr1= new Array("010") var myarr2= new Array("-",&quo ...

  6. springboot-elasticsearch项目启动报错:'elasticsearchTemplate' that could not be found

    解决: 将elasticsearch的相关配置加入到application.yml配置文件中就可以解决

  7. python__007内置函数

    本文摘自:https://docs.python.org/3/library/functions.html?highlight=built#ascii     内置功能     abs() delat ...

  8. cmd命令简单别木马的蛛丝马迹

    一些基本的Windows命令往往可以识别木马的蛛丝马迹,而且在保护网络安全上起到很大的作用. 检测网络连接 如果你怀疑自己的计算机上被别人安装了木马,或者是中了病毒,但是手里没有完善的工具来检测是不是 ...

  9. python GIL全局解释器锁与互斥锁 目录

    python 并发编程 多线程 GIL全局解释器锁基本概念 python 并发编程 多线程 GIL与Lock python 并发编程 多线程 GIL与多线程

  10. Linux内核基础优化

    Linux内核基础优化 net.ipv4.ip_forward = 1 #开启网络转发 net.ipv4.conf.default.rp_filter = 0 #开启代理功能 net.ipv4.con ...