如果我可以得到中间的提交,我该如何将一个Git提交分成两个?

4
应用了大规模的代码更改后,对几乎每个文件进行了格式化处理。
A --------- B

一个提交 B 合并了这两个操作,导致巨大的差异,难以审查,手动进行交互式重新基础和挑出 hunk 进行拆分也很困难。但是,我可以通过在检出 A 并对其运行代码格式化程序来轻松构造一个中间提交 I
A --------- B
 \ 
  \
   -- I

假设我刚刚提交了 I,如何轻松生成一个提交 B',使其与提交 B 具有完全相同的树内容?

A --------- B
 \            
  \           
   -- I --- B' # (has exact same contents as B)

如果我天真地这样做

git checkout B
git rebase I

我最终得到了大量冲突。我不想聪明绝顶,只是想最终到达与 B 相同的目录,并假设 I..B 的差异将比 A..B 显著更小。

1
git checkout I && git checkout B -- . && git commit -m "Copy of B" - phd
@NickT:我认为phd提供了你正在寻找的答案。 - LeGEC
3
您的方法会使得在I但不在B中的文件留在提交记录中。如果要删除这些文件,您可以在checkout之间插入 git rm -rf --cached . 命令;这将从提交中删除这些文件,但是它们仍会留在工作目录中,需要使用其他方式(例如 git clean)从工作目录中删除。 - j6t
2个回答

7

好的,如果你已经有I和B,你可以这样做:

git checkout --detach B
git reset --soft I # move branch pointer (HEAD pointer, in this case) to I, set all differences in index ready to be committed
git commit -m "blah blah"

现在你可以指定你想要的任何分支,只要你喜欢结果:

git branch -f blah
git checkout blah

3
由于它与“用户可访问”的命令行API一起使用,因此这是一种好的方法。我喜欢直接使用git commit-tree,因为它可以让您以非常直接的方式从您拥有的部分构建最终提交,但随后您必须快进或重置到最终提交。 - torek
1
我曾经经常使用commit-tree... 但是后来我发现了 reset --soft,我的生活从此不同了。使用起来简单得多... 如果未来有需要的话,肯定会回到commit-tree - eftshift0
1
OP明确表示他创建了提交,没有提到分支,但我认为他暗示BI实际上是分支名称;在这种情况下,我会采用@phd的解决方案(发布在OP问题下面的评论中)。 - LeGEC
我们的新版主万岁!\o/ - Romain Valeri
这是一个漫长的旅程(实际上,你比我先到了,祝贺你!)。下一站:git 的金徽章 :-) - eftshift0
1
...但是有Torek和你来窥探我的积分,我应该如此理所当然地得到它们,我们要花一段时间才能达到那个目标。 :-D - eftshift0

1

仅提及toerk在上面评论中提到的commit-tree解决方案:

git reset --hard $(git commit-tree -m "Did this and that" -p I $(git rev-parse B^{tree}))

git commit-tree需要3个参数来创建一个新的提交:

-m "Did this and that"为你的B'提交添加一条消息
-p I设置它的父提交(I)
最后,
$(git rev-parse B^{tree})(不带标志,因为它是主要(必需)参数)来指示它应该有哪个树

reset --hard命令将当前分支设置为这个新的提交。因此,您可以选择在当前分支上执行它以进行“修复”,也可以在为此目的创建的新分支上执行它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接