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

时间:2019-04-30 16:16:43

标签: git merge

问题:

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

背景

我们正在研究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的历史记录视图和基本{{1 }}输出。

我上一次进行合并-上图中的C-是作为壁球合并进行的,这给了我所有的更改,但只进行了一次提交。

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

非常感谢,我能够很容易地通过重新合并C,保留历史记录然后合并E来恢复。

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

感谢您的时间!

3 个答案:

答案 0 :(得分:2)

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

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

每个提交都有自己的唯一哈希ID。哈希ID 实际上是提交-尽管从技术上讲,它是该提交内容的加密校验和。内容包括保存的源快照的哈希ID,以及前一次提交的哈希ID。这就是允许Git从 last 提交开始并通过一系列提交来一次向后进行一次提交的原因:提交Z具有父哈希Y,因此Git可以找到Y并发现它具有父哈希X,依此类推。

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

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

可以做什么

通常,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,当它遇到上面的*之类的合并提交时,它应该仅查看合并的 first 父级。

哪个父母 是第一父母?答案是:合并的第一个父对象是进行合并时 当前提交的提交。因此,在这种情况下,我们有:

          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 :(得分: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

这就是您所需要的。

答案 2 :(得分:-1)

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

git checkout development
git merge --squash upstream
git commit

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