使分支保留提交基于另一个分支上的提交

时间:2019-11-15 21:08:29

标签: git git-branch git-commit git-rebase

对不起,标题是否引起误解,但我不确定如何描述我遇到的情况。

我像这样提交和分支

A --- B --- C --- D (master)
                   \
                    E (another)

,我想从B分支中删除提交CD(保留master),但将它们保留在基于master的another分支中。 因此,转换后,我的树应如下所示:

A --- D (master)
 \
  B --- C --- E (another)

我想,我可能应该只是对master进行基准调整,但是我不确定B是否还会包含Canother从中删除/省略D

我应该如何实现上述效果?

3 个答案:

答案 0 :(得分:2)

假设您要移动变更集,应该不难:

git rebase --onto A C master

这会将分支master移动到A的顶部,放弃直到C的修订(所以只有D将与分支指针一起移动)。然后:

git rebase --onto C D another

这将使E在C的基础上,放弃修订直到D(换句话说,也仅将E在C的顶部移动...也移动分支指针)。

那应该做。

答案 1 :(得分:2)

要获得所需的结果-嗯,至少要达到 的结果-您必须停止使用现有的提交D和{{1} },其原因是没有人(不是您本人还是Git本身)可以完全更改有关 any 现有提交的任何,以及之间 / em>提交实际上是存储在父/子对子代中的 中的哈希ID。

也就是说,给定第一个图形,提交E是根提交:它没有父项。没有箭头指出在A之前的提交是_____ ,因为在A之前没有提交。但是提交A 确实有一个箭头,指向它指向提交B在我之前的提交是提交A 。提交A包含指向C的箭头; B包含指向D的箭头;并且C包含指向E的箭头:

D

commits 不同,分支名称​​ 可以更改:它们充当指向您选择的任何提交的箭头。因此,A <-B <-C <-D <-E 当前指向现有提交master,而D指向现有提交another。 Git可以从E开始查找another,使用E查找E,使用D查找D,依此类推;或Git可以从C开始找到master,然后找到DCB

您想要的结果具有指向A的提交B和指向A的{​​{1}},因此现有的提交通过C都很好但是,您希望使用B的新改进版,它不用指向C,而是直接指向D

此经过改进的C可能具有快照,而该快照没有现有的提交。要制作A的快照,您希望Git采取D'D'中的快照之间的差异,并将该差异应用于C中的快照。

Git可以自动执行此操作。执行此操作的基本Git命令是D。稍后,我们将介绍如何使用A来运行(正确的git cherry-pick命令集){em>,,但是让我们从选择樱桃本身开始。

类似地,您希望获得git rebase的新的和改进的副本,我们可以将其称为git cherry-pick,其中的改进是:

  • 指向E,而不是E';和
  • 具有快照,该快照是通过将快照CD之间的差异应用于D中的快照而制成的。

同样,这是E的工作。因此,让我们看看如何做到这一点。

使用C

要制作父级为git cherry-pick的经过改进的git cherry-pick,我们必须首先D'提交A本身,最好在其中附加一个临时分支名称,以避免混乱。 (在内部,使用git checkout,Git使用 no 临时分支名称来完成所有这些操作。)因此,我们将运行:

A

这给了我们

git rebase

现在我们像这样使用git checkout -b temp <hash-of-A>

A   <-- temp (HEAD)
 \
  B--C--D   <-- master
         \
          E   <-- another

此副本会将提交git cherry-pickgit cherry-pick <hash-of-D> # or: git cherry-pick master 指向该提交)(我们可以通过其哈希ID或名称D给予它)到新提交master ',现在master指向。 (每次进行新提交时,Git都会将新提交的哈希ID存储在 current 分支中:附加了一个D。因此temp现在指向复制{ {1}}。)

HEAD

现在,我们需要另一个新的临时分支,指向提交temp,因此我们运行D'。 (除了原始哈希,我们可以使用Git必须找到提交A--D' <-- temp (HEAD) \ B--C--D <-- master \ E <-- another 的任何其他方式,例如C,但是只要您剪切,原始哈希就可以使用剪切粘贴功能。正确的。)这给我们:

