【问题标题】:Change parent of a commit更改提交的父级
【发布时间】:2019-07-10 15:21:17
【问题描述】:

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

目前的情况是:

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

我想把提交 E 移到 C 前面

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

E' 与 E 相同,除了 parrent 现在是 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

我尝试了使用不同参数的 rebase 以及cherry-pick,但所有这些最终都尝试将 C 中所做的任何更改合并到新的 E' 提交中:(

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

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

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

【问题讨论】:

    标签: git merge move rebase cherry-pick


    【解决方案1】:

    是的,你的图表是准确的! ?

    唉,Git 中没有明显的工具可以实现您想要的结果。这里的问题是,rebase 只是自动挑选,而挑选是关于将提交及其快照转换为更改集,并将这些更改与其他提交合并以进行新的提交,这不是你想要的want:你想保留原始快照。

    幸运的是,有几种非常简单的方法可以做到这一点。不幸的是,他们中的一些人至少使用了一个管道命令,即,一个不适合用户的,不是闪亮的瓷器,内部 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 "&lt;message&gt;" 可以替换为 -F &lt;file&gt; 以从文件中读取消息,甚至可以将 -F - 替换为从标准输入中读取消息。然后,您可以使用 git log --no-walk --format=%B &lt;hash-of-E&gt; 从提交 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 可能是最好的。

    【讨论】:

    • 非常感谢,尤其是我想到的#2 :) 为了完整起见,我认为#1 $ git read-tree 的命令中缺少一个“m” -mu 不是吗?
    • @Foobar:你可以使用-m。结果应该是一样的;不同之处在于(根据文档)是否替换了索引中的统计信息。
    猜你喜欢
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2013-03-24
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多