Git指南 部分2:黄金法则和其他基础基础

让我们看看运行git rebase时会发生什么以及为什么需要小心。 

这是Mail.ru云解决方案团队翻译的Pierre de Wulf博客的Git指南第二部分第三部分第一部分可以在这里阅读

变基的本质


变基到底是如何发生的:


您可以说变基是取消固定要移动的分支并将其连接到另一个分支。这个定义是正确的,但请尝试更深入一些。如果您查看该文档,那么它是关于重新基准的内容:“应用提交到另一个分支(重新提交在另一个基本技巧的顶部)”。

这里的主要用语是应用,因为rebase不仅仅是将粘贴分支复制粘贴到另一个分支。Rebase按顺序从选定分支中获取所有提交,并将它们重新应用到新分支。

此行为导致两点:

  1. 通过重新应用提交,Git创建新的提交。即使它们包含相同的更改,Git仍被视为新的独立提交。
  2. Git rebase会覆盖提交,并且不会删除旧提交。这意味着在完成变基之后,您的旧提交将继续存储在.git文件夹的/ gjects子文件夹中。如果您不完全了解Git如何存储和考虑提交,请阅读本文的第一部分。

这是对变基期间发生的情况的更正确的解释:


如您所见,功能分支包含全新的提交。如前所述,从Git的角度来看,这是一组相同的更改,但是是全新的对象。 

这也意味着旧的提交不会被破坏。它们只是直接变得不可访问。如果您还记得的话,分支只是指向提交的链接。因此,如果分支和标签都没有引用提交,则尽管它仍存在于磁盘上,但无法使用Git对其进行访问。

现在让我们讨论黄金法则。

黄金基准规则


黄金重设规则是:永远不要重设共享分支!”。共享分支是指网络存储库中存在的分支,除您之外,其他人也可以使用。

通常,在没有适当理解的情况下应用此规则,因此,我们将分析其出现的原因,特别是因为它将有助于更好地了解Git的工作。

让我们看一下开发人员违反黄金法则的情况,以及在这种情况下会发生什么。

假设Bob和Anna正在一个项目上。下面是Bob和Anna存储库以及GitHub上原始存储库的外观:


所有用户都有与GitHub同步的存储库。

现在,Bob打破了黄金法则,进行了重新设置,与此同时,在Feature分支中工作的Anna创建了一个新的提交:


你知道会发生什么吗?

鲍勃(Bob)试图执行推送提交;他拒绝了以下内容:


Git不成功,因为Git不知道如何将Bob的功能分支与GitHub的功能分支结合在一起。

允许Bob推送的唯一解决方案是使用force键,它告诉GitHub存储库删除feature分支,并接受Bob正在为该分支推送的功能。之后,我们得到以下情况:


现在,安娜想启动她的更改,这将发生以下情况:


Git告诉Anna,这是正常的,她没有功能分支的同步版本,也就是说,她的分支版本与GitHub中的分支版本不同。安娜必须完成任务。与Git推送时将本地分支与存储库中的分支合并的方式相同,Git尝试在拉动时尝试将存储库中的分支与本地分支合并。

在本地和GitHub分支中进行拉式提交之前,如下所示:

A--B--C--D'   origin/feature // GitHub
A--B--D--E    feature        // Anna

拉动时,Git合并以消除存储库中的差异。因此,这将导致:


提交M是合并提交。最后,Anna和GitHub的功能分支已完全合并。安娜松了一口气,所有的冲突都解决了,她可以进行一推。 

鲍勃在拉,现在一切都同步了:


在查看由此造成的混乱时,您必须确保黄金法则的重要性。还要记住,这种混乱是由一个开发人员在一个只有两个人共享的分支上创建的。想象一下,一个十人的团队。 

Git的众多优点之一是,您可以随时回滚而不会出现任何问题。但是犯的错误越多,例如所描述的,就越困难。

