繁体   English   中英

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

[英]How can I merge a branch from another repo without adding the branch's full commit history?

题:

有没有一种方法可以合并另一个仓库中的一个分支,但是只将该分支中的最新提交添加到我们的提交历史中?

背景:

我们正在从事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合并到存储库中时,我们“获取”由B表示的所有提交。这些额外的提交使存储库膨胀,并在BitBucket的历史记录视图和基本git log输出中与我们自己的提交交错显示。

上次我进行合并时(例如上图中的C),我以壁球合并的方式进行了合并,这给了我所有的更改,但只进行了一次提交。

不幸的是,在事实(我仍在学习git)之后,我意识到有效地切断了与上游提交的链接。 因此,当我合并E时,通用的基本提交是A而不是C。就git而言,我已经独立完成了分支中B和C中的工作。 我在B&C中修改了文件,然后在D&E中进一步修改了文件,产生了成千上万的合并冲突。

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

但是,我回到了最初的问题。 我希望能够将导致C的所有更改合并到我们的存储库中,但从本质上讲,只有C出现在我们存储库的提交历史记录中(当我合并E时用作通用基础)。 有什么好方法吗?

谢谢你的时间!

简短的答案是:不,您不能这样做。 可以做一些事情, 可能就足够了。

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

每个提交都有自己的唯一哈希ID。 从本质上讲,该哈希ID 提交,尽管从技术上讲,它是该提交内容的加密校验和。 内容包括保存的源快照的哈希ID,以及前一次提交的哈希ID。 这就是让Git从最后一次提交开始并向后工作的一次,通过一系列提交:一次提交Z具有父哈希Y ,因此Git可以找到Y并看到它具有父哈希X ,等等上。

合并提交的一种特殊方式是特殊的:它们具有多个父哈希。 (通常它们只有两个;一个章鱼合并超过两个,这些并没有真正实现无法通过多个单独的合并完成的任何事情,尽管它们对于表明合并的目标是将一个一堆修订同时进行,当然是为了炫耀自己的Git-fu。:-))在合并提交时, 除非您不告诉 Git, 否则 Git将遵循两种历史(请参见下文)。

如您所见, git merge通过遵循历史记录(向后看的提交链)回到共享提交来工作。 您要么拥有提交,然后就可以共享它们; 或您没有它们,因此无事可做。 然后,对于普通的git merge ,它将进行合并提交,同时记住两个直接的前任,从而可以进行将来的合并。 使用git merge --squash附加的父对象,这至少有可能,而且在实践中也是如此,这会使将来的合并变得更加困难,因为您要获得古老的祖先而不是所需的现代祖先。

能做什么

通常, git log通过向后遍历提交图来一次跟踪历史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将遵循merge * 两条腿 ,它会一次提交一次。 但是您可以告诉它不要这样做

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进行合并提交* 因此,commit *第一个父级是最上面一行的提交,即您运行git merge时所在的提交。

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

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

有没有一种方法可以合并另一个仓库中的一个分支,但是只将该分支中的最新提交添加到我们的提交历史中?

有很多方法可以做到这一点,但从根本上讲,还不能完全掩饰自己。 抱歉,这听起来很尴尬,但是我找不到简单的表达方式:合并历史记录必然会合并您合并的历史记录。

因此,您可以将合并的历史记录修剪为仅所需的提交,或者将合并的历史记录的显示修剪为仅所需的提交。 两者都是可行的,甚至很容易。

要发现此处涉及多少实际回购膨胀,可以整理历史记录并比较结果。 如所承诺的,这很容易:

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

这就是您所需要的。

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

git checkout development
git merge --squash upstream
git commit

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM