Git commit,如何将一个提交分成两部分

时间:2018-07-27 20:07:27

标签: git

在分支 temp 中,我创建了一个新分支 temp2 。我对文件进行了重排,然后对其进行了一些清理。我承诺。

后来我发现,如果我进行了重新整理,提交,然后进行清理,然后提交,那么我的历史将更加清晰。

换句话说,我只想将此提交分为两个阶段,两个提交。

好吧,我记得重新安排的部分。因此,我切换到分支 temp 并再次进行了重新排列,而没有进行清理。我的想法是我可以将提交从 temp2 提交到temp。

我所拥有的:

------ before ----- rearrangement <-- temp
              \
               \ 
                \
                 ------- rearrangement plus cleanup <-- temp2

我想要什么:

------ before ----- rearrangement ----- rearrangement plus cleanup <-- temp

首先,我尝试将 temp2 重新设置为 temp ,但是遇到合并冲突。这让我感到惊讶,因为它没有要求合并任何内容。我以为重新定基只是更改了附加提交的位置。所以我放弃了。

然后,我尝试将“重新安排+清理”从 temp2 挑选到 temp 。但是我 still 发生合并冲突。真的让我感到惊讶,因为我认为这是完全不可能的。所以我也放弃了。

我被困住了。我不明白这里发生了什么。 git不是基于差异的。提交只是快照。那么为什么会有冲突呢?我要做的只是按一定顺序排列这些快照。为什么这么难,我该怎么办?

2 个答案:

答案 0 :(得分:2)

还没有在命令行中尝试过,但是步骤应该相似:

  1. 硬重置为temp2。只是为了确保您使用的是temp2而没有额外的差异。
  2. 混合重置为temp1。现在您正在使用temp1,但是将temp2的数据作为差异。
  3. 将当前差异作为对temp1的新提交rearrangement plus cleanup提交。
  4. 中提琴。

答案 1 :(得分:2)

TL; DR

使用:

git checkout temp
hash=$(git log --no-walk --pretty=format:%B temp2 | git commit-tree -p temp temp2^{tree})
git merge --ff-only $hash

将提交C复制到新的提交C',该提交再次使用日志消息和C的树,然后调整分支名称temp指向新的提交。

  

我以为重新定位只是在附加提交的地方发生了变化...

那是关键错误!

如果我拿起您的图表并缩小名称,我们将从以下内容开始:

A--B  <-- temp
 \
  C   <-- temp2

git checkout temp2; git rebase temp所做的(或试图做的)是将提交C复制到新的提交C',该提交在某种程度上与C类似,但具有B作为其父母。如果成功,最终结果将是:

     C'  <-- temp2
    /
A--B   <-- temp
 \
  C   [abandoned]

我想你已经知道了;您错在哪里是如何复制的想法。

git rebase的本质上是运行git cherry-pickgit cherry-pick所做的是-从逻辑上讲;潜在的机制是使用Git的合并机制-将提交转化为更改,然后将该更改应用于其他地方。因此,Git将比较(差异)提交C与提交A,然后比较差异提交BA,并尝试将两个变更集组合以产生提交C'上方B。当然,这两个变更集重叠,这就是为什么您会发生合并冲突的原因。

没有使用git rebase来实现这一目标。由于C已经是您想要的最终结果,因此您可以简单地将C的快照以及可能的提交消息复制到新的提交C'中。没有面向用户的Git命令可以执行此操作,但是有一个底层命令可以执行此操作。这些低级命令就是Git所说的 plumbing 命令。

尤其是,git commit-tree写入一个提交对象。为此,它需要一个现有的 tree 对象(快照)以及一些父哈希集。通常,我们可以使用管道命令git write-tree来创建一棵树,该命令会写出当前索引,但是在现有的提交C中我们已经有一个非常合适的最终结果。我们只需要获取它的树哈希ID。 gitrevisions syntaxtemp2^{tree},因为名称temp2指向提交C

我们希望提交提交C的副本的父级当然是提交B。名称temp就足够了,因为它现在指向提交B。我们还必须在标准输入或作为参数或文件的情况下向git commit-tree提供提交日志消息。 git log --no-walk --pretty=format:%B将从给定的提交中获取日志消息。

一旦我们创建了新的提交对象,就必须在宽限期内(对于git gc / git prune)抓紧时间,并为其命名一个 name ,默认为14天。为此,我们可以使用git merge --ff-only扩展当前分支名称(即temp)。

(请注意,只要您确定git log ... | git commit-tree都可以使用,就可以将整个操作作为一个单行表达式来完成。)