远程git rebase如此邪恶的详细原因

时间:2016-08-25 21:11:28

标签: git git-rebase

所以我来自一个集中的VCS背景,并试图确定我们在Git(新公司,年轻代码库)的工作流程。我无法找到一个简单而详细的答案的一个问题是远程分支上的rebase究竟是做什么的。我理解它会重写历史记录,一般应仅限于本地分支机构。

我目前正在尝试审核的工作流程涉及一个远程协作分支,每个开发人员都拥有"一个用于共享代码的目的。 (在可预见的未来,有2个开发人员和最多3个开发人员,每个项目和功能请求的功能分支似乎过多,而且开销比获得的收益更多。)

然后我遇到了this answer并尝试了它并完成了我想要的东西 - 一个开发者提交并经常推送到他自己的合作分支,当他知道什么被批准发布给他时在合并到开发之前,可​​以远程(压缩并重新组织)进行rebase。

输入原始问题 - 如果远程分支是出于协作的目的,其他人必须迟早将其拉出来。如果这是一个过程/培训问题,那么就没有“客户开发人员”这样的问题。提交到那个协作分支,分支所有者实际发生了什么,重新定义了那个远程分支?

2 个答案:

答案 0 :(得分:9)

它并非真正邪恶,这是实施和期望的问题。

我们从一堆事实开始:

  • 每个Git哈希都代表一些独特的对象。出于我们的目的,我们只需要考虑 commit 对象。每个哈希是将加密哈希函数(对于Git,特别是它的SHA-1)应用于对象内容的结果。对于提交,内容包括源树的ID;作者和提交者的姓名和电子邮件地址以及时间/日期戳;提交消息;最重要的是,提交的ID。

  • 即使只改变内容中的一个位,也会产生一个新的,非常不同的哈希ID。散列函数的加密属性,用于验证和验证每个提交(或其他对象),也意味着没有办法让某些不同的对象具有相同的散列ID。 Git也会依赖于在存储库之间传输对象。

  • Rebase通过将提交复制到新提交来工作(必要)。即使没有其他变化 - 通常,与新副本相关联的源代码与原始源代码不同 - rebase的整个 point 是重新父级某些提交链。例如,我们可以从:

    开始
    ...--o--*--o--o--o   <-- develop
             \
              o--o       <-- feature
    

    其中分支feature在提交develop与分支*分开,但现在我们希望featuredevelop的提示提交下降,所以我们改变它。结果是:

    ...--o--*--o--o--o        <-- develop
             \        \
              \        @--@   <-- feature
               \
                o--o          abandoned [used to be feature, now left-overs]
    

    其中两个@是原始两次提交的副本。

  • 分支名称,如develop,只是指向(单个)提交的指针。我们倾向于认为的事情是&#34;分支&#34;,就像两个提交@--@一样,是通过从每个提交向后工作到其父代来形成的。

  • 分支机构总是希望增加新的提交。发现developmaster添加了一些新的提交是完全正常的,因此该名称现在指向提交 - 或许多提交的最后一个 - 指向返回的位置名称​​使用指向。

  • 每当你让你的 Git与其他Git及其他存储库同步(无论何种程度)你的存储库时,你的Git和他们的Git都有一个交换ID特别是哈希ID。究竟哪些ID取决于转移的方向,以及任何分支名称,您要求Git使用。

  • 远程跟踪分支实际上是您的 Git存储的实体,与您的存储库相关联。您的远程跟踪分支origin/master实际上是您Git记住的地方&#34; origin的Git所说的他的 {{1}是,我们最后一次谈话。&#34;

所以,现在我们采用这七个项目,看看master的工作原理。例如,您可以运行git fetch。此时,你的Git在git fetch origin上调用Git并询问它的分支。他们说originmaster = 1234567之类的东西(尽管散列值都是40个字符长,而不是这些7个字符)。

