GIT rebase需要重新进行更改

时间:2015-01-09 08:19:34

标签: git bitbucket git-rebase

在其他任何事情之前,我只是git分支的新手。我不知道每个功能分支都应该从master扩展出来,并且只使用与下一个功能分支有关的先决条件功能分支。

我有三个分支。 masterfeature-1feature-2都推送了Bitbucket存储库(启用了问题跟踪)。事情是提交M4M5是关键提交,所有分支都应该在提交合并之前对其进行重新定义(git rebase的任务)

M1 -- M2 -- M3 -- M4 -- M5   [master]
  \        /
   A1 --- A2                 [feature-1]
           \
            B1 -- B2 -- B3   [feature-2]

feature-2的开发已完成,现在需要合并到master。以下是我feature-2M4M5提交feature-2提交的任务的优先顺序。

  1. git push - 开发feature-2
  2. git checkout feature-2
  3. git rebase master
  4. 解决冲突
  5. git pull
  6. 执行git status后,我已经注意到了这些步骤。我必须再次推送所有提交(feature-2M4M5和冲突提交。我所要做的就是做git push并启动拉取请求并完成,对吧?但这会在问题跟踪器中添加另一个git commit注释。

    有没有办法将feature-2重新定位到master,而无需再次推送feature-2M4M5以及{{ 1}}应该包含冲突提交。

    更新

    • 更改问题详细信息以获得更好的澄清

3 个答案:

答案 0 :(得分:11)

由于您已经推送了两个功能分支,因此根本不应该进行变基。强烈建议重新定位已发布的分支,因为它会破坏其他开发人员的存储库。原因是rebase只是提交的完全重写。您正在重新设置的提交将被重新创建,内容已更改,最重要的是 - 具有不同的哈希值。这导致新提交与旧提交不兼容,因此拥有旧提交的人最终会与替换它们的新提交冲突。

对此的正确解决方案是简单地合并更改。虽然这可能不会看起来那么漂亮,但它是一种非破坏性的动作,其中没有改变现有的提交。所有这一切都是提交添加,这将导致推或拉时没有问题。

话虽这么说,你可以改组并仍然发布已更改的分支。但要做到这一点,你需要强制推动分支机构,而其他开发人员需要将这些更改重置为新版本。


将下面的一些评论包含在答案中:重要的是要理解在Git中,分支只是提交的指针。整个历史 - 没有分支 - 是一个很大的非循环图,其中提交只指向其父提交。所以从问题中拿出你的例子,这是历史,没有任何分支指针:

A -- B -- C -- D -- E
 \       /
  F --- G
         \
          H -- I -- J

每个字母代表一个提交,所有提交一个连接到,左边是其父级。例如,F的父级为AC是与父级BG的合并提交。

现在,如果我们向该可视化添加分支,那么我们只需添加指向某些提交的指针。它真的没什么别的(分支实际上只是一个包含提交哈希的文件):

                  master
                    ↓
A -- B -- C -- D -- E
 \       /
  F --- G  ← feature-1
         \
          H -- I -- J
                    ↑
                feature-2

现在,假设我们提交feature-2分支。我们将该提交添加到树中......

         \
          H -- I -- J -- K
                    ↑
                feature-2

...然后我们向前移动分支指针:

         \
          H -- I -- J -- K
                         ↑
                     feature-2

现在,为了理解推送过程中发生的事情,我们需要记住远程分支也只是分支,因此只是另一组指针。所以它实际上看起来像这样:

         \
          H -- I -- J ----------- K
                    ↑             ↑
           origin/feature-2    feature-2

我认为您可以想象现在推送期间会发生什么:我们告诉远程存储库更新其分支指针,使其指向K。但是服务器只有J,所以我们需要给服务器一些东西来构造K可访问的树(所以其中的任何其他提交,以及这些提交的所有实际内容)。但是我们当然不需要实际推送JH,甚至A(尽管feature-2上的所有技术 }分支,因为你可以到达他们); Git非常聪明,可以找出实际丢失的对象(你可以看到Git在你开始推送时计算它)。

因此,一旦我们将丢失的对象转移到远程存储库,我们就告诉远程存储库更新其feature-1指针,因此它也将指向K。如果成功,我们也会更新我们的远程分支(origin/feature-2)以指向它(只是为了同步)。

现在,合并情况确实如此。想象一下,我们将master合并到feature-2(在git merge master时使用feature-2):

                  master
                    ↓
A -- B -- C -- D -- E -----
 \       /                 \
  F --- G  ← feature-1      \
         \                   \
          H -- I -- J -- K -- L
                              ↑
                          feature-2

