在合并之前压缩旧的git提交

时间:2017-04-28 10:40:26

标签: 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

我想将0000A5和0000A6压制在一起。

当我尝试使用交互式rebase时,magit(我恰好使用的git的emacs前端)警告我"尽管在rebase范围内合并,仍继续进行?"当我继续时,它失败了以下(取自我的实际工作,而不是上面的简化例子)。我不确定在合并之前是否存在重新定位提交的问题,或其他内容("未跟踪的文件"行是可疑的,因为合并包括将客户端项目移动到& #34; 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

1 个答案:

答案 0 :(得分:2)

这似乎有几件事情在这里发生。我不熟悉您正在使用的前端,但看起来它正在尝试自动化rebase,并且如果暂停通常需要手动干预,则会挽救它不知道如何修复。

虽然通过合并进行调整可能很棘手,但我认为这与目前的问题无关。在我看来,96ac7ac只是fb5de84的一个版本,所以当你把它们压在一起时,你会得到一个空的提交。这是允许的,但需要人工干预。 (rebase会停止,你会说git commit --allow-empty之类的东西,然后继续。)

您可以通过

确认96ac7ac是否真的是完美的还原
git diff 96ac7ac fb5de84^

如果是,并且如果你的前端无法满足rebase所需的干预,那么你可以放弃这两个提交,而不是压缩它们。

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

即使使用正确的选项,通过合并进行变基的问题在于,原始合并中的任何工作都会超出默认自动分辨率。如果需要手动解决冲突,那么git将再次停止并期望您执行解决方案(您的前端可能不会与之合作)。此外,如果自动解析成功并且合并包含手动应用的更改(幸运的是罕见但不是不可能),则更改可能会无声地丢失。

另一种选择是"解决问题"合并(如果没有太多的话)。如果你有

X --- A --- B --- M --- C <--(master)
  \             /
   D --- E --- F

你希望压缩EF,然后你可以先

git checkout F
git checkout -b temp_branch
git rebase -i D

并设置壁球,给你

X --- A --- B --- M --- C <--(master)
  \             /
   D --- E --- F
    \
     EF <--(temp_branch)

然后重做合并

git checkout B
git merge temp_branch

在此合并期间,您必须复制原始合并M中完成的所有工作。如果M只是自动解析,那么合并(M')也是如此。您可以通过

确认它是好的
git diff M M`

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

当然,如果合并信号冲突,你必须解决它们;再次对M进行分析应提供良好的指导。

完成后,你有

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

(抱歉这个奇怪的符号,图表让我发疯; <*> s应该是FM的一行。但不要担心,我们即将解开它。)

清理并对合并后提交进行最后的变更:

git branch --delete temp_branch
git rebase --onto M` M master

产生

X --- A --- B --- M' --- C` <--(master)
  \              /     
   D --------- EF

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

git diff master master@{1}

如果您还想验证每个历史提交,那么这种表示法可能会变得混乱......)

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