Git Merging vs. Rebasing(译文)

原文地址

git reabase有这样一个声誉,那就是初学者要远离这个命令,但是当开发团队能小心谨慎的使用它时,将会使我们的生活更加美丽。在这篇文章里我们将深度比较 git rebase和与之相关的 git merge命令并且找到能将其纳入我们的git工作流的潜在机会。

###概述 首先要明白的是git reabase命令和git merge命令解决了相同的问题,这两个命令都是设计用来整合一个分支上的更改到另一个分支-只不过是用了不同的方式。 首先思考一下当你为一个工作分支添加新特性时发生了什么,接下来另一个团队成员通过提交commitg更新了master分支。这会产生分叉历史记录,任何使用Git作为协作工具的人都应该熟悉这一点。

现在,假设master中的新提交与您正在处理的功能相关,为了合并这个新提交到你的工作分支,你有两个选择 merging或者 rebasing。 ###合并选项 最简单的合并 marster分支到本地分支的办法就是使用下面的命令

git checkout feature
git merge master
复制代码

或者写成一行

git merge master feature
复制代码

这样将会在你的工作分支上创建一个merge commit将两个分支的历史联系在一起,分支结构如下图所示: ![IMAGE](quiver-image-url/3464B87C2B36C0C3B8A72C5B623355C6.jpg =561x366) 合并操作相当完美因为他是无害操作,现有的分支没有任何改变,这样可以避免所有的rebasing隐患,(下面讨论) 另一方面,这也意味着每次需要合并上游更改时,工作分支都会有一个无关的合并提交。 如果master分支改动非常频繁,这会让你的工作分支的提交记录看起来非常的丑陋,尽管使用高级的git log选项可以缓解这个问题,但是这这可能会让其他开发者难以理解项目的历史。

###变基选项 作为合并的替代方法,您可以使用以下命令将工作分支重新绑定到master分支上:

git checkout feature
git rebase master
复制代码

这会将整个工作分支移到master分支的前面,有效的将所有新提交合并到master中。但是,相比于使用合并,变基操作通过为原始分支中的每个提交创建全新的提交来重新编写项目历史记录。

变基操作的一个好处就是能得到更清晰的项目提交历史。第一,他消除了 git merge所需的不必要的合并提交。第二,如上图所示,变基操作可以产出完美的线性提交历史记录,你可以在没有任何分叉的情况下沿着项目的开头一直到最新的提交。这使得使用 git loggit bisectgitk等命令轻松导航项目。 但是为了获得简介的历史提交历史,有两个地方需要权衡:安全性和可追溯性,如果你不遵守变基操作的黄金法则,那么重写项目的历史记录对你的git工作流来说可能是灾难性的。而且,变基操作会失去合并提交提供的上下文 - 你无法看到上游更改何时合并到你的工作分支中来。

###交互式的变基操作 交互变基让你有机会在提交到新分支时更改提交,这比自动变基更加强大,因为它提供了对分支的提交历史的完全控制,通常情况下,这是在合并到主干分支之前清理混乱的历史提交。 要开始交互式重新绑定会话,在git rebase命令后加上i选项

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合并根本做不到的事情。 ###变基的黄金法则 当你明白变基能干什么后,你要学习一件非常重要的事就是什么时候不去使用它。 法则一:永远不要再公共分支上用他 例如,想象一下当你将master分支变基到开发分支上时会发生什么?
变基操作将会把master分支上的提交移动到当前分支的最前面,问题是现在这个分支只存在于你自己的仓库中。其他的开发者仍然在原来的master分支上工作,由于变基产生全新的提交,Git会认为你的主分支的历史与其他人的不同。 同步两个主分支的唯一方法是将它们合并回去,这将导致一个额外的合并提交和两组提交包含相同的更改(原来的和来自你的reabae后的分支)不用说,这是一个非常混乱的情况。 所以,在你运行 git rebase之前,总是问自己:“还有其他人在看这个分支吗?”如果答案是肯定的,那就把手放在键盘上,开始思考一个非破坏性的方法来做出你的改变(例如, git revert命令)。 否则的话,您可以随心所欲地重写历史记录。 ###强制提交 如果你尝试将变基后的master分支推送到远端仓库,Git将不允许你这样子干因为本地master现在和远端仓库的master分支冲突了。但是你可以再 push命令后添加 --force选项来强制提交。就像这样:

# Be very careful with this command!
git push --force
复制代码

这个操作将会根据本地仓库的master分支来重写远端的master分支,将导致项目中的其他成员感到困惑。所以执行这个操作前必须三思而后行。 你唯一应该强制推送的情况是,当你将私有特性分支推送到远程仓库之后,你执行了一次本地清理(例如,为了备份目的) 这就像是说,“糟糕,我不是真的想把这个原始版本的特性分支推出去。而是用后面的替代。 同样重要的是,没有人在原始分支上提交代码。 ###工作流程演练 变基操作可以将现有的Git工作流程整合到您的团队所熟悉的程度,在本节中,我们将介绍变基在功能开发的各个阶段可以提供的优势。 任何利用git rebase的工作流程的第一步是为每个功能创建一个专用的分支。这将给你必要的分支结构,以安全利用rebasing:

