【译文】Git merge 和 Git rebase比较
【译文】Git merge 和 Git rebase比较
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分支中。 
如果你想用这个方法重写整个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这个选择。
原文出处
【译文】Git merge 和 Git rebase比较的更多相关文章
- Git merge 与 git rebase的区别
Git merge的用法: git merge Dev // Dev表示某分支,表示在当前分支合并Dev分支 git merge -m "Merge from Dev" Dev ...
- git merge与 git rebase区别及实例
接Git分支创建与合并,在分支合并时,有两种方式:git merge 和git rebase. git merge:将两个分支,合并提交为一个新提交,并且新提交有2个parent. git rebas ...
- git第七节---git merge和git rebase
# git merge和git rebase 都可以进行分支合并 #git merge 合并后保留记录两个分支的记录 #git rebase合并后会展示成一个分支的记录,另一个分支的提交实际生成了一个 ...
- git merge和git rebase的区别
git merge是用来合并两个分支的.# 将b分支合并到当前分支git merge b git cherry-pick可以选择某一个分支中的一个或几个commit(s)来进行操作.例如,假设我 们有 ...
- git merge和git rebase的区别和异同
1.git merge和git rebase作用差不多,都是将远程代码和本地代码合并 2.git merge和git rebase作用差不多,都是将远程代码和本地代码合并 3.git merge ...
- git merge 及 git rebase的区别
Git上合并代码有git merge 及 git rebase 两种方式. 前置知识点 Master分支:首先,代码库应该有一个.且仅有一个主分支.所有提供给用户使用的正式版本,都在这个主分支上发布. ...
- git merge 与 git rebase的区别?
一,git merge 与 git rebase的区别 1,git merge 例如: master分支合并dev分支,git将两个分支dev和master上的所有commit , 按照提交时间的先后 ...
- git pull、git fetch、git merge、git rebase的区别
一.git pull与git fetch区别 1.两者的区别 两者都是更新远程仓库代码到本地. git fetch相当于是从远程获取最新版本到本地,不会自动merge. 只是将远程仓库最新 ...
- git merge 和 git rebase 小结
Git merge是用来合并两个分支的. git merge b # 将b分支合并到当前分支 同样 git rebase b,也是把 b分支合并到当前分支 ---------------------- ...
随机推荐
- cocoapods [!] Unable to find a pod with name, author, summary, or description matching `xx`
pod search MJRefresh的时候报错 [!] Unable to find a pod with name, author, summary, or description matchi ...
- 阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_1 请求参数绑定入门
请求参数的绑定 参数绑定 创建新的页面 给方法加上注解 前面没有斜线 重新部署项目 传递一个username的值 后台方法接收 重新部署项目 再传一个password的值 再输出password ja ...
- global和nonlocal的用法
1 nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量.写在内部嵌套函数里面,它实质上是将该变量定义成了全局变量,它等价于用两个global来定义该变量.只不过用两个gl ...
- SAS中的聚类分析方法总结
SAS中的聚类分析方法总结 说起聚类分析,相信很多人并不陌生.这篇原创博客我想简单说一下我所理解的聚类分析,欢迎各位高手不吝赐教和拍砖. 按照正常的思路,我大概会说如下几个问题: 1. 什么是 ...
- 定位网页元素、透明度、z-index、包裹性和破坏性
一.定位 position属性————规定元素的定位类型,即元素脱离文档流的布局,在页面的任意位置显示 也可以参见以前的总结 <—— 戳 static:默认值,没有定位 relative:相对定 ...
- Prometheus Querying Function rate() vs irate()
rate() rate(v range-vector) calculates the per-second average rate of increase of the time series in ...
- MySQL数据库的常见操作
1.查看所有的数据库 1 show databases; 2.创建数据库 后面的时编码格式 1 create database dbName charset='utf8'; 3.使用/切换数据库 1 ...
- Java作业 题目:16版.情人节送玫瑰花
题目:16版.情人节送玫瑰花 题干: 1.实验要求 本实验要求:以情人节送花为业务背景,体验自定义异常以及异常处理机制. 1-1. 业务说明: 1-1.1. 本实验以情人节送花为业务背景,女方提出送花 ...
- linux 内核数据结构之 avl树.
转载: http://blog.csdn.net/programmingring/article/details/37969745 https://zh.wikipedia.org/wiki/AVL% ...
- CSP-S全国模拟赛第三场 【nan死了】
mmt 居然第一步膜化乘除 都没看出来,没救了... 大概是贡献前缀和优化的做法 巨兔式讲解:大家都学会了么? 咱发现有大量的 (i/j , i%j ) 同时 对很多 c 产生了贡献,咱可以去优化这一 ...