您的Git可能已经拥有这些提交对象。如果是这样,我们差不多完成了!如果没有,它会要求他们的Git发送这些提交对象,以及Git需要的任何其他对象来理解它们。附加对象是与这些提交一起使用的任何文件,以及那些提交使用您尚未拥有的任何父提交,以及父母&#39;父母等等,直到我们找到你所拥有的一些提交对象。这将为您提供所有新历史记录所需的所有提交和文件。 1

一旦你的Git安全地存储了所有对象,你的Git就会用新的ID更新你的远程跟踪分支。他们的Git刚告诉你他们的branch = 89abcdemaster,所以现在你的1234567设置为origin/master。他们的1234567也是如此:它成为您的branch,而您的Git会保存origin/branch哈希值。

如果您现在89abcde,您的Git会使用git checkout branch创建一个新的本地标签,指向origin/branch。让我们画一下:

89abcde

(我已将此...--o--*--o--1 <-- master, origin/master \ o--8 <-- branch, origin/branch 缩短为1234567,将1简化为89abcde,以使其更适合。)

为了让事情变得非常有趣,让我们在8上进行拥有新提交。让我们说它编号branch

aaaaaaa...

(我将...--o--*--o--1 <-- master, origin/master \ o--8 <-- origin/branch \ A <-- branch 简化为aaaaaaa...)。

然后,有趣的问题是,如果他们是获取某些东西的Git会发生什么。例如,假设他们将A重新定义为branch。这会复制一些提交。现在您运行master并且您的Git看到他们说git fetch。你的Git检查你是否有这个对象;如果没有,你得到它(及其文件)及其父(以及那些提交文件)等等,直到我们达到一些共同点 - 事实上,它将提交branch = fedcba9

现在有这个:

1234567

我已在...--o--*--o--1 <-- master, origin/master \ \ \ o--F <-- origin/branch \ o--8--A <-- branch 提交F提交fedcba9origin/branch现在指向。

如果你稍后发现这一点而没有意识到上游人员重新定位他们的branch(你的origin/branch),你可能会看到这一点,并认为必须写 o--8--A链中的所有三个提交,因为它们已经在branch而不在origin/branch上了。但是他们不在origin/branch上的原因是上游放弃了他们而不是新副本。事实上,很难说这些新副本是副本,而且你也应该放弃这些提交。

1 如果分支在&#34;正常&#34;,&#34;预期&#34;方式,你的Git和他们的Git非常容易找出你的Git需要从哪些提交:origin/master告诉你上次看到他们master的位置,现在他们{ {1}}指向更长的链条。您需要的提交正是 master master提示后的提交。

如果分支机构以不太典型的方式进行改组,那就更难了。在最常见的情况下,他们只需要通过哈希ID枚举 all 他们的对象,直到你的Git告诉他们已经达到了你已经拥有的对象。浅克隆使具体细节变得更加复杂。

这不是不可能的

它不是不可能告诉,而且由于Git版本2.0左右,现在有内置的工具可以让Git为你找到它。 (具体而言,由origin/master调用的git merge-base --fork-point使用git rebase --fork-point的reflog来确定origin/branch链曾经在o--8上这只适用于保留这些reflog条目的时间段,但这默认为至少30天,给你一个月的时间赶上。的30天时间线:运行origin/branch后的30天,不管上游多长时间都进行了变基。)

这真正归结为如果您和您的上游事先同意某些特定的分支机构得到重新定位,您可以安排在 中执行任何操作存储库每次执行此操作。然而,通过一个更典型的开发过程,你不会期望它们会变形,如果他们不这样做 - 如果他们从不放弃&#34;您已经提取的已发布的提交 - 然后您无需从恢复

答案 1 :(得分:1)

发布(远程)分支的重新定位(或重写历史)的主要问题是,基于它们重新整合工作变得很困难。因此,如果这些遥控器仅用于审查而且没有提交,即使是合并,也不会在那些通常没有很多问题的情况下进行。否则,合并和解决冲突可能很快就会成为主要的烦恼。