现在,如果我们想推送feature-2,我们再次需要为远程存储库提供它没有的所有对象。由于我们现在正在进行合并提交,我们需要检查所有父项:因此,如果服务器没有K,我们需要推送K;但是,如果它没有E,我们必须推E。当然,我们需要再次关注这些父母,以确保遥控器上存在所有对象。一旦完成,我们再次告诉遥控器更新分支指针。

所以总结一下:分支包含所有提交,通过在非循环树中导航其提交的父级来以某种方式可访问。但即使这意味着分支通常非常“大”(历史长度),Git只会将这些对象传输到它没有的远程存储库。因此,虽然合并可以向分支添加更多提交,但如果远程已经从另一个分支知道它们,则不一定必须传输这些提交。


最后,关于变基的最后一句话:上面我们git merge mastermaster分支合并到feature-2。如果我们改为git rebase master,那么完整的树现在看起来像这样:

                  master               feature-2
                    ↓                      ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
 \       /
  F --- G  ← feature-1
         \
          H -- I -- J -- K
                         ↑
                  origin/feature-2

如您所见,有新的提交H'I'J'K'。这些是重写的提交,因此它们从E(在rebase时指向master处)而不是G开始。由于我们重新定位,因此没有合并提交L。如上所述,原始提交仍然存在。只是没有指针指向它们;因此他们“迷失”并最终将被垃圾收集。

那么现在推的问题是什么?远程分支仍指向原始K,但我们希望它现在指向K'。因此,我们开始为远程存储库提供所需的所有对象,就像之前一样。但是当我们告诉它设置更新分支指针时,它会拒绝这样做。这样做的原因是,通过将指针设置为K',它必须“返回历史记录”并忽略提交HK的存在。它不知道我们已经重新定义了那些,并且重写的和原始的之间也没有联系。因此,为了防止意外数据丢失,远程将拒绝更新分支指针。

现在,您可以强制推送分支。这将告诉远程存储库更新分支指针,即使这样做会抛出那些原始提交。所以你这样做,情况将是这样的:

                                   origin/feature-2
                  master               feature-2
                    ↓                      ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
 \       /
  F --- G  ← feature-1

到目前为止,一切都很顺利:你决定对分支进行rebase,你告诉远程存储库接受它而不用质疑它。但现在想象我想那个;我的分支仍然指向I。因此,run pull与反向推送相同:遥控器为我提供了完成历史记录所需的所有对象,然后它告诉我在哪里设置分支指针。在那时,我的本地Git拒绝这样做,原因与远程存储库之前做的相同。

通过之前的推动,我们知道我们想要替换原始提交;但是我们没有那个,所以我们现在需要调查或询问我们是否应该只更换我们的本地分支,或者是否实际上有一些故障。如果我们在当地做了一些我们想要合并的工作,情况会变得更糟。

这些问题发生在每次获取原始提交的人身上。一般来说,你想完全避免这种混乱,所以规则是永远不要改变你已经发布的东西。只要没有其他人得到那些原始提交,你就可以进行改造,但是一旦不再这样,那么对于所有参与者来说都会是一团糟。所以合并绝对是首选。

答案 1 :(得分:2)

据我了解,您的代码库(团队)规则要求您针对master重新设置功能分支。你可以这样说git rebase --onto master A2 feature-2这意味着“接受以A2独占的特征-2的提交并将它们置于主人之上”。然后,您可以将更改直接推送到master,同时删除或保持feature-2分支不变,这取决于工作流程约定。

另一方面,如果不要求rebase - 您可以将feature-2简单合并到master,将更改推送到master,如@poke建议。

答案 2 :(得分:1)

  

有没有办法将feature-2重新定位到master,而无需再次推送feature-2的提交

不是真的,考虑到一个rebase将重播master上的feature-2的提交,给你这个:

M1 -- M2 -- M3 -- M4 -- M5   [master]
  \        /              \
   A1 --- A2 [feature-1]   A1' --- A2'
                                     \
                                      B1' -- B2' -- B3'   [feature-2]

'符号所示,所有提交都已更改(其SHA1不同)。

由于A1A2已经合并到master,因此更好的基础会是

git rebase --onto master A2 feature-2

那会得到:

M1 -- M2 -- M3 -- M4 -- M5   [master]
  \        /              \
   A1 --- A2 [feature-1]   B1' -- B2' -- B3'   [feature-2]

您应该git push --force新修订的feature-2分支,以便更新您的提款请求。

强制推送应该更新Bitbucket中的拉取请求,因为它支持自2012年第四季度以来的推送(issue 4913)。 强制推动不会产生任何其他不利影响,考虑到你正在推动分支到你的分支,大概是你是唯一推动更新的分支。
由于拉取请求会自动使用feature-2的新历史记录自行更新,因此不会影响所述拉取请求。