合并后压缩Git历史记录

44

我将一个大型项目的上游合并到了我的本地git仓库。在合并之前,我的历史记录很少,很容易查看,但是在合并之后,我的仓库中出现了大量历史记录。我不需要来自上游仓库的所有历史提交。

在这个上游合并之后,已经有了其他提交,我想保留它们。如何将合并自上游的所有历史压缩成一个提交,同时保留在上游合并之后所做的提交?

5个回答

40

我能够使用这里找到的策略,在从主分支进行多次合并后,压缩了几个提交记录:https://dev59.com/WG025IYBdhLWcg3wCxVN#17141512

git checkout my-branch            # The branch you want to squash
git branch -m my-branch-old       # Change the name to something old
git checkout master               # Checkout the master branch
git checkout -b my-branch         # Create a new branch
git merge --squash my-branch-old  # Get all the changes from your old branch
git commit                        # Create one new commit

如果您需要将已经压缩的分支推送到之前已推送过的远程代码库上,您需要强制更新,例如:git push origin my-branch -f


1
git checkout main -- 需要确保你在最新的分支上,如果主分支默认没有指向 HEAD 的话! - Sion C
git checkout main -- 需要确保你在最新的分支上,如果主分支默认没有指向头部! - Sion C

14

我最终采用的解决方案是手动重新创建历史记录。我这样做主要是因为我不想花太多时间寻找一个优雅的解决方案,而且历史记录并不是很多(大约有30个提交需要手动合并)。

因此,在我合并巨大的上游之前,我创建了一个分支:

git checkout -b remove-history-fix <commit ID before merge>

然后使用--squash选项重新合并上游。

git merge --squash <upstream tag>

然后手动从旧分支(具有大量上游历史记录的分支)合并后挑选提交内容。

git cherry-pick <commit ID>
在所有这些提交合并到我的remove-history-fix分支之后,我将带有上游历史记录的分支删除了。
git branch -D <upstream-history-branch>

1
请接受您的答案。 :) - sjas
只是想补充一下,经过我自己多次尝试后,这个过程(或者这个过程的复制粘贴变体)也是我能找到的唯一方法,来压缩那些在合并提交淹没分支时间线后不再处于分支末端的东西。这样的合并对于需要长时间进行PR的分支至关重要(相比之下,压缩只是一个小奢侈品)。只需在任何初步合并之前添加您的rebase -i任务,它将为您节省后来的烦恼。 - leRobot
值得注意的是,通过进行压缩合并,您并没有记录实际的合并操作,因此可能会使您的上游提交变成孤立状态(例如,如果您正在压缩合并一个热修复分支,然后删除了没有标签的上游热修复分支)。 - void.pointer

4

以下是几个选项:

限制日志记录

这不完全是你所要求的,但可能是一个好的替代方案,而且更加容易。这允许您像平常一样使用git,但隐藏了所有您不想看到的东西(假设问题是历史记录杂乱无章,而不是原始存储空间。如果您在第一次获取upstream时就为合并操作获取了upstream,则在分支中压缩合并不会阻止git包括来自upstream的所有提交)。

在这种情况下,您将执行普通合并,但在记录日志时,您将向命令添加--first-parent

例如,如果没有此选项,我可能会有以下提交记录(假设“sample more”1到3实际上是更多的提交记录):

$ git log --oneline
0e151bf Merge remote-tracking branch 'origin/master' into nosquash
f578cbb sample more 3
7bc88cf sample more 2
682b412 sample more 1
fc6e1b3 Merge remote-tracking branch 'origin/master'
29ed293 More stuff
9577f30 my local change
018cb03 Another commit
a5166b1 Initial

但是,如果我添加--first-parent,它会变得更加简洁:

$ git log --oneline --first-parent
0e151bf Merge remote-tracking branch 'origin/master'
fc6e1b3 Merge remote-tracking branch 'origin/master'
9577f30 my local change
018cb03 Another commit
a5166b1 Initial

注意,我分支后主分支的所有提交(“我的本地更改”是我分叉提交)都消失了。只有我做过的提交显示出来,包括合并时的提交。如果我在合并时使用更好的提交消息,我甚至可能知道这批更改是什么。

