我想从某个提交开始创建一个新分支。但是,我希望之后提交的所有提交也成为该分支的一部分。
假设这是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
我还没有将任何内容推送到远程存储库。 有没有办法做到这一点?
答案 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-D
和D
......好吧,我们可以将指向。我们想要做的是让他们指向&#34;提示&#34;分支。
Git从分支名称指向的地方开始提交:branch-D
,o
,或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
命令来复制C
,D
和<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
,以及B
,C
和{的新分支{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
,那么您必须重写A
,B
和C
- 并且您最容易用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
- 但它会通过复制A
,branchC
为B
创建新的历史记录和C
。所以你会有
D
无论如何,要执行您想要的操作意味着从x -- B' -- C' -- D' <--(branchC)
\
A <--(branchD)
的历史记录中删除B
,C
和D
。你提到这些提交还没有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分支。