git checkout -b temp2 hash-of-C

(请注意,由于Cmaster~1现在如何附加到A--D' <-- temp \ B--C <-- temp2 (HEAD) \ D <-- master \ E <-- another 上了。)现在我们选择HEAD来完成temp2的制作:

git checkout -b

会成功,因为E指向提交E'。如果一切顺利,Git会自行进行新提交,我们有:

git cherry-pick another

我们现在需要做的是强制名称another引用提交E,名称A--D' <-- temp \ B--C--E' <-- temp2 (HEAD) \ D <-- master \ E <-- another 引用提交master。要立即执行此操作,我们只需使用D'

another

这给了我们

E'

尽管提交git branch -fgit branch -f master temp git branch -f another temp2 没有名称(这使它们很难找到),但它们会在您的Git存储库中停留很长一段时间,通常至少持续30天。 (这可以通过各种 reflog 过期设置来控制。)如果您将其哈希ID保存在某个地方(并且您已经-或者更确切地说,Git已将哈希ID保存在某些reflog中),您仍然可以获得他们在这段时间回来。

您现在可以A--D' <-- master, temp \ B--C--E' <-- another, temp2 (HEAD) \ D [abandoned] \ E [abandoned] 个原始分支名称之一,并删除两个D名称。

使用E

git checkout的本质上是 1 运行temp命令的系列,并通过运行与git rebase强制分支名称指向 last 复制的提交,并git rebase指向该分支。 git cherry-pick将复制的提交集来自rebase称为其 upstream 参数的内容。变基将它们复制到的位置,就像git branch -f一样,来自变基称为其 onto 参数的内容。

也就是说,您运行:

git checkout

其中 git rebase 是要在第一个复制的提交之前 进行的提交,然后 git cherry-pick 告诉您Git提交 not 复制的内容。乍一看,这种“什么也不能复制”很奇怪,但是您已经习惯了。 2 它还允许您在大多数情况下忽略git rebase --onto <target> <upstream> (尽管在您的特殊情况下不是这样) )。

Git所做的是枚举target中的提交,但排除了某些通常不希望的提交。 3 列出了应复制/精选的提交哈希ID列表。 。此列表将保存到一个临时文件中。 4 然后,Git运行HEAD分离变量upstream,以检出 --onto 提交upstream..HEAD,如果您未指定git checkout,则为 target 。然后,Git对保存的哈希ID进行挑选。最后,如果一切顺利,Git会强行将分支及其分支--onto重新附加到rebase操作中最后复制的提交。

对于您的特殊情况,eftshift0 has already shown the appropriate git rebase commands在大约20分钟之前击败了我。 :-)这只是对实际情况的详尽解释。


1 我在这里说出好像是,因为某些重新配置方法使用其他方法,而某些重新配置实际上是运行upstream,或者-最现代的Git-直接内置到Git所谓的 sequencer 中,该实现实现了摘樱桃。

2 由于Git的--onto限制语法,实际上很自然。这告诉Git:找到可从 HEAD到达的提交,但不包括那些可从 git cherry-pick到达的提交。 )有关可达性的更多信息,请参见Think Like (a) Git

3 不受欢迎的是现有的合并提交,以及任何已经被精心挑选的提交。 Git使用A..B程序找到后者。正确地进行描述有点棘手,我在这里不再赘述。

4 它位于B下,但位置已在Git的整个开发过程中移动。视乎其他情况,如果您好奇的话,有时可以用A或类似的名称来找到它们。

答案 2 :(得分:0)

另一种实现方法是简单地使用交互式rebase:

https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History

从master创建另一个分支,然后手动重写它们:

$ git checkout -b another
$ git rebase -i 

git rebase(不带参数)将为您提供该分支上所有提交的列表。然后只需在提交列表中用“ d”标记要删除的内容即可。例如对于母版,您要“删除” B,C和E。

d b72a395    E
pick 5bca8d9 D
d 15aab26    C
d 25aab26    B
pick 35aab26 A

对于“另一个”分支,将“ D”标记为要删除。