###本地清理 把rebasing融入工作流程的最好方法之一是清理本地真在开发的特性。通过定期执行交互式底图,可以确保您的功能中的每个提交都是专注和有意义的。这可以让你编写你的代码,而不用担心把它分解成孤立的提交 - 你可以在事后修复它。 当调用git rebase时,对于新的基础有两个选项:特性的父分支(例如master)或者在你的工作分支中之前的提交。我们在“交互式重新绑定”部分看到了第一个选项的示例。后面的选项是很好的,当你只需要修复最后几个提交。例如,以下命令仅开始最后3次提交的交互式重新分页。

git checkout feature
git rebase -i HEAD~3
复制代码

通过指定HEAD〜3作为新的基础,你实际上并没有移动分支 - 你只是交互地重写它后面的3个提交。请注意,这不会将上游更改合并到功能分支中。

如果你想用这种方法重写整个工作分支,那么 git merge-base命令可以用来查找工作分支的原始基础。 以下内容返回原始基础的提交ID,然后您可以将其传递给git rebase:

git merge-base feature master
复制代码

这种使用交互式重新绑定是将git rebase引入到工作流中的好方法,as it only affects local branches. 其他开发者唯一会看到的就是你的成品,这应该是一个干净,容易遵循的功能分支历史。 但是,这只能用于私人功能分支。如果你通过同一个功能分支与其他开发人员合作,该分支是公开的,n那么你不允许重写其历史记录。 没有git merge的替代方案来清理本地提交的交互式rebase。

###将上游变更纳入特性分支 在概述部分,我们看到了一个功能分支如何使用git mergegit rebase合并来自master的上游变更.合并是一个安全的选项,可以保留存储库的整个历史记录,而变基操作通过将特征分支移动到master分支上创建线性历史记录。 这种使用git rebase类似于本地清理(可以同时执行),但是在这个过程中它包含了来自master的上游提交。 请记住,将其分配到远程分支而不是master分支是完全合法的。当与其他开发人员在同一功能上进行协作时,可能会发生这种情况,并且需要将其更改合并到存储库中。 例如,如果您和另一位名为John的开发人员向功能分支添加了提交,则在从John的远端仓库获取远程功能分支后,您的仓库可能如下所示:

您可以使用完全相同的方式来解决这个问题,就像您整合来自主服务器的上游更改一样:将本地功能与john / feature合并,或将您的本地功能重新绑定到john / feature的顶端。
请注意,这个rebase并没有违反Rebasing的黄金规则,因为只有你的本地特性提交正在被移动 - 在这之前的所有内容都是不变的。这就好像在说:“把我的改变添加到John已经完成的工作中”。在大多数情况下,这比通过合并提交与远程分支同步更直观。 默认情况下, git pull命令执行合并,但可以强制它通过传递 --rebase选项来将远程分支与 rebase集成。 ###使用合并请求查看功能 如果你使用 pull请求作为你的代码审查过程的一部分,你需要避免在创建 pull请求之后使用 git rebase。只要您提出拉取请求,其他开发人员就会查看您的提交,这意味着它是一个公共分支。重写它的历史将使Git和你的队友无法追踪任何后续提交的功能。 其他开发人员的任何更改都需要使用 git merge而不是 git rebase。 出于这个原因,在提交您的请求之前,清理交互式底图代码通常是一个好主意。 ###集成已发布的特性 在你的团队发布了一个新功能之后,你可以选择将这个功能rebaseing到主分支的顶端,然后使用 git merge将这个功能集成到主代码库中。 这与将上游更改合并到功能分支中的情况类似,但由于您不允许在主分支中重写提交,你必须最终使用git merge来整合这个功能。但是,通过在合并之前执行rebase,可以确保合并将被快速转发,从而产生完美的线性历史记录。这也让你有机会压缩在拉取请求期间添加的任何后续提交。
如果你对git rebase不太满意,你可以在一个临时的分支上执行rebase。这样,如果您不小心搞乱了您的功能的历史记录,您可以查看原始分支并重试。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch
复制代码

###总结 这就是你真正需要知道的开始重新分配你的分支机构。如果你更喜欢一个干净的,线性的历史,没有不必要的合并提交,你应该达到git rebase而不是git merge从另一个分支集成更改。 另一方面,如果你想保存你的项目的完整历史,并避免重写公共提交的风险,你可以坚持使用git merge。这两个选项都是完全有效的,但至少现在你可以选择利用git rebase的好处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值