使用Git rebase压缩旧提交

使用Git rebase压缩旧提交,git,Git,假设我有这样一个git历史记录: -- A -- B -- C -- D -- \ / -----E----- 有没有相对快速的方法将B和C压缩成一个提交 我希望我的最终历史看起来像: -- A -- BC' -- D' -- \ / ----E---- 使用普通的重定基来压缩B和C是很容易的——似乎让新的提交取代B和C成为祖先或D是很麻烦的 当一个指向D的分支签出后,运行git-rebase-i-master(a

假设我有这样一个git历史记录:

-- A -- B -- C -- D -- 
    \            /
     -----E----- 
有没有相对快速的方法将B和C压缩成一个提交

我希望我的最终历史看起来像:

-- A -- BC' -- D' --
    \         /
     ----E----
使用普通的重定基来压缩B和C是很容易的——似乎让新的提交取代B和C成为祖先或D是很麻烦的

当一个指向D的分支签出后,运行git-rebase-i-master(a的前身),并选择挤压C时,生成的历史如下所示:

A -- BC' -- E'
 \
  --- E

假设此图代表您的主分支:

master: A <- B <- C <- D (what you have)
master: A <- S <- D      (what you want)
完成后,键入以下内容以完成重新基准:

git rebase --continue
通过选择提交B的
squash
,它将把提交B合并到提交C中,给您留下您想要的内容。请记住,挤压
B
C
提交可能会产生合并冲突

使用普通的重定基来压缩B和C是很容易的——似乎让新的提交取代B和C成为祖先或D是很麻烦的

让新的提交取代B和C成为D的祖先不仅很容易,而且是默认行为。

我不确定使用git rebase是否不可能做到这一点(使用
--preserve
),而是使用
git filter branch
,通过过滤,您可以完全删除提交
B
。然后,过滤器序列将
C
的树复制到
C'
,并使
C'
的父级成为
A
,将
D
的树复制到
D'
,并使
D'
的父级(仍然有两个)分别成为
E
C'

注意,当我在这里调用副本
C'
时,它有C的树,它也包括
B
中更改的内容。因此,您可以正确地将其命名为
BC'
,这样您就可以得到您所绘制的内容

git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = id-of-B ]; then skip_commit "$@";
    else git commit-tree "$@"; fi' branchname
(在此处填写B的ID,并填写一个适当的正引用;使用branchname~5..branchname或类似工具来减少迭代的提交次数,选择
~
值,使筛选器分支看到所需内容,但足够低,不会“复制”根本不会更改的提交)

您还可以使用较低级别的git命令(特别是两个
git提交树
命令,然后是强制分支移动)来实现这一点。考虑到下面的脚注1,这可能会变得更容易(取决于
D
是否是所讨论分支的最尖端提交,以及您对
过滤器分支的舒适度)



1正如任何带有
git过滤器分支的东西都可以说是“容易的”…:-)即使您完全理解了它,仍然需要进行后期过滤清理。

您想要的是什么?我当然需要使用它-但是如果我使用更一般的用例,我总是在分支顶端使用新的B+C组合提交(或者--on分支)或默认选项。我想让他们留在他们的位置上。这应该能满足你的需要。@nihilon,恐怕不行:(
git-rebase-i A-p
然后将
C
pick
更改为
fixup
此答案不起作用。请在作为答案发布之前尝试您建议的命令。
master
git-rebase
中不正确,并合并提交(
D
)将不会以您为初学者提供的选项显示在交互式重基中。我担心这并不能完全满足我的要求-B和C提交仍然会出现在我的历史记录中,因为C是D的前身。您的示例简化了这一点,从而使git垃圾回收器删除不再引用的提交。请请更新您的问题,以便清楚地知道您想要什么。将新的组合提交设置为D的祖先仅是当D除了要组合的提交之外没有其他祖先时的默认行为。这不适用于我的示例,我相信D也必须成为新的提交(D’)为了反映这样一个事实,即它的祖先集合需要从{C,F}到{BC',F}请尝试做一个我在回答中列出的“香草”重基。如果你没有得到预期的结果,那么更新你的问题,说明为什么正常的重基不会按我们预期的方式运行。
git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = id-of-B ]; then skip_commit "$@";
    else git commit-tree "$@"; fi' branchname