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

Git 从特定提交开始创建新分支,git,version-control,branch,commit,git-commit,Git,Version Control,Branch,Commit,Git Commit,我想创建一个新分支,从某个提交开始。然而,我希望所有随后提交的提交也成为该分支的一部分 假设这是带有所有提交的分支-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 ---- comm

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

假设这是带有所有提交的
分支-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
我还没有将任何内容推送到远程存储库。
有什么方法可以做到这一点吗?

最直接的方法是在提交a之前在提交的基础上创建2个新分支。然后cherry pick提交a到1个分支,cherry pick提交B、C,D到第二个分支。

最直接的方法是在提交A之前在提交的基础上创建两个新分支。然后cherry pick提交A到1个分支,cherry pick提交B、C、D到第二个分支。

更新-我误读了您的图表,结果混淆了分支名称。正如torek所指出的,对图表进行“更正确”的阅读也会有一些含糊不清的地方。我将保留我的答案,因为我相信它传达了主要的原则;如果您需要进一步编辑
branchC
history,我将介绍如何使用rebase;但要获得更详细的答案,请参阅torek的回复


首先,让我谈谈我认为您正在寻找的解决方案;但我建议阅读更多,因为这个问题中存在概念上的混乱,值得澄清

那么你现在有了

A -- B -- C -- D <--(branchD)
然后将
branchD
分支移回
A

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

完成这些步骤后,您就可以

A <--(branchD)
 \
  B -- C -- D <--(branchC)
这里,
branchD^
是“在
a
之前提交”的可能名称。如果有这样的提交,它可能有其他名称-可能是
master
,或者肯定是它的提交ID。如果没有这样的提交,那么我想你可以说

git rebase -i branchD^ branchC
git rebase -i --root branchC
(但在这种情况下,试图从
分支历史中删除
A
可能毫无意义,因此我怀疑这就是发生的情况)

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

x -- B' -- C' -- D' <--(branchC)
  \
   A <--(branchD)

x--B'--C'--D'更新-我误读了您的图表,结果混淆了分支名称。正如托雷克所指出的,对您的图表进行“更正确”的阅读会有一些含糊不清的地方。我将保留我的大部分答案(但要理顺分支名称)我相信它传达了主要原则;如果您需要进一步编辑
branchC
history,我将稍微介绍一下如何使用rebase;但要了解更详细的答案,请参阅torek的回复


首先,让我来谈谈我认为你在寻找的解决方案;但我建议阅读更多的内容,因为这个问题中存在概念上的混乱,值得澄清

那么你现在有了

A -- B -- C -- D <--(branchD)
然后将
branchD
分支移回
A

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

完成这些步骤后,您就可以

A <--(branchD)
 \
  B -- C -- D <--(branchC)
这里,
branchD^
是“在
a
之前提交”的可能名称。如果有这样一个提交,它可能有其他名称-可能是
master
,或者肯定是它的提交ID。如果没有这样的提交,那么我想你可以说

git rebase -i branchD^ branchC
git rebase -i --root branchC
(但在这种情况下,试图从
分支历史中删除
A
可能毫无意义,因此我怀疑这就是发生的情况)

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

x -- B' -- C' -- D' <--(branchC)
  \
   A <--(branchD)

x--B'--C'--D'Git的奇特之处在于,分支名称不会影响提交历史记录中的内容,只会让您和其他人找到提交,这一点非常重要。秘密在于,在Git中,提交可能同时发生在多个分支上

但重要的是,一旦做出承诺,任何承诺都无法改变。可以将其复制到(新的,略有不同的)提交,但不能更改

此外,虽然可以强制任何分支名称指向任何提交,但分支名称运动有一个“正常方向”:通常,它们只前进。正如我们稍后将看到的,前进意味着无论他们过去指的是什么承诺,仍然“在”那个分支上。因此,一旦您将您的提交给了其他人一些其他Git,并要求他们调用这些提交
branch-D
,就很难让其他Git收回这些提交。因此:

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

是的,可能有一个非常简单的方法:“还没有推送任何东西”意味着你是唯一一个有这些提交的人,所以无论如何
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 checkout -b branch-C master
...--o--o   <-- branch-C (HEAD), master
         \
          A--B--C--D   <-- branch-D
 git cherry-pick branch-D~3..branch-D
          B'-C'-D'   <-- branch-C (HEAD)
         /
...--o--o   <-- master
         \
          A--B--C--D   <-- branch-D
git branch -f branch-D branch-D~3
git branch -f branch-D <hash-of-A>
git checkout hashOfCommitA
git checkout -b NewBranchName
git checkout nameOfFirstBranch
git revert hashOfCommitA