更改提交的父项

时间:2019-02-16 19:43:49

标签: git merge move rebase cherry-pick

我尝试将提交移至另一个分支的顶部,而忽略目标分支中所做的所有更改。

当前情况是:

      master
        ↓
--A--B--C
   \
    D--E

我想将提交E移到C前面

      master
        ↓
--A--B--C--E'
   \
    D--E

E'与E相同,只是下等额现在是C而不是D(这意味着E和E'的树应该相同)。

要更加精确:

"git cat-file -p E" shows e.g.
tree b98c9a9f9501ddcfcbe02a9de52964ed7dd76d5a
parent D

"git cat-file -p E'" should show
tree b98c9a9f9501ddcfcbe02a9de52964ed7dd76d5a
parent C

我尝试使用不同的参数以及Cherry-pick重新设置基准,但是所有这些最终都尝试将C中所做的所有更改合并到新的E'commit中:(

到目前为止,我发现的唯一阻止大规模合并的解决方案是

check out C
copy over all stuff from E to C
commit and get E'

现在树是相同的,父树是不同的,但是必须有一种更简单,更快的方法,因为要做的就是用现有的树对象创建一个简单的提交对象。

1 个答案:

答案 0 :(得分:2)

是的,您的图表是准确的!

A,Git中没有明显的工具可以达到您想要的结果。这里的问题是,rebase只是自动化的自动选择,而自动选择是关于将提交及其快照转换为变更集,并将这些变更与其他提交合并以进行新的提交,这是不希望的想要:您想要保留原始快照。

幸运的是,有几种简单易行的方法可以做到这一点。不幸的是,其中一些使用至少一个 plumbing命令,即对用户不友好,不发光的瓷器,内部Git命令。

首先,让我们注意您自己的解决方案是正确的:

  

到目前为止,我发现的唯一阻止大规模合并的解决方案是

check out C
copy over all stuff from E to C
commit and get E'

在实际的Git命令中,例如:

$ git checkout -b new-branch master
$ git rm -r .                     # in case there are files in C that aren't in E at all
$ git checkout <hash-of-E> -- .   # overwrite using E
$ git commit

这实际上还不错,但是它会导致对工作树的大量更新,如果您的下一个make花费一个小时或其他时间,可能会很烦人。

#1简便方法

第一种更简单的方法是:

$ git checkout -b new-branch master
$ git read-tree -u <hash-of-E>
$ git commit

read-tree操作将您的 index 内容替换为提交E中的内容。-u标志告诉Git:在执行此索引更新时,请更新工作树也是如此:如果将文件从索引中完全删除,也将其从工作树中删除,或者如果文件在索引中被替换,也将其在工作树中也替换。并不是真正需要的,因为git commit将使用索引中的内容,但这是个好主意。

#2简便方法

第二种更简单的方法是:

$ git commit-tree -p master -m "<message>" <hash-of-E>^{tree}

打印出新提交的哈希ID;然后,我们需要设置一些内容以指向 这个新的哈希ID:

$ git update-ref refs/heads/new-branch <hash-ID>

或者,一行:

$ git update-ref refs/heads/new-branch $(git commit-tree -p master -m "<message>" <hash-of-E>^{tree})

请注意,-m "<message>"可以替换为-F <file>以从文件中读取消息,甚至可以替换为-F -以从stdin中读取消息。然后,您可以使用E从提交git log --no-walk --format=%B <hash-of-E>复制提交消息,并将其通过管道传递到单行命令的其余部分。

请确保new-branch确实是一个新的分支名称,否则请确保它是您要重置的分支,而不是当前分支,因为git update-ref不会进行错误检查默认情况下。

#3简便方法

您还可以执行以下操作:

$ git checkout -b new-branch <hash-of-E>   # now at E, with E in index and work-tree
$ git reset --soft master                  # make new-branch identify C, without
                                           # touching index or work-tree
$ git commit -c <hash-of-E>                # make new commit using E's message

最后一种方法的工作树搅动最少,因此这三种方法中最好的。但是,方法2创建新提交时不会碰到任何东西,因此,如果您实际上不希望想要进入新分支,方法2可能是最好的。