我有两个分支 - master
和some
:
C2---M3---C4---C5 some
/ /
C1---C3---C6---C7 master
C1
,C2
... - 提交和M3
- 合并提交C3
。
我想这样做:
git checkout some
git reset --soft C1
但我想绕过合并提交M3
。
我该怎么做?
答案 0 :(得分:2)
因此,您希望通过提交C2
,C4
和C5
进行更改,而不是来自合并M3
。我会用一个新的分支来做到这一点:
# Create a new branch started at C1
git checkout -b some-new C1
# Cherry-pick commits
git cherry-pick C2 C4 C5
# Delete the old branch and rename the new
git branch -D some
git branch -m some
答案 1 :(得分:2)
首先,让我们注意提交是(完全)只读和(大部分)永久性的。给定像这样的提交图片段:
C2---M3---C4---C5 <-- some
/ /
C1---C3---C6---C7 <-- master
无论我们做什么reset
,这些相同的提交将继续存在,至少暂时存在。如果我们添加一个也指向提交C5
的保存名称(分支或标记名称),那么无论我们对名称some
做什么,我们仍然可以命名提交C5
, C4
,M3
和C2
。
其次,请记住每个提交都存储一个完整的独立快照。这包括合并提交M3
。我们可以通过运行git diff
来将合并提交转换为一组更改,以将快照的内容与提交的父级内容进行比较 - 但是对于合并提交,其中 2 < / em>父母,我们必须选择两个父母中的一个。
第三,既然你提到了git reset --soft
,那么请注意,除了提交之外,Git为我们提供了一个工作树,我们在其中进行实际工作(并且可以查看提交),以及索引 - 也称为临时区域或缓存 - 我们用来构建每个提交之前我们做了。如果我们运行:
git checkout some
Git将从名称some
当前指向的提交内容(即C5
)填充索引和工作树,并将HEAD
附加到名称{ {1}}。我们此时使用some
附加第二个名称tmp
:
git branch tmp
此时运行 C2---M3---C4---C5 <-- some (HEAD), tmp
/ /
C1---C3---C6---C7 <-- master
将使名称git reset --soft <hash-of-C1>
指向提交some
,同时保持索引和工作树不变。也就是说,索引和工作树内容将继续与C1
:
C5
如果我们现在通过运行 C2---M3---C4---C5 <-- tmp
/ /
C1---C3---C6---C7 <-- master
.
... <-------------- some (HEAD)
进行新的提交,我们会得到一个新的提交git commit
,其内容与C8
的内容相匹配。这是因为新提交是由索引的内容构成的,它与C5
的内容相匹配。 C5
的父级将为C8
,但是,提供给我们:
C1
之后 C2---M3---C4---C5 <-- tmp
/ /
C1---C3---C6---C7 <-- master
\
C8 <-------------- some (HEAD)
将完全没有差异。
您在评论中提到,您希望在索引中或可能作为新提交git diff some tmp
的内容是内容,这可以通过挑选C8
来实现,{ {1}}和C2
atop commit C4
。单独使用C5
无法获得此结果。
最简单的方法是将分支名称设置为指向提交C1
,同时设置索引和工作树以匹配git reset
:
C1
然后使用C1
,可选择git checkout -b new <hash-of-C1>
,与phd's answer一样,我在输入时输入。你也可以使用git cherry-pick
,而不是-n
,将git reset --hard
移至指向git reset --soft
,同时保留提交some
的名称(如上图所示,使用C1
)。然后,通过挑选三个所需的提交,您构建的新分支的 name 将为C5
。
最后,如果您愿意,可以尝试通过减法进程构建新提交。这可能有点容易出错,因为它取决于合并tmp
, 1 的内容,但它的工作原理如下:
我们知道some
实际上是M3
,加上C5
- 变更集,加上C1
- vs - C2
作为changeset-via-merge,加上C1
- as-changeset,加上C3
- as-changeset。
我们可以通过C4
直接计算C5
- vs - C1
。
更好:我们可以通过C3
git diff
vs C1-vs-C3
计算git diff
间接。这将处理某些重复情况,其中C2
- vs - M3
的相同更改为C1
- vs - C3
,因此他们没有加倍地加入C1
- vs - C2
。
我们可以(至少尝试)反向应用任何我们喜欢的补丁。也就是说,将提交转换为变更集(通过与父级进行比较),而不是将这些更改复制到不拥有它们的某些提交中,我们可以撤消 拥有它们的某些提交中的那些更改。执行此操作的命令是C2
。
然后,假设我们像以前一样检查提交M3
,以便我们只进行初始设置:
git revert
现在我们运行some
。这告诉Git将 C2---M3---C4---C5 <-- some (HEAD)
/ /
C1---C3---C6---C7 <-- master
与git revert -m 1 <hash-of-M3>
区别开来以查看更改的内容。 2 结果是新提交C2
:
M3
很可能包含您想要的内容:C8
作为快照加上 C2---M3---C4---C5---C8 <-- some (HEAD)
/ /
C1---C3---C6---C7 <-- master
作为变更集以获取C1
作为内容,加上{{1} } vs C2
作为变更集来获取C2
作为内容(但最终减去 M3
vs C2
作为最后的变更集),等等上。由于M3
取消 M3
所做的事情,C2
应该包含所需的内容。
此时,如果您愿意,可以C8
,按照您希望的方式设置索引和工作树,然后运行M3
以创建提交C8
:
git reset --soft <hash-of-C1>
没有 name 可以找到它们,这里图表顶行的所有提交都变得不可见了,大约30天后,Git在运行垃圾收集器时真的删除了它们
1 特别是,如果我们使用git commit
,我们可能会回复太多。这就是我们C9
在这里的原因。
2 这假设 C2---M3---C4---C5---C8
/ /
C1---C3---C6---C7 <-- master
\
C9 <-------------- some (HEAD)
的第一个父亲实际上是git revert <hash-of-C3>
。在任何正常的提交增长过程中,它都是,但值得仔细检查。
答案 2 :(得分:1)
我喜欢挑选樱桃的方法,但另一种方法是:
git checkout some
git rebase --interactive --preserve-merges C2
...然后编辑显示的选项列表,将合并提交M3的行前面的pick
更改为drop
。您最终会将C5->C4->C2
作为[{1}}上提交的some
提交内容,而master
也不会{。}}。