从特定提交开始创建新分支

时间:2017-11-20 14:47:38

标签: git version-control branch commit git-commit

我想从某个提交开始创建一个新分支。但是,我希望之后提交的所有提交也成为该分支的一部分。

假设这是branch-D所有提交。

> branch-D
commit-A ---- commit-B ---- commit-C ---- commit-D

我想删除从commit-B到新branch的所有内容,我们将其命名为branch-C,并且commit-A只保留branch-D

> branch-D
commit-A

> branch-C
commit-B ---- commit-C ---- commit-D

我还没有将任何内容推送到远程存储库。 有没有办法做到这一点?

4 个答案:

答案 0 :(得分:3)

Git的一个特殊之处是分支名称不会影响提交历史记录中的内容,除非他们允许您和任何人否则,这非常重要 - 找到提交。秘诀是,在Git中,提交可能同时在许多分支

重要的是,一旦提交,任何提交都不能更改。它可以复制到(新的,略有不同的)提交,但它不能被更改。

此外,虽然任何分支机构名称​​都可以被强制指向任何提交,但是有一个"正常方向"对于分支名称动作:通常,它们只是前进。正如我们在某个时刻所看到的那样,前进意味着无论他们曾经指出过什么样的承诺,他们仍然会在" on"那个分支。所以,一旦你向其他人 - 其他一些Git提交了你的提交 - 并要求他们调用这些提交branch-D,很难让其他 Git来收回那个。因此:

  

我还没有将任何内容推送到远程存储库。有没有办法做到这一点?

是的,并且可能有一种非常简单的方法:“还没有推动任何东西”#34;意味着你只有只有一个拥有这些提交,所以无论你找到它们,每个人如何找到它们,因为你'重新"每个人"。 :-)没有必要让任何人改变任何东西。

只要他们已经按照您想要的方式布局,您只需要重新排列找到这些提交的方式。

让我们绘制你现有的一系列提交" Git Way":

... <--A <--B <--C <--D   <-- branch-D

在Git中,每个提交都由其哈希ID唯一标识。这些东西既大又丑,而且显然是随机的(虽然它们实际上是完全确定的),所以我们几乎不使用它们,或使用像d1c9d3a这样的缩写形式。而不是那样,让我们​​只调用最后一次提交D

在内部提交D中,还有另一个哈希ID,用于标识D 父提交。我们来说c033bae,但我们只是称之为#34;提交C&#34;。我们说D 指向 C

同样,C指向B,指向A,这指向master,好吧,也许是...--o--o <-- master \ A--B--C--D <-- branch-D 的提示 - 你没有说,但是现在让我们假设:

master

这是一种更紧凑的绘制方式。提示总是,必然,指向向后,所以我们并不需要内部箭头 - 我们知道他们总是倒退。但是,分支名称,例如branch-DD ......好吧,我们可以将指向。我们想要做的是让他们指向&#34;提示&#34;分支。

Git从分支名称指向的地方开始提交:branch-Do,或master第一行的最终o 。然后它查看当前提交的父级,以向后移动。然后它查看父母的父母,依此类推。因此,master branch-D上有两个master提交:我们可以从branch-D开始查找它们,或者我们可以通过从...--o--o <-- master \ A <-- branch-D \ B--C--D <-- branch-C 开始,向后退四步。

这意味着我们想要的图片看起来像这样:

master

此处,A上的提交也在两个分支上,提交branch-D(现在是branch-C的提示)仍在branch-C上好。它不再是 ?--?--? <-- branch-C / ...--o--o <-- master \ A <-- branch-D \ B--C--D <-- ??? 提示

另一方面,也许我们想要的图片看起来像这样:

branch-C

也就是说,我们需要回答一个问题。名称A将指向某个特定提交。当我们退回三步时,我们应该到达提交master吗?或者我们应该到达branch-C上的最后一次提交?

如果第一张图片是正确的,答案很简单:创建新名称D,指向提交branch-D;然后强制现有名称A返回提交git branch branch-C branch-D # copy the hash ID from branch-D to new branch-C 。要做到这一点:

git reset --hard <hash-of-A> # move current branch; re-set index and work-tree

然后,根据现在检出的分支,可以:

git branch -f branch-D <hash-of-A> # force branch-D to point to A

或:

git reset --hard

请注意,在使用git reset --hard之前,确保您没有要保存的任何修改是一个非常好的主意。虽然提交几乎是永久性的(您可以将它们取回,通常至少30天,即使您将所有分支名称删除),B的索引和工作树clobbers