替换历史记录

这是你所要求的内容。

https://git-scm.com/book/en/v2/Git-Tools-Replace中获取灵感。

我们将在此处压缩远程历史记录,用我们的压缩版本替换他们的历史记录,并合并压缩版本。

在我的示例存储库中,上游添加但我尚未合并的修订版为682b412“sample more 1”到origin/master(f578cbb“sample more 3”)(虽然对于此示例不那么长,请假装之间有50个提交或其他)。

我想要的第一件事是远程侧的本地分支:

git checkout -b squashing origin/master

接下来,我想快速压缩它

git reset --soft 682b412~
git commit -m "Squashed upstream"

请注意波浪线字符~。这会导致我们的分支位于我们要合并的第一个提交的父级,并且因为我们指定了--soft,所以我们的索引仍然位于我们要合并的最后一个提交处。提交行结果是一个包含我们从第一个到最后一个提交的单个提交。
此时,origin/master和squashing分支具有相同的树内容但不同的历史记录。
现在,我们告诉git,在看到对origin/master原始提交的引用时,使用我们的压缩提交代替。使用git log我可以看到新的"Squashed upstream"提交是1f0bc14,所以我们执行:
git replace f578cbb 1f0bc14

从这里开始,你的Git将使用“压缩上游”提交。

回到我们原来的分支(如果它是“master”)

git checkout master
git merge f578cbb

这似乎是将源主分支(f578cbb)合并,实际上获取了1f0bc14的内容,但将其记录为具有父SHA1为f578cbb

我们不再需要压缩分支,所以可以摆脱它。

现在,假设上游添加了更多功能。 在此简单示例中,在上游的存储库上,日志可能显示如下:

84f5044 new feature
f578cbb sample more 3
7bc88cf sample more 2
682b412 sample more 1
29ed293 More stuff
018cb03 Another commit
a5166b1 Initia

在我们拉取上游代码后,如果从我们的代码库查看其日志,我们会看到以下内容:

84f5044 new feature
f578cbb squashed upstream
29ed293 More stuff
018cb03 Another commit
a5166b1 Initial

请注意,它似乎也把历史记录压缩了一样,更重要的是,压缩后的上游SHA1显示了上游历史记录中使用的SHA1(对于他们来说,这确实是“示例3”的提交)。
因此,合并继续像往常一样工作。
git merge origin/master

但是我们没有这样混乱的日志:
4a9b5b7 Merge remote-tracking branch 'origin/master' for new feature
46843b5 Merge remote-tracking branch 'origin/master'
84f5044 new feature
f578cbb squashed upstream
fc6e1b3 Merge remote-tracking branch 'origin/master'
29ed293 More stuff
9577f30 my local change
018cb03 Another commit
a5166b1 Initial

如果上游的“新功能”提交也是大量的提交,我们可以重复这个过程来压缩它。

1
我遇到了类似的问题。当你解决合并冲突时,如果没有最新的提交历史,就会出现这个问题。当你有一个旧的PR,并且自从创建旧的PR以来,主分支已经合并了许多更改时,就会重现这个问题。以下是我解决它的方法:
  • 使用git命令git fetch获取最新的提交历史。在主分支和特性分支上都执行此操作。
  • 然后解决合并冲突。

0

无法这样做,因为您将无法将更改推回或合并到该远程存储库或该项目的任何其他存储库中。当压缩时,您正在更改历史记录,导致您的存储库和远程存储库之间的sha1哈希不同。

您必须接受大量历史记录。


没有强制的方法吗?将来我可能需要回推或合并,我希望能像以前一样操作。 - E-rich
这次你是如何合并的?那个仓库的起源是什么? - Femaref
起源是从头开始的。一开始我收到了一个tar文件,里面有源代码,然后我进行了更改/添加,接着想要合并来自官方上游库的更新/修复。合并需要相当数量的手动合并,这对我来说没问题,因为我不会经常做这件事。 - E-rich
你能否更新问题并附上你用于从上游合并的命令? - Femaref

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