我怎么能做`git reset --soft <some-commit>`绕过提交合并?

时间:2018-03-19 09:00:24

标签: git git-reset

我有两个分支 - mastersome

  C2---M3---C4---C5 some
 /    /
C1---C3---C6---C7 master

C1C2 ... - 提交和M3 - 合并提交C3

我想这样做:

git checkout some
git reset --soft C1

但我想绕过合并提交M3

我该怎么做?

3 个答案:

答案 0 :(得分:2)

因此,您希望通过提交C2C4C5进行更改,而不是来自合并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做什么,我们仍然可以命名提交C5C4M3C2

其次,请记住每个提交都存储一个完整的独立快照。这包括合并提交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也不会{。}}。