压缩合并之前的旧 git 提交

Dav*_*arb 1 git merge rebase magit

我正在清理 git 存储库以使其更易于理解。到目前为止,它都是私人的,所以我可以改变历史。

大多数情况下,我一直将提交压缩成有意义的集合。

问题在于,该项目在其历史早期是其他两个项目的合并。我在压缩合并之前的提交时遇到问题。

我的问题是:我该怎么做?

具体来说:我有

xxxxxx * master: Latest commit
xxxxxx * another commit
xxxxxx *   Merge projectA and projectB
xxxxxx |\
0000A6 | * a minor commit in project A
0000A5 | * another minor commit
Run Code Online (Sandbox Code Playgroud)

我想把 0000A5 和 0000A6 压在一起。

当我尝试交互式变基时,magit(我碰巧使用的 git 的 emacs 前端)警告我“尽管合并在变基范围内,但仍继续吗?” 当我继续时,它会失败(取自我的实际工作,而不是上面的简化示例)。我不确定合并之前的重新提交提交是否存在问题,或者其他问题(“未跟踪文件”行是可疑的,因为合并包括将客户端项目移动到更大的“client/”子目录中)项目。

Last commands done (5 commands done):
   pick fb5de84 Simplified schema:
   squash 96ac7ac Revert schema to full complexity
Next commands to do (29 remaining commands):
   pick 4ad389a Just indentation, for readability
   pick 4241835 First pass at schema
You are currently editing a commit while rebasing branch 'master' on 'ad91ab4'.

Untracked files:
    client/

No changes
You asked to amend the most recent commit, but doing so would make
it empty. You can repeat your command with --allow-empty, or you can
remove the commit entirely with "git reset HEAD^".

Could not apply 96ac7ac7753c03e83b8c1296d892ce9c5fea44c7... Revert schema to full complexity
Run Code Online (Sandbox Code Playgroud)

Mar*_*ger 5

这里似乎发生了一些事情。我不熟悉您正在使用的前端,但看起来它正在尝试自动执行变基操作,并在出现通常需要手动干预但不知道如何修复的暂停时进行救援。

虽然通过合并进行变基可能很棘手,但我认为这与当前的问题没有任何关系。在我看来,96ac7ac 只是 fb5de84 的恢复,所以当你将它们压缩在一起时,你会得到一个空的提交。这是允许的,但需要手动干预。(变基会停止,你会说类似的话git commit --allow-empty,然后继续。)

您可以通过执行以下操作来确认 96ac7ac 是否真的是完美的恢复

git diff 96ac7ac fb5de84^
Run Code Online (Sandbox Code Playgroud)

如果是,并且您的前端无法适应变基所需的干预,那么您可以删除这两个提交,而不是压缩它们。

您可能遇到的下一件事是,除非您提供该选项,否则 rebase 将尝试使历史记录呈线性--preserve-merges。我认为这就是您的前端警告您的内容,一旦您告诉它继续,我不知道它是否通过了该选项。

即使使用正确的选项,通过合并进行变基的问题是,超出默认自动解析范围的原始合并中的任何工作都可能会丢失。如果需要手动解决冲突,那么 git 将再次停止并期望您执行解决方案(您的前端可能不配合)。此外,如果自动解析成功,但合并包含手动应用的更改(幸运的是,这种情况很少见,但并非不可能),这些更改可能会悄然丢失。

另一种选择是“解决”合并(如果合并数量不是太多)。如果你有

X --- A --- B --- M --- C <--(master)
  \             /
   D --- E --- F
Run Code Online (Sandbox Code Playgroud)

你想挤压E并且F,那么你可以先

git checkout F
git checkout -b temp_branch
git rebase -i D
Run Code Online (Sandbox Code Playgroud)

并设置壁球,给你

X --- A --- B --- M --- C <--(master)
  \             /
   D --- E --- F
    \
     EF <--(temp_branch)
Run Code Online (Sandbox Code Playgroud)

然后重做合并

git checkout B
git merge temp_branch
Run Code Online (Sandbox Code Playgroud)

在此合并期间,您必须重现在原始合并中完成的所有工作M。如果M只是自动解析,那么 mew merge ( M') 也应该如此。您可以确认它是好的

git diff M M`
Run Code Online (Sandbox Code Playgroud)

如果这显示出差异,您将必须手动应用它们(这也可能是发生的情况M)。我相信你可以让工作树看起来正确并做出承诺--amend

当然,如果合并信号发生冲突,您就必须解决它们;再次反对M应该提供良好的指导。

完成此操作后,您将拥有

         <*>- M --- C <--(master)
             /
X --- A --- B --- M' <--((HEAD))
  \              /     
   D --------- EF <--(temp_branch)
    \
     E --- F -<*>
Run Code Online (Sandbox Code Playgroud)

(抱歉,奇怪的符号,这张图让我着迷; s 应该是从到 的<*>单行。但别担心,我们即将解开它。)FM

清理并执行合并后提交的最终变基:

git branch --delete temp_branch
git rebase --onto M` M master
Run Code Online (Sandbox Code Playgroud)

屈服

X --- A --- B --- M' --- C` <--(master)
  \              /     
   D --------- EF
Run Code Online (Sandbox Code Playgroud)

您可以看到这可能非常乏味,特别是如果有许多合并和/或包含手动冲突解决或其他非自动更改的合并。验证最终状态是关键。(您可以使用 reflog 来帮助解决此问题,或者在开始这一切之前标记原始主 HEAD。例如使用 reflog:

git diff master master@{1}
Run Code Online (Sandbox Code Playgroud)

如果您还想验证每个历史提交,这个符号可能会变得混乱......)

另一种变体是,您可以压缩然后设置一个 with 来重新设置父级 from to ,而不是重做合并并将合并后工作E重新定F基到其上。有关如何使用的详细信息,请参阅文档(尽管您必须稍微调整示例以处理合并)。这可能有效,因为您特别希望结果具有未更改的树。git filter-branch--parent-filterMB , FB , EFgit filter-branch--parent-filter