还要注意,重复的提交会出现在网络存储库中。在我们的例子中,D和D'包含相同的数据。实际上,重复提交的数量可以和您的基于基础的分支中的提交数量一样大。

如果您仍然不确定,让我们介绍一下第三位开发人员Emma。在Bob犯错并当前要推送之前,她在功能部门工作。假设到她推送我们之前的小脚本时,它已经完成。结果如下:


哦,那个鲍勃!!!

此文本可能使您认为rebase仅用于将一个分支移至另一分支的顶部。这是可选的-您可以基于同一分支。

美容拉底


如您在上面看到的,如果Anna使用pull rebase可以避免。让我们更详细地考虑这个问题。

假设鲍勃在与母公司不同的分支中工作,那么他的故事可能像这样:




鲍勃认为是时候该拉了,正如您已经了解的那样,这将导致一些混乱。由于Bob的存储库已从GitHub移开,因此Git将询问合并是否完成,结果将如下所示:


该解决方案可以正常工作,但是,对于解决该问题还有其他解决方案可能会很有用。其中之一是pull-rebase。

当您进行pull-rebase时,Git会尝试找出哪些提交仅在您的分支中,哪些提交在网络存储库中。然后,Git将网络存储库中的提交与本地和网络存储库中存在的最新提交进行组合。然后将本地提交重新部署到分支的末尾。 

听起来很复杂,因此我们举例说明:

  1. Git只关注您和网络存储库中的提交:

    它看起来像GitHub存储库的本地克隆。
  2. Git对本地提交进行重新设置:


您还记得,当重新设置基准时,Git一次提交提交,也就是说,在这种情况下,它先将主提交E,然后再将F.应用于分支的末尾,其结果就是对自己的重新确定。看起来不错,但问题来了-为什么这样做?

在我看来,合并分支机构的最大问题是提交的历史受到污染。因此,pull-rebase是一个更优雅的解决方案。我什至更进一步地说,当您需要将最新更改下载到分支时,应始终使用pull-rebase。但是您需要记住:由于重新设置基准会依次应用所有提交,因此,当您重新设置20个提交基准时,可能必须一个接一个地解决20个冲突。 

通常,您可以使用以下方法:很久以前进行的一项大更改是合并,最近进行了一次小更改进行了重新调整。

实力立足于


假设您的提交历史记录如下:




因此,您想将功能2分支重新设置为master分支。如果您在master分支上进行常规重新配置,请获取以下信息:


提交D同时存在于两个分支中(在功能1和功能2中)是不合逻辑的。如果将功能1分支移至master分支的末尾,则将提交两次提交D。

假设您需要得到不同的结果:


为了实现这种情况,git rebase正是所要的。

首先,阅读文档:

SYNOPSIS
       git rebase [-i | --interactive] [<options>] [--exec <cmd>]
               [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
       git rebase [-i | --interactive] [<options>] [--exec <cmd>] 
[--onto <newbase>]
               --root [<branch>]
       git rebase (--continue | --skip | --abort | --quit | --edit-todo 
| --show-current-patch)


我们对此感兴趣:

OPTIONS
       --onto <newbase>
          Starting point at which to create the new commits. If the 
--onto option is not specified, the starting point is <upstream>. May be 
any valid commit, and not just an existing branch name.


使用此选项指示创建新提交的时间。

如果未指定此选项,则上游将成为起点。

为了理解,我再给出一张图片:

A--B--C        master
    \
     D--E      feature1
         \
          F--G feature2

Here we want to rebase feature2 to master beginning from feature1
                           |                                |
                        newbase                         upstream


也就是说,master分支是newbase,feature 1分支是上游。

因此,如果要获得上图所示的结果,则必须在feature2分支中对主feature1运行git rebase --on。

祝好运

Mail.ru Cloud Solutions支持翻译

还有什么要阅读的主题

  1. Git指南的第一部分。
  2. 我第二年作为独立开发人员
  3. 我们关于数字转换的电报频道。 


All Articles