更改提交的父级

Change parent of a commit

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

目前情况是:

      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' 提交中:(

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

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

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

耶,你的图表很准确!

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

幸运的是,有几种非常简单的方法可以做到这一点。不幸的是,他们中的一些人至少使用了一个 管道命令 ,即一个不适合用户的、不闪亮的瓷器内部 Git 命令。

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

Only solution preventing massive merge I found so far is

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:当你做这个索引更新时,也更新工作树:如果一个文件从索引中完全删除,也从工作树中删除它,或者如果一个文件在索引中被替换,在工作中替换它-tree 也是。 这个标志实际上不是必需的,因为 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 - 以从标准输入读取消息。然后,您可以使用 git log --no-walk --format=%B <hash-of-E> 从提交 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 也许是最好的。