git - 了解git rebase和 “golden rule”

标签 git version-control

我在一个大型团队中工作,最近我们从TFS转到了Git。

我们已经有一些工作被覆盖的问题,并且相信git pull --rebase是我们想要做的,因此与其直接 merge ,不如直接 merge 它在本地分支上重播 pull 出的提交。

但是,我们一直在阅读黄金法则,但仍不确定是否可以使用rebase。

我们的理解是,当您使用重定基向远程分支进行推送时,黄金法则就会发挥作用。从https://medium.freecodecamp.org/git-rebase-and-the-golden-rule-explained-70715eccc372

You have probably came across that rule, maybe phrased differently. For those who haven’t, this rule is quite simple. Never, NEVER, NEVER, rebase a shared branch. By shared branch I mean a branch that exists on the distant repository and that other people on your team could pull.



所以这很糟糕:
  • Charles正在寻求支持,并使用git pull --rebase与远程
  • 同步
  • Taylor正在使用功能A,并使用git pull --rebase与远程
  • 同步
  • Charles使用--rebase merge 了对母带的支持,并简化了历史记录,Taylor的更改是在他尝试对Master进行正常 merge 时进行的。

  • 可以:
  • Charles会pull origin support --rebase
  • Taylor做pull origin feature-a --rebase
  • 都使用push到远程分支而不使用--rebase,并 merge 到主

  • 本质上,我们不想在每次推送时都使用--rebase,对吗?

    最佳答案

    实际上,没有任何一种简单,通用的规则在任何情况下都适用于每个人。 “永远不要让任何东西 rebase 础”是一个简单的规则,确实可以,但是并不适合所有人。有一个更复杂的规则始终有效:只要拥有原始提交的每个人都愿意切换到新副本,Rebase就可以了。

    描述

    您以及与您一起工作的每个人所需要知道的是,Git对待 promise 非常宝贵,并且努力工作以确保您永远不会丢失任何东西。但是分支名称的重要性要小得多。分支名称充当指针,指向特定的提交。分支名称使Git可以找到提交,但重要的是提交。当您使用git rebase时,您是在告诉Git将一些现有(旧)提交复制到一些新提交中。新的将获得新的“真实名称”(请参见下文)。将旧的提交复制到新的提交有很多原因,包括没有提交可以更改的事实。制作完成后,它(通常)是永久性的且不可更改。

    您可以做一件事使提交似乎消失:您可以完全停止使用它。一旦没有人使用它很长一段时间,并且它没有名称,也找不到它,它的确会消失。但是提交是很宝贵的,因此这需要一段时间。 (这背后的实际机制是reflog,您可以在其他地方阅读更多信息。)

    一个简单的过程

    现在,提交具有丑陋的哈希ID作为其“真实名称”。这些名称很难使用:例如d35688db19c9ea97e9e2ce751dc7b47aee21636b4d7268b888d7bb6d675340ec676e4239739d0f6d的相对关系是什么?谁知道,它们只是个丑陋的数字,似乎是随机的。这些是Git真正关心的名称,而不是我们仅人类使用的名称:我们使用诸如masterv2.14之类的名称。

    这就是分支(和标签)名称的来源。Git让我们选择一个分支名称,例如master,用于一段时间内标识d35688db19c9ea97e9e2ce751dc7b47aee21636b。最终,我们向master分支添加了一个新的提交,并且新的提交获得了一个新的,又大又丑陋的真实名称-Git更改了master以将其映射到新名称。这是一个简单的示例,在一个仅以三个提交开始的存储库中:

    A <-B <-C   <--master
    

    名称master“指向” commit C(无论如何C代表c0193fa8...-不管怎么说,它是一个非常丑陋的哈希ID)。分支名称保留当前的分支提示哈希ID,作为Git自动执行的服务。我们之所以说“指向”,是因为Git将在名称之后跟随ID来获取提交。 Commit C依次指向(存储)commit B的哈希ID,commit B点指向commit A。由于A提交是有史以来的第一个提交,因此它不会指向任何地方:它是一个根提交。这就是Git知道在哪里停下来的方法。

    要立即添加新的提交,请在存储库工作树中工作,编辑文件git addgit commit。 Git现在编写一个新的提交(我们将其称为D),它指向C,并将名称master更改为指向您的最新提交:
    A--B--C--D   <-- master
    

    (内部箭头始终指向后方,更新的箭头指向较旧的箭头,因此没有真正的必要继续绘制它们。很难在更复杂的图中绘制它们。)

    merge

    只要您所做的唯一一件事就是添加新的提交,就可以很好地工作,因为Git是围绕添加新的提交的整个想法而构建的。如您的示例所示,如果Charles和Taylor都向某个分支添加了新的提交,然后其中一个或任何第三方去获取您的新提交,则Git能够添加您的提交及其提交一起。如果添加的内容很简单:
    A--B--C--D--E--F   <-- master
    

    然后名称master只会随着向右添加新提交而继续向右移动。如果添加是并行进行的,则Git必须添加“merge 提交”以记录两个较新的提交。比如说,查尔斯添加了E,泰勒添加了F,两者的工作速度都不比另一个快,所以我们得到了这一点:
               E
              /
    A--B--C--D
              \
               F
    

    现在我们有一个问题:master只能指向一个提交。如果选择E,则无法处理F。如果我们选择F,则会丢失E。 merge 工作流通过在EF之上添加一个新的提交G来处理此问题,该提交指向EF:
               E
              / \
    A--B--C--D   G   <-- master
              \ /
               F
    

    这很好用,但是随着时间的流逝,您会得到很多 merge 气泡,这些气泡仅用于记录人们并行工作的事实。尽管这是该项目的正确,真实的历史记录,但它充满了无用的细节。这是重新部署的地方。让我们看一下它是如何工作的。

    rebase

    Rebase复制了一些提交,然后(至少在理想情况下)复制了您和其他拥有原始提交的所有人,都停止了使用原始提交。相反,您和其他所有人都使用了 Shiny 的新提交。

    例如,假设我们有:
               E   <-- origin/master
              /
    A--B--C--D
              \
               F   <-- master
    

    在Taylor的存储库中,提交E的名称为origin/master,提交F的名称为master。 (请记住,Taylor制作了F。)

    Taylor是唯一拥有F的程序员。

    Taylor现在可以选择-这是一个选择,而不是必须-将提交F复制到更新的,更漂亮的F',至少有一个区别:F'将指向的不是D,而是指向E

    (Taylor的新F'应该至少有另一个区别:他也应该接受Charles在E中所做的任何工作。)

    如果泰勒要跑:
    git rebase origin/master
    

    他的Git会找到要复制的提交列表,该列表只会列出F。然后它将F复制到F'之上构建的E:
                 F'  <-- [temporary]
                /
               E   <-- origin/master
              /
    A--B--C--D
              \
               F   <-- master
    

    现在到了棘手的部分:Taylor的Git将移动Taylor的master指向复制的提交,从而放弃原始的F。结果是相同的图,但名称已移动:
                 F'  <-- master
                /
               E   <-- origin/master
              /
    A--B--C--D
              \
               F   [abandoned]
    

    由于F现在已被放弃-它没有明显的名称-Git停止显示它。提交仍然存在,因为(大多数)提交是永久的并且(完全)不变,但是最终它将消失,因为泰勒是唯一拥有提交的人,并且他更改了master的名称,以便master不再找到它。从master开始,他的Git依次找到F'EDC,等等—它再也看不到原始的F了。

    如果我们停止完全绘制F,而将新提交的git push F'停止,以便其他人现在都可以看到,我们得到:
    A--B--C--D--E--F'   <-- master, origin/master
    

    (我们已经更新了origin/master,因为我们推送了F',所以现在originmaster也指向F',泰勒的Git知道了这一点,并相应地调整了他的origin/master)。

    猴子 Spanner

    但是现在假设泰勒在 rebase 之前先推送F。假设他在分支F上而不是在feature上创建master,因此他在重新设置基准之前就拥有了它:
               E   <-- origin/master
              /
    A--B--C--D   <-- master
              \
               F   <-- feature
    

    通过将F推送到feature上的origin,Taylor在另一个Git中创建一个分支origin。 Taylor自己的Git通过命名为origin/feature(也指向F)来记住这一点:
               E   <-- origin/master
              /
    A--B--C--D   <-- master
              \
               F   <-- feature, origin/feature
    

    现在,如果Taylor执行与以前相同的 rebase 操作,他将获得与以前相同的F',但是这次有一个F的名称:
                 F'  <-- feature
                /
               E   <-- origin/master
              /
    A--B--C--D   <-- master
              \
               F   <-- origin/feature
    

    由于位于origin的另一个Git存储库具有指向feature的名称F,因此其他任何人(例如Charles)都可以选择commit F

    现在,请记住,提交是很宝贵的。分支名称可以更改,但是提交是永久且不变的,因此,如果Charles具有F,而Taylor推送F',则Charles仍然具有commit F

    如果Charles知道自己在做什么,也知道Taylor在做什么,那么通过移动任何允许他访问F的分支名称,Charles可以让Git忘记F。一旦Charles放弃了F,而Taylor放弃了F,而其他所有拥有F的人都放弃了F,那么-直到那时,F最终才能真正消失。

    因此,使用git rebase的规则实际上是这样的:只要拥有原始文档的每个人都愿意切换到 Shiny 的新提交,Rebase就可以了。如果您(或泰勒)是唯一拥有原件的人,那会容易得多。如果您已将那些原始提交复制到其他存储库中,那么会有更多的人不得不告诉他们的Git切换。

    要 merge 还是要 rebase ?就是那个问题

    merge 更容易,因为 merge 意味着“添加 merge 的提交”。您可能需要在 merge 操作(动词 merge )中做一些工作,但是一旦完成,它就完成了; Git进行了一个 merge 提交,该提交不仅记录了一个先前的提交,而且还记录了两个提交,并且新的提交被添加到其中,每个Git都会自动知道该如何处理。

    但这会使您的历史变得无用。调整基础是更多的工作-可能复制许多提交,并让拥有旧提交的每个人都切换到新提交。在此过程中有更多出错的机会。更糟糕的是,每个复制步骤实际上是另一个 merge 即动词。不过,一旦完成所有操作,您将拥有光鲜,外观清晰的历史,就好像您刚开始时就确切知道要去的地方,并且从未做过任何错误的举动。看到发生的事情要容易得多,或者更确切地说,是您希望人们认为发生的事情,这与实际发生的事情不同。

    由您(和每个人)决定要做什么。有时重新定价既容易又好。有时候,这是艰难而美好的。有时,这太难了,或者不是一个好主意,因为它掩盖了确实或可能会造成影响的太多真实历史。您必须立即做出判断:将来会不会很重要?如果现在改头换面,明天,下个月或明年工作会更容易吗?还是会更困难?

    关于git - 了解git rebase和 “golden rule”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46656240/

    相关文章:

    git - 使用子目录中的 git archive 来归档整个项目树

    git - git add 的文件模式是否有否定语法?

    git - 更改以模式开头的提交的提交作者(在消息中)

    Git GUI/TortoiseGit 没有调用正确使用 powershell 的 Hook ? ( window )

    Git 将 origin/master 中的所有提交压缩为 origin/master 中的单个提交

    visual-studio-2008 - 如何阻止VS2008不必要地修改CSPROJ文件?

    git - 位桶 ssh_exchange_identification : read: Connection reset by peer

    php - OpenShift 操作 Hook 部署问题

    macos - 混合版本控制与 Mac OS X Lion 版本

    svn - 如何实用地控制配置文件的版本?