从以前的分支制作的分支的PR被压扁并合并显示前一个分支的提交

问题描述 投票:0回答:1

注意:对不起标题。我不确定如何简明扼要地说出我的挑战,因此将这个问题标记为重复(准确)将非常有帮助。

以下是相关行动的时间表,它将构成我的问题的基础:

  1. 为要素A打开PR以打开
  2. 特征B从特征A分支,继续工作。
  3. 功能A被压缩并合并为主。
  4. 打开PR以使功能B掌握

问题:功能B的PR显示来自功能A的所有先前(未撤销)提交。

我如何,最好不要手动删除所有功能A提交或挑选功能B提交,在主服务器上重新设置功能B并在PR中仅显示来自A..B的提交?

git github
1个回答
1
投票

TL;DR

你必须挑选,即使你不想。您可以使用git rebase --onto以高度自动化且经常但并非总是轻松无痛的方式进行挑选。

Description

据我所知,GitHub本身(这不是那么远),在这里完全没用。但是,你可以在shell级别的Git中做你需要做的事情。

简要背景回顾:当你在Git中构建一个分支时,你真正在做的是添加提交,通常一次一个。提交的基本单位和raison d'être是提交。每个提交都由像95ec6b1b3393eb6e26da40c565520a8db9796e9f这样的哈希ID唯一标识。 No two different Git objects ever have the same hash ID.每个提交几乎都是一个独立的实体,包含源代码的完整快照。 “几乎”部分是因为大多数提交包含,作为其metadata的一部分,一个先前提交的哈希ID,我们称之为提交的父提交。像feature-A这样的分支名称包含一个提交的哈希ID,Git称之为分支的提示提交。

当你git checkout feature-A,编辑,git add文件,和git commit结果,你创建一个新的提交。新提交的父级是提交,这是提示,你有git checkout-ed。它的快照是原始提交中的所有文件,除了那些git add用您编辑的新内容覆盖的文件。作为一个全新的提交,它获得一个新的,唯一的哈希ID,然后Git将新ID存储到分支名称中,这样您刚刚提交的新提交现在是feature-A的提示。

The problem

到目前为止,这并不是非常有趣,但我们应该注意这些提交是如何链接在一起的,一次一个,建立在之前的提交上:

          1   <-- feature-A (HEAD)
         /
...--o--o   <-- master

成为:

          1--2   <-- feature-A (HEAD)
         /
...--o--o   <-- master

最终成为:

          1--2--3--4--5   <-- feature-A (HEAD)
         /
...--o--o   <-- master

然后你提出了一个拉取请求:“请获取这些新的提交1-2-3-4-5并做一些事情来合并它们。”谁是你的上游最终确实获得了这些提交并合并它们,但是 - 这就是问题 - 他们使用GitHub的“壁球和合并”功能按钮,内部运行git merge --squash,它根本不包含这些提交。

相反,git merge --squash所做的是使用Git的合并机制来进行“合并作为动词”组合变化的过程,但随后进行全新的提交。在他们的上游,他们可能已经添加了一些其他新的提交,所以当他们提交你的提交1-2-3-4-5他们有:

          1--2--3--4--5   [imported - no name]
         /
...--o--*--A--B   <-- master

他们让他们的Git(和GitHub)结合了从提交*(合并基础)到B的变化,即他们做了什么,从*5的变化,即你做了什么,并从中做了一个新的提交C结果。因为这是--squash操作,所以新提交不会记录其第二个父级,使图形看起来像这样:

          1--2--3--4--5
         /
...--o--*--A--B---------C   <-- master

当你可能希望它看起来像这样:

          1--2--3--4--5
         /             \
...--o--*--A--B---------C   <-- master

但它没有额外的联系,所以现在你必须处理这个问题。

Meanwhile, you made more commits

您继续在自己的存储库中创建了一个feature-B分支:

          1--2--3--4--5   <-- feature-A, feature-B (HEAD)
         /
...--o--o   <-- master

你现在做了一些提交:

                        6  <-- feature-B (HEAD)
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master

最终导致:

                        6--7--8  <-- feature-B (HEAD)
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master

