如何在不添加分支的完整提交历史的情况下合并来自另一个仓库的分支?

问题描述 投票:1回答:3

题:

有没有办法从另一个仓库合并分支,但只将该分支中的最新提交添加到我们的提交历史记录中?

背景:

我们正在开发一个UE4项目。当Epic发布更新时,我们尝试更新到最新的引擎版本。我们的工作流程是这样的:

dev:          a - b -- c -- d -- e -- f -- g - h - i
             /             /              /
upstream:  A (4.19) - B - C (4.20) - D - E (4.21) - F - G

注意,b和c代表几百次提交,B代表通常几千次提交。当我们将C合并到我们的repo中时,我们“获取”由B表示的所有提交。这些额外的提交将膨胀添加到我们的repo中,并在BitBucket的历史视图和基本的git log输出中显示与我们自己的提交交错。

我最后一次合并 - 比如上图中的C - 我把它作为一个squash合并,它给了我所有的更改,但只做了一次提交。

不幸的是,我意识到事实上(我还在学习git)这有效地切断了上游提交的链接。因此,当我去合并E时,共同基础提交是A而不是C.就git而言,我独立完成了我们分支中B和C的工作。我收到了数以万计的合并冲突,这些冲突来自于在B&C中修改过的文件,然后在D&E中进一步修改。

值得庆幸的是,通过重新合并C,保留历史记录,然后合并E,我能够相当容易地恢复。

但是,我回到原来的问题。我希望能够将导致C的所有更改合并到我们的repo中,但实际上,只有C出现在我们的repo的提交历史中(当我合并E时用作公共基础)。有没有办法做到这一点?

谢谢你的时间!

git merge
3个回答
2
投票

简短的回答是:不,你做不到。你可以做一些可能就足够的事情。

历史不过是承诺。提交是历史。

每个提交都有自己唯一的哈希ID。在非常真实的意义上,该哈希ID是提交 - 尽管从技术上讲,它是该提交内容的加密校验和。内容包括保存的源快照的哈希ID,以及前一次提交的哈希ID。这是允许Git从最后一次提交开始并向后工作,一次提交一次,通过一系列提交:commit Z有父哈希Y,所以Git可以找到Y并看到它有父哈希X,所以上。

合并提交在一种方式中是特殊的:它们具有多个父哈希。 (通常它们只有两个;两个以上是章鱼合并,这些并不能真正实现任何多个单独合并无法完成的任务,尽管它们对于显示合并的目标是为了一起修改一堆,当然是为了炫耀一个人的Git-fu。:-))在合并提交时,Git将遵循这两个历史,除非你告诉它不要(见下文)。

正如您所看到的,git merge的工作原理是遵循历史 - 一个向后看的提交链 - 回到共享提交。你要么有提交,要么共享;或者你没有它们,所以没有什么可做的。然后,对于普通的git merge,它进行合并提交,记住两个直接的前任,这将启用未来的合并。使用git merge --squash剪辑额外的父母,这至少可能,并且通常实际上太多 - 使得未来的合并变得更加困难,因为你得到一个古老的祖先而不是理想的现代祖先。

What you can do

通常,git log遵循历史记录 - 所有历史记录 - 通过步骤提交图,一步一步,向后:

...--o--o--o--o   <-- branch (HEAD)

当历史是线性的(没有合并)时,这很好,但是当它有一个合并时:

          o---------o-------o
         /                   \
...--o--o                     *--o--o   <-- branch (HEAD)
         \                   /
          o--o--o--o--o--o--o

Git将遵循合并*的两条腿,它一次只做一次。但你可以告诉它不要这样做:

git log --first-parent

这个--first-parent选项告诉Git当遇到上面的*之类的合并提交时,它应该只查看合并的第一个父级。

哪位家长是第一位家长?答案是:合并的第一个父项是进行合并时当前提交的提交。所以在这种情况下,我们有:

          o---------o-------o   <-- branch (HEAD)
         /
...--o--o
         \
          o--o--o--o--o--o--o   <-- other

在你跑git merge之前。你运行git checkout branch进入这个状态。然后你运行git merge other使合并提交*。因此,提交*的第一个父级是顶行提交,即运行git merge时所在的提交。

因此,git log --first-parent根本不会显示最底层的提交。它们仍将存在,是历史的一部分,使未来的合并能够很好地工作,当然也会使您的存储库变大,但您不会看到它们。

大量的git log论点是关于没有看到特定的提交:除草树,以便你可以看到森林。例如,git log --simplify-by-decoration会跳过显示没有分支或标记名称的任何提交。使用git log [--follow] -- <path>,你告诉Git不要显示不改变给定文件或子树的提交。还有其他选择可以影响这种“历史简化”的工作方式,并且它们变得相当复杂。您可以在几天内学习git log手册页。但是从--first-parent开始。


1
投票

有没有办法从另一个仓库合并分支,但只将该分支中的最新提交添加到我们的提交历史记录中?

有很多方法可以做到这一点,但不是完全有你的蛋糕和隐藏它太感觉。这听起来很尴尬,对不起,但我找不到更简单的方法来说明:合并历史必然会合并你合并的历史。

因此,要么将合并的历史记录修剪为您想要的提交,要么将合并历史记录的显示修剪为您想要的提交。两者都是可行的,它们甚至很容易。

要了解这里涉及多少实际回购膨胀,您可以制作修剪历史并比较结果;按照承诺,这很简单:

git clone --bare . --single-branch --branch upstream `mktemp -d`
cd $_

一个简单的du -sh可以为您的上游分支在您的回购中占用多少空间提供合理的基线。

要将历史记录剥离为高光,您可以

git filter-branch --tag-name-filter 's,^,sliced-,' -- upstream --simplify-by-decoration
git clone --no-hardlinks --bare . --single-branch --branch upstream `mktemp -d`
cd $_
du -sh

并查看可以节省多少回购空间。我在Git分支上运行了这个,703个标记的提交,~55K提交。它节省了100MB的磁盘空间。我的截图目录需要更多。 Git结账需要三倍。

如果重要的是让你的git log显示器变得杂乱,你不必做任何这些。在您的回购中,做

mkdir .git/info
git rev-list upstream --parents --simplify-by-decoration >.git/info/grafts

这就是你所需要的。


-1
投票

假设您的上游分支被称为上游分支,并且您希望将其合并到您的开发分支中:

git checkout development
git merge --squash upstream
git commit

这将从上游分支获取所有提交,将它们压缩为1个提交,并将其与您的开发分支合并。

© www.soinside.com 2019 - 2024. All rights reserved.