对不起,标题是否引起误解,但我不确定如何描述我遇到的情况。
我像这样提交和分支
A --- B --- C --- D (master)
\
E (another)
,我想从B
分支中删除提交C
和D
(保留master
),但将它们保留在基于master的another
分支中。
因此,转换后,我的树应如下所示:
A --- D (master)
\
B --- C --- E (another)
我想,我可能应该只是对master
进行基准调整,但是我不确定B
是否还会包含C
和another
从中删除/省略D
。
我应该如何实现上述效果?
答案 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
,然后找到D
和C
和B
。
您想要的结果具有指向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'
;和C
和D
之间的差异应用于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-pick
(git 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
(请注意,由于C
,master~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 -f
和git 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”标记为要删除。