在某些时候,您甚至可能从您的上游(他们的Git存储库)获得了他们的提交A-B-C。如果你还没有这样做,你现在应该这样做:

                        6--7--8  <-- feature-B (HEAD)
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master

请注意,他们的提交C大致相当于添加你的提交1-2-3-4-5,除了C的父亲是B,而不是那个(并且在这张图中仍然是)你的master的提示。

您现在要做的是复制提交链6-7-8,除了您希望将这些副本基于提交C,而不是提交5。也就是说,您想要的结果如下所示:

                        6--7--8  [old feature-B, to be abandoned]
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master
                 \
                  C6-C7-C8   <-- feature-B

Git命令集复制提交,并在此过程中使副本有一个新的基础,是git rebase。但是,如果你只是运行:

git checkout feature-B && git rebase upstream/master

Git将选择复制那些可以从名称feature-B而不是名称upstream/master访问的提交。这里可达到的单词意味着如果我们从提示开始,并按照Git的方式向后工作,我们将遇到哪些提交?我们将从提交8开始,然后到达(通过其父哈希)提交7,然后6,依此类推向左边的链。最终我们将达到你的master的提示,并继续向左。但是如果我们从upstream/master开始并向后工作,我们将达到你的master的提示并继续向左 - 所以这些提交是那些没有复制的提交。离开提交1-2-3-4-5-6-7-8被复制。

再次,这就是问题:这里有太多的提交。我们想要提前停止提交5,以便我们只复制6-7-8链。这是我们使用git rebase --onto而不仅仅是git rebase的地方。

Using --onto

git rebase完成它的工作时,它必须选择两件事,而不仅仅是一件事:

  • 我们应该复制哪些提交?更准确地说,哪些提交不应该复制?我们将一些提交复制到当前提交,但是限制是什么?我们有什么不复制的?
  • 我们应该把副本放在哪里?

通常我们只是说git rebase upstream/master,它从这个名字中找出了这两个。副本位于命名提交之后,我们复制的提交是我们无法从命名提交中获取的提交。

使用git rebase --onto upstream/master,我们明确地告诉Git:在upstream/master的提示之后放置副本。这留下了另一个参数来指定限制:不要复制。我们想告诉Git:不要先复制提交5或其他任何东西。所以我们需要找到提交5的哈希ID,或者找到提交5的东西。

分支名称feature/A指向提交5。看看我们上面绘制的图表:它就在那里!或者,运行git log --all --decorate --online --graph并查看Git将绘制的图形。是否存在结束Git不应复制的链的提交的名称?如果是这样,您可以使用该名称。如果没有,您只需输入原始哈希ID即可。

在我们的例子中,只要没有名称改变了他们指向的提交,我们就可以运行:

git checkout feature-B
git rebase --onto upstream/master feature-A

这告诉Git要检查(进入提示,并记录名称)feature-B;然后,在当前提交结束时,复制一些提交,在upstream/master指向的提交之后放置副本。副本以当前提交结束,并以删除以feature-A结尾的提交后的任何内容开始。

那当然是提交6-7-8。因此Git将git checkout --detach upstream/master,使HEAD直接指向(没有分支名称)提交:

                        6--7--8  <-- feature-B
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master, HEAD

然后Git将复制提交6,就像在其哈希ID上执行git cherry-pick一样:

                        6--7--8  <-- feature-B
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master
                 \
                  C6   <-- HEAD

如果情况顺利,Git将挑选提交7

                        6--7--8  <-- feature-B
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master
                 \
                  C6-C7   <-- HEAD

然后重复8成为C8;最后,Git将撕下旧链feature-B上的标签6-7-8并将其粘贴到新副本C6-C7-C8的末尾:

                        6--7--8  [abandoned]
                       /
          1--2--3--4--5   <-- feature-A
         /
...--o--o   <-- master
         \
          A--B--C   <-- upstream/master
                 \
                  C6-C7-C8   <-- feature-B (HEAD)

有标签feature-B指向C8,Git将重新附加HEAD到该标签,并且rebase现在已完成,您可以发出一个拉取请求,要求您的上游人员将提交C6-C7-C8合并到他们的存储库中。

© www.soinside.com 2019 - 2024. All rights reserved.