虽然 merging 和 rebasing 在 git 中类似时,但他们提供不一样的功能。为了让你的历史尽量的干净和完整,你应该知道如下几点。git
git rebase
命令已 神奇的 Git voodoo 而闻名,初学者应该远离它,但它实际上可让开发团队在使用时更加轻松。在本章中,咱们将 把 git rebase
和与之有关联的 git merge
命令相比较 ,并在典型的 Git 工做流 中从新定位,识别其全部潜在的机会。面试
首先要明白关于 git rebase
的事情是它像 git merge
同样解决相同的问题。git rebase 和 git merge 同样都是被设计用于从一个分支获取并合并到当前分支,可是他们采起不一样的工做方式。spring
考虑一下,当你开始在 一个专用的分支上开发新特性,与此同时另外一个团队成员用新的提交来更新了 master
分支时,会发生什么呢?这会致使分叉的历史记录,对于这个问题,使用 Git 做为协同工具的任何人来讲都应该很熟悉。后端
如今,假设你在工做时在 master
上的新提交与新特性相关。为了将新提交合并到你的 feature
分支上,你有两种选择:merging 或者 rebasing。安全
最简单的选项是使用如下命令将 master
分支合并到 feature
分支:编辑器
git checkout feature git merge master
或者,你能够简化成一句:svg
git merge master feature
这将在 feature
分支上建立一个新 “ 合并提交 ” ,并把两个分支的历史联系在一块儿。分支结构显示以下:工具
Merging 之因此好是由于它是一个不可逆的操做。在任何状况下,现有分支不能被更改。这避免了全部 rebasing 的潜在陷阱(详见下文)。学习
另外一方面,这也意味着每次须要合并上游更改时, feature
分支都将有一个额外的 merge 提交产生。若是 master
很是活跃,这可能破坏你所有的 feature 分支的历史。使用高级的 git log
选项来减缓这个问题是有可能的,也让其余开发人员很难理解这个项目的历史记录。this
做为 merging 的一个替代品,你可使用如下命令将 feature
分支合并到 master
分支:
git checkout feature git rebase master
这将整个 feature
分支从 master
分支的顶端开始,有效地将全部新的提交合并到主分支中。可是,并非使用合并提交,而是经过为每一个在原始分支上的提交建立全新的提交来重写项目历史。
rebasing 最主要的益处是你将得到一个十分干净整洁的项目历史。首先,它经过 git merge
排除多余的 merge 提交需求;其次,正如你在上图所看到的那样,rebasing 也会产生完美线性的项目历史记录—你能够顺着 feature
一直到项目的起始位置而没有任何分支。能够方便的使用 git log
,git bisect
和 gitk
追踪提交记录。
可是,对于新的提交历史有两点须要权衡:安全性和可追溯性。若是你不遵循 Rebasing 的黄金法则,为你的协做工做流重写项目历史可能会成为潜在的灾难。另外,不重要的是,rebasing 会丢失合并提交所提供的上下文—你不能看到什么时候合并到 feature 分支中的上游变化。
当他们移动到新的分支上,交互式合并给你机会来修改提交。自从它提供彻底控制整个分支的提交历史以后,它比自动合并更强大。具备表明性的,在合并一个 feature 分支到 master
时,它是被用来清除错误的历史。
要开始一个交互式的重基会话,请将 i
选项传递给 git 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 ,从而产生以下所示的项目历史记录:
像这样排除不重要的提交使你的特性历史至关易懂。这一点是 git merge
没法比拟的。
一旦你明白什么是 rebasing ,最重要的事情是学习何时不用它。 git rebase
的黄金法则是永远不要在公有分支上使用它。
举例说,想象一下若是你将 master
分支合并到 feature
分支上会发生什么:
rebase 操做将 master
中全部提交移动到 feature
的头部,但问题是这一切都发生在你的仓库中。其余开发者依然在原来的 master
分支上继续工做。自从 rebasing 产生了全新的提交,Git 将会认为你的 master
分支的历史记录与其余人的历史记录不一样。
使两个 master
分支 同步的惟一方法是将他们合并到一块儿,致使出现一个额外的合并操做和两组都包含相同改变(最原始的那个,和那些来自你从新创建的分支)的提交。不用说,这是一个很是混乱的场景。
所以,在你运行 git rebase
以前,必定要问本身,“还有其余人在看这个分支吗?”,若是回答是确定的,那么把你的手从键盘上拿开并开始考虑让你的改变没有破坏性(例如, git revert
命令)。不然,你能够为所欲为地重写历史。
若是你尝试将合并的 master
分支推送到远程库中,Git 将防止你这样作,由于它与远程 master
分支有冲突。可是,你能够经过传递 --force
标志来强制推送,就像这样:
# Be very careful with this command! git push --force
该操做会将远程仓库的 master
分支替换为 rebase 过的 master
分支,这会给团队的其余成员带来困扰。所以,当你确切的知道你要作什么的时候,才要很是当心的使用这些命令。
推送一个私有新特性分支到远程仓库(例如,用于备份)。这就好像是说,“哎呦,我不想推送 feature 分支的原始版本,拿当前的版本替换吧。”再强调一次,没有人在 feature 分支的原始版本中工做是很重要的。
Rebasing 可以根据团队的须要或多或少的被合并到你现存的 Git 工做流 中。在这个选项中,咱们将检查 rebasing 提供在不一样阶段的 feature 分支开发的好处。
在任何工做流中,首先第一步是利用 git rebase
为每个 feature 建立一个专用的分支。这给你必要分支结构来安全使用 rebasing :
最好的方法之一是合并 rebasing 到你的 工做流 以此来清理本地正在进行的 feature 分支。经过按期的执行一个交互式的 rebase ,你能够确保每个在你的 feature 分支中的提交是集中且有意义的。这将让你编写你本身的代码而不须要在独立提交中担忧破坏它—你能够在过后修复它。
当调用 git rebase
,对于新的分支你有两个选项:feature 父类分支(举例说,master
分支),或者在你的 feature 分支中较早的提交。咱们查看了在 交互式的 Rebasing 章节中首个选项的示例 。当你仅仅须要修复最新提交时,后者的选择最好。举例说,交互式 rebase 的最后3次提交显示以下:
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
交互式 rebasing 的做用在于当他仅仅影响本地分支时,它是一个 引进 git rebase
到工做流中的好方式。其余开发人员惟一能看到的是你最后提交的成果,这应该是一个简单且易于理解的 feature 分支历史记录。
可是在刚开始,这仅仅只为私有 feature 分支工做。若是你借助相同 feature 分支与其余开发者协做,分支是共有的,你也不被容许重写它的历史记录。
没有 git merge
以外的其余选择时可使用交互式 rebase 来清除本地提交。
在开篇章节中,咱们知道了 feature 分支如何使用 git merge
或 git rebase
合并 master
分支的上游提交。当 rebasing 经过移动你的 feature 分支到 master
分支的头部来建立一个线性历史时,Merging 是一个用于保护你仓库的整个历史记录的安全选项。
git rebase
的做用与本地清除类似(可以同时被执行),可是在此过程当中,它合并了 master
的上游提交。
牢记,远程分支取代 master
分支是彻底合法的。这发生在其余开发者在同一个 feature 分支上协做时和你须要合并他们的更改到你的仓库中时。
举例说明,若是你和一个名为 John 的开发人员添加了对 feature
分支的提交,从 John 的仓库中获取远程 feature
分支后,你的仓库看起来像以下所示:
你能够用与 master
分支集成上游更改相同的方法来解决这个分叉:或者你本地的 feature
分支与 john/feature
分支合并,或者 rebase 你本地 feature
分支到 john/feature
分支的头部。
请注意,任何事情在未更改以前,rebase 不能违反 Rebasing 的黄金法则 ,由于 feature
仅仅移动了本地提交。这就好像是在说,“将个人更改添加到 John 已经完成了的操做中。” 在大多数状况下,这比经过合并提交与远程分支同步更为直观。
默认状况下, git pull
命令执行合并,可是你能够强制经过使用 rebase 的 --rebase
选项整合远程分支。
若是你使用 Pull 请求做为代码的审计过程,建立的 pull 请求以后,你须要避免使用 git rebase
。一旦你发出 pull 请求,其余开发人员就能看到你的提交,这就意味着它是一个公有分支。重写它的历史记录将使 Git 和你的队友没法追踪到任何添加到 feature 分支上的后续提交。
任何来自其余开发者的更改须要使用 git merge
取代 git rebase
来被合并。
为此,在提交你的 pull 请求以前,使用交互式 rebase 清理你的代码,一般是一个好主意。
在 feature 分支被你的团队承认以后,在使用 git merge
整合 feature 分支到主代码库以前,你有一个 rebasing feature 分支到 master
分支的选项。
合并上游更改到 feature 分支是一个相似的状况,可是,自从你不被容许在 master
中重写提交,你最后不得不使用 git merge
来整合 feature 分支。然而,经过在合并以前执行 rebase 确保 merge 将快速进行,造成完美的线性历史。这也给了你在 pull 请求期间将任何后续提交塞入到 feature 分支中的机会。
若是你对 git rebase
感到不太舒服,你能够在临时分支中一直执行 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
在你开始 rebasing 你的分支以前,这是全部你真正须要知道:若是您想要一个没有没必要要的干净的合并提交的线性历史记录,你应该争取 git rebase
代替 git merge
整合来自另外一个分支的改变。
另外一方面,若是你想保存你项目的完整历史而且避免重写公有提交的风险,你能够坚持使用 git merge
。任何一个选项都是彻底有效的,至少如今你是有选择性的利用 git rebase
的好处。
本文做者:Tim Pettersen, 翻译:Queena
原文连接: https://dzone.com/articles/me...
译文首发: http://didispace.com/git-merg...
本文有spring4all技术翻译组完成,更多国外前沿知识和干货好文,欢迎关注公众号:后端面试那些事儿。