如果您想要第二张图片,但是如果您希望提交A的父级不是提交B - 那么您必须复制B提交给新的,不同的提交,其中包括B,但是......&#34;。原始git cherry-pick与副本之间的差异将包括已更改的父哈希ID。

对于那个,你需要使用git rebase或等价物(例如git checkout -b branch-C master ,这基本上是一个复制大量的樱桃挑选操作提交)。要做到这一点,你可以:

...--o--o   <-- branch-C (HEAD), master
         \
          A--B--C--D   <-- branch-D

,并提供:

git cherry-pick

然后运行三个B命令来复制CD<commit>~<number>;或者更简单 - 这使用 git cherry-pick branch-D~3..branch-D 符号:

          B'-C'-D'   <-- branch-C (HEAD)
         /
...--o--o   <-- master
         \
          A--B--C--D   <-- branch-D

一次复制所有三个。这会产生:

branch-D

此时,可以安全地强制A指向提交git branch -f branch-D branch-D~3

git branch -f branch-D <hash-of-A>

您可以通过哈希ID执行此操作,如前面的示例所示:

branch-D~3

我们对D所做的就是告诉Git:计算三个父步骤,一次一个父级。我们从C开始,向后计算一步到B,向后计算另一步到A,然后倒数第三步到{{1}}。

答案 1 :(得分:1)

更新 - 我误读了您的图表并因此混淆了分支名称。同样,torek指出,一个更正确的&#34;阅读您的图表包含一些含糊之处。我将保留我的答案,因为我认为它传达了主要原则;如果您需要进一步修改branchC历史记录,我会稍微谈谈使用rebase;但是有关更详细的答案,请参阅torek的回复。

首先让我找到我认为你正在寻找的解决方案;但我建议阅读以外的内容,因为这个问题的概念混淆值得清理。

所以你现在有了

A -- B -- C -- D <--(branchD)

我认为您希望A上的 branchD,以及BC和{的新分支{1}}。因此,第一步是签出D(或branchD),创建新分支

D

然后将git branch branchC 分支移回branchD

A

(我使用git reset --hard HEAD~3 ,因为在此示例中,这将是HEAD~3的一个名称。这取决于A历史记录中的提交次数使用master的提交ID(SHA)代替A总是可以的。)

完成这些步骤后,您有

HEAD~3

看起来像你所描述的内容,但我们没有达到描述所暗示的方式。

我没有移动提交;我搬了分店。这在git中更直接,它反映了提交不属于&#34; git中的分支与他们在某些系统中的方式相同。因此,语句&#34;在前一次提交时分支,但在该分支上包括以下提交&#34;在git中没有真正有意义。

问题中前后图表的含糊不清确实让我误解了A <--(branchD) \ B -- C -- D <--(branchC) 应该包含在其历史中的确切内容。如果branchC 意味着包含branchC,那么您必须重写ABC - 并且您最容易用D做到这一点。创建git rebase -i并移动branchC后,您可以说

branchD

此处git rebase -i branchD^ branchC 是&#34; branchD^之前提交的可能名称。如果存在这样的提交,它可能具有其他名称 - 可能是A,或者肯定是其提交ID。如果没有这样的提交,那么我想你可以说

master

(但在这种情况下,尝试从git rebase -i --root branchC 历史记录中移除A可能毫无意义,所以我怀疑这是“这里发生了什么”。< / p>

rebase命令将打开一个带有TODO列表的文本编辑器,其中包含每个提交的条目。您可以从列表中删除提交branchC的条目,然后保存并退出。这不会打扰A - 它仍会指向branchD - 但它会通过复制AbranchCB创建新的历史记录和C。所以你会有

D

无论如何,要执行您想要的操作意味着从x -- B' -- C' -- D' <--(branchC) \ A <--(branchD) 的历史记录中删除BCD。你提到这些提交还没有branchD,所以应该没问题;但这是一个要记住的重要区别。每当您想要从分支的历史记录中删除提交时,如果远程分支尚未了解相关提交,则更容易做到。

答案 2 :(得分:0)

您可以使用:

git checkout hashOfCommitA

然后

git checkout -b NewBranchName

现在你有了一个提交A的新分支。然后你可以回去提交D

git checkout nameOfFirstBranch

然后从此分支恢复提交A

git revert hashOfCommitA

现在你必须分支其中一个只有提交A而另一个是提交b,c和d。

答案 3 :(得分:-1)

最直接的方法是在commit-A之前创建2个新的分支。然后,樱桃挑选提交-A到1分支,樱桃挑选提交B,C,D到第2分支。