虽然合并和变基在git中很相似,但它们提供不同的功能。为了使您的历史记录尽可能干净和完整,您应该了解以下几点。gitrebase命令被称为神奇的Git巫术,初学者应该远离它,但它实际上可以让开发团队的生活更轻松。在本章中,我们将比较gitrebase与其关联的gitmerge命令,并确定其在典型Git工作流程中所有潜在的rebase机会。概述关于gitrebase首先要了解的是它解决了与gitmerge相同的问题。gitrebase和gitmerge都被设计为从一个分支中获取并将其合并到当前分支中,但它们的工作方式不同。考虑一下当您开始在专用分支上开发新功能时会发生什么,同时另一个团队成员使用新提交更新master分支?对于这个问题,这导致了分叉的历史记录,任何使用Git作为协作工具的人都应该熟悉这个问题。现在,假设您正在master上进行与新功能相关的新提交。为了将新提交合并到您的功能分支中,您有两个选择:合并或变基。合并选项最简单的选项是将master分支合并到feature分支中,使用:gitcheckoutfeaturegitmergemaster或者,你可以将其简化为一句话:gitmergemasterfeature这将在feature分支上创建一个新的“合并提交”,并将两个分支的历史链接起来一起。分支结构如下所示:合并是好的,因为它是一个不可逆的操作。现有分支在任何情况下都不能更改。这避免了rebase的所有潜在陷阱(详见下文)。另一方面,这也意味着每次需要合并上游变更时,特性分支都会有一次额外的合并提交。如果master非常活跃,这可能会破坏功能分支的整个历史记录。可以使用高级git日志选项来缓解这个问题,同时也让其他开发人员难以了解项目的历史。rebase选项作为合并的替代方法,您可以使用以下命令将功能分支合并到主分支中:gitcheckoutfeaturegitrebasemaster这将从主分支的尖端开始整个功能分支,有效地将所有新提交合并到主分支中。但是,不是使用合并提交,而是通过为原始分支上的每个提交创建全新的提交来重写项目历史。变基的主要好处是您可以获得非常干净整洁的项目历史记录。首先,它消除了通过gitmerge进行冗余合并提交的需要;其次,正如您在上图中所见,变基还产生了一个完美的线性项目历史——您可以一直关注该特性,直到项目开始。没有分支机构。您可以轻松地使用gitlog、gitbisect和gitk来跟踪提交记录。然而,新的提交历史有两个权衡:安全性和可追溯性。如果您不遵循变基的黄金法则,为您的协作工作流重写项目历史可能是一场潜在的灾难。此外,不重要的是,变基会丢失合并提交提供的上下文——您无法看到上游更改何时合并到功能分支中。InteractiveRebasing和InteractiveMerging使您有机会在提交移动到新分支时对其进行修改。它比自动合并更强大,因为它提供了对整个分支的提交历史的完全控制。通常,它用于在将功能分支合并到master时清除错误的历史记录。要启动交互式rebase会话,请将i选项传递给gitrebase命令:gitcheckoutfeaturegitrebase-imaster这将打开一个文本编辑器,列出所有要移动的提交:pick33d5b7aMessageforcommit#1pick9480b3dMessageforcommit#2pick5c67e61Messageforcommit#3这个列表是精确定义的分支如何处理执行变基。通过更改pick命令或调整条目的顺序来更改分支的提交历史记录,您可以使分支看起来像任何您想要的样子。例如,如果第二次提交是为了修复第一次提交中的一个小错误,您可以使用fixup命令将它们减少为单个命令:pick33d5b7aMessageforcommit#1fixup9480b3dMessageforcommit#2pick5c67e61Messageforcommit#3当您保存并关闭文件时,Git将执行根据您的说明进行变基,导致项目历史看起来像这样:排除不重要的提交这样使您的功能历史相当可读。这是gitmerge无法比拟的。变基的黄金法则一旦你理解了什么是变基,最重要的事情就是学会什么时候不使用它。gitrebase的黄金法则是永远不要在公共分支上使用它。例如,想象一下如果将master分支合并到feature分支会发生什么:变基操作将master中的所有提交移动到feature的头部,但问题是这一切都发生在你的存储库中。其他开发人员继续在原来的master分支上工作。因为rebase会产生全新的提交,Git会认为你的master分支与其他人的历史不同。使两个master分支同步的唯一方法是将它们合并在一起,从而导致额外的合并操作和两组包含相同更改的提交(原始的和来自您rebased的分支的提交)。不用说,这是一个非常混乱的场景。所以,在你运行gitrebase之前,一定要问问自己,“还有其他人在看这个分支吗?”,如果答案是肯定的,那么把手从键盘上拿开,开始考虑让你的改变不具破坏性(因为例如,gitrevert命令)。否则,您可以随心所欲地重写历史。Force-Pushing如果你试图将合并后的master分支推送到远程仓库,Git会阻止你这样做,因为它与远程master分支冲突。但是,您可以通过传递--force标志来强制推送,如下所示:#Beverycarefulwiththiscommand!gitpush--force这将用rebasedmaster分支替换远程master分支,这将有利于团队的其他成员打扰。因此,当您确切知道要做什么时,请非常小心地使用这些命令。将私有的新功能分支推送到远程存储库(例如,用于备份)。就像在说,“哎呀,我不想推送原来版本的特性分支,换成现在的版本”。同样,重要的是没有人在功能分支的原始版本上工作。WorkflowWalkthroughRebasing可以根据您的团队需要尽可能多或尽可能少地合并到您现有的Git工作流程中。在此选项中,我们将检查变基在功能分支开发的不同阶段提供的好处。在任何工作流程中,第一步都是使用gitrebase为每个功能创建一个专用分支。这为您提供了必要的分支结构以安全地使用变基:进行本地清理的最佳方法之一是将变基合并到您的工作流中以清理本地正在进行的功能分支。通过定期执行交互式变基,您可以确保功能分支中的每个提交都是有针对性且有意义的。这将使您可以编写自己的代码,而不必担心在单独的提交中破坏它——您可以在事后修复它。调用gitrebase时,您有两个新分支选项:功能父分支(例如,主分支)或功能分支中的较早提交。我们在交互式变基一章中查看了第一个选项的示例。当您只需要修复最新的提交时,后一种选择是最好的。例如,交互式rebase的最后3次提交显示如下:submit。请注意,这不会将上游更改合并到功能分支中。如果你想用这种方法重写整个特性,gitmerge-base命令对于找到特性分支的原始起点非常有用。以下返回原始起点的提交ID,然后将其传递给gitrebase:gitmerge-basefeaturemaster交互式rebase的强大之处在于,当gitrebase仅影响本地分支时,它是将gitrebase引入您的工作流程的好方法。其他开发人员唯一会看到的是您的最后一次提交,这应该是功能分支的简单易懂的历史记录。但起初,这只适用于私有特性分支。如果您在同一功能分支上与其他开发人员协作,则该分支是共享的,并且您不能重写其历史记录。当除了gitmerge别无选择时,交互式变基可用于清理本地提交。将上游更改合并到Feature在开篇中,我们看到了如何使用gitmerge或gitrebase将feature分支与来自master分支的上游提交合并。虽然rebase通过将功能分支移动到master分支的头部来创建线性历史,但合并是保护存储库整个历史的安全选项。gitrebase就像一个本地清除(可以同时执行),但在这个过程中它合并了来自master的上游提交。请记住,远程分支取代主分支是完全合法的。当其他开发人员在同一功能分支上进行协作并且您需要将他们的更改合并到您的存储库时,就会发生这种情况。例如,如果你和一个名叫John的开发人员向特性分支添加提交,在从John的存储库中获取远程特性分支后,你的存储库将如下所示::将您的本地功能分支与john/feature分支合并,或者将您的本地功能分支重新定位到john/feature分支的头部。上传成功请注意,rebase不能在任何更改之前违反Rebase的黄金法则,因为该功能仅移动本地提交。这就像在说,“将我的更改添加到John已经完成的工作中。”在大多数情况下,这比通过合并提交与远程分支同步更直观。默认情况下,gitpull命令执行合并,但您可以使用rebase的--rebase选项强制整合远程分支。使用拉取请求验证功能分支如果您使用拉取请求作为代码审核过程,则需要避免在创建拉取请求后使用gitrebase。一旦您发出拉取请求,其他开发人员就可以看到您的提交,这意味着它是一个公共分支。重写它的历史将使Git和你的队友无法跟踪添加到功能分支的任何后续提交。来自其他开发人员的任何更改都需要使用gitmerge而不是gitrebase来合并。出于这个原因,在提交拉取请求之前使用交互式变基来清理代码通常是个好主意。集成已批准的功能在您的团队批准功能分支后,您可以选择先将功能分支变基到主分支,然后再使用gitmerge将功能分支集成到主存储库中。将上游更改合并到功能分支是类似的情况,但是,由于您不允许在master中重写提交,因此您最终不得不使用gitmerge来集成功能分支。但是,通过在合并之前进行rebase可确保合并将快速进行,从而形成完美的线性历史记录。这也让您有机会在拉取请求期间将任何后续提交填入功能分支。如果你对gitrebase感到不舒服,你总是可以在临时分支中进行rebase。这样,如果您不小心弄乱了您的功能分支历史记录,您可以多次检出原始分支。示例:gitcheckoutfeaturegitcheckout-btemporary-branchgitrebase-imaster#[Cleanupthehistory]gitcheckoutmastergitmergetemporary-branch总结在你开始变基你的分支之前,这里是你真正需要知道的:如果你想要一个没有不必要的干净合并提交的线性历史为了记录,你应该争取gitrebase而不是gitmerge来合并来自另一个分支的更改。另一方面,如果您想保留项目的完整历史并避免重写公共提交的风险,您可以坚持使用gitmerge。两种选择都完全有效,至少现在您可以选择性地利用gitrebase的好处。本文作者:TimPettersen,译文:Queena原文链接:https://dzone.com/articles/merging-vs-rebasing版权归作者所有,转载时请注明作者、原文、译者等出处信息转载
