是的,你的图表是准确的! ?
唉,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 "<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 可能是最好的。