使用git将一系列小型合并从master转换到我的分支,同时保持对master的引用?

时间:2016-10-31 14:38:52

标签: git version-control squash

我有一个非常复杂的合并要做。问题的一部分在于我让太多时间过去了,因此纳入我的分支的变化数量是巨大的。

为了简化操作,我选择git merge origin/master~20,然后git merge origin/master~17git merge origin/master~15等,这样我就能以零散的方式解决冲突问题,而不必采取措施一下子。

问题是这会导致我希望摆脱的日志历史的污染。合并所有这些提交的最佳方法是什么,同时仍然保持结果提交指向我的分支和主服务器?

我通常使用git reset --soft压缩但不会留下对主分支的引用。我还尝试了git rebase -i --preserve-merges但我收到了"Refusing to squash a merge"错误消息。

我该怎么办?

1 个答案:

答案 0 :(得分:2)

让我用这种方式描述你的情况:你有你想要的合并结果 - 源树 - 但不是你想要的历史导致这个结果。< / p>

作为VonC has put it elsewhere,您自己尝试了,git reset --soft通常 答案。您执行软重置,然后进行新的提交。如果此时只能进行 merge 提交,那么它仍然是答案。

如果没有git rerere,有三种简单的方法可以做到这一点。一种是作弊,其中一种方式是记录作弊&#34;因此可能是正确的方法。 (我喜欢的方法是中间的,因为它很短而且很甜,但显然是作弊,所以有一天它可能会停止工作。)

方法1:笨重,但使用所有普通工具(不作弊)

请注意,此处的命令序列假定您位于存储库的顶层(特别是下面的git rmgit checkout步骤,请参阅.表示&#34;所有内容& #34)。我还使用$startpoint进行您想要进行合并的提交,并$other引用另一个分支名称或提交ID(您想要git merge的那个)。

  1. 保存最终结果的ID(我们想要,但标准工具最简单的方法是引用提交,这也可以正常工作):

    $ git tag temp-save-result
    

    (或使用剪切和粘贴或reflog来保存它;为了简单起见,我在这里展示了一个标签)。

  2. 复位。这可能是--hard,而不是--soft

    $ git reset --hard $startpoint
    
  3. 运行合并,但会因冲突而失败。忽略冲突并删除整个索引和工作树。我们不想要冲突的合并,或者到目前为止的任何临时结果,因为我们有正确的结果在别处。

    $ git merge $other
    $ git rm -r .
    

    (如果你有一些自定义合并工具留下粪便,你可能也希望在这里清除工作树中的那些,虽然它们不会影响任何重要的东西:它们只是混乱你的工作树。)

  4. 提取步骤1中保存的工作树,并提交结果:

    $ git checkout temp-save-result -- .
    $ git commit
    
  5. 此提交结束合并,其来自您在步骤1中保存的树。您现在可以删除标记:

    $ git tag -d temp-save-result
    

    方法2:作弊

    git commit进行新提交时,如果.git/MERGE_HEAD存在,则进行合并提交。 MERGE_HEAD文件包含第二次提交的ID,即正在合并的其他远程--theirs

    因此,我们像往常一样进行软重置,然后添加合并ID,然后提交。 (注意:我最近没有尝试过,Git可能也想要.git/MERGE_MSG。准备好调整作弊,或者继续进行方法3.)

    $ git reset --soft $startpoint
    $ git rev-parse $other > .git/MERGE_HEAD
    $ git commit
    

    第一个命令是我们通常的git reset --soft步骤,第二个命令是Git说我们正在解决冲突的合并(并且索引全部已解决,因此我们必须完成该步骤) ,git commit现在提交合并。

    方法3:使用管道命令(&#34;记录作弊&#34;)

    生成实际提交对象的命令 - git commit毫无疑问只是一个在末尾附近运行它的shell脚本git commit-tree。它要求:

    • 包含所需树的树对象
    • 提交消息(它将从stdin中读取一条消息,但您可能应该使用-m-F
    • 新提交的父母的父ID

    并将新的提交对象写入存储库,并打印出对象的哈希ID。

    我们已经树了!由于它是当前提交,因此其ID为HEAD^{tree}(使用gitrevisions syntax)。我们也有两个父ID。我们所需要的只是信息:

    $ tmp=$(git commit-tree -p $startpoint -p $other -m 'merge msg' HEAD^{tree})
    

    一旦我们有了提交ID,我们只需要git reset --hard我们当前的分支指向它:

    $ git reset --hard $tmp
    

    (当然,你可以在第二个命令中使用$(...)而不是$tmp将这两个组合成一个大命令,尽管这假设你的git commit-tree命令可以工作:它&#39为了个人舒适,可能最好做两个步骤。如果您更喜欢,可以通过剪切和粘贴哈希ID来省略$tmp变量和相应的shell $(...)语法。并且,你可以输入一个俗气的临时提交消息,然后在重置后用git commit --amend编辑它:--amend选项也适用于合并提交。)