计算机上同一存储库的多个副本

Yur*_*kee 5 git diskspace github git-clone

我有一台本地计算机,上面有同一个 GIT 存储库的多个副本,每个副本针对不同的用户。所以它可能看起来像这样:

/home/userA/BigRepository
/home/userB/BigRepository
/home/userC/BigRepository
/home/userD/BigRepository
/home/userE/BigRepository
Run Code Online (Sandbox Code Playgroud)

假设每个存储库使用约 2-3GB,20 个用户将使用 40-60GB 不必要的冗余数据。用户可能会在他们的私有分支上开发一些东西,但大多数数据仍然是冗余的。这就是为什么我想优化磁盘使用。

我想知道解决这个问题的最佳方法是什么。


我已经检查过的内容:

  • git clone --local- 每个存储库将与裸存储库共享 .git/objects,但这意味着 .bare 存储库必须在本地可用(所以它不能是 GitHub,对吧?)
  • git clone --depth <n>- 这将减少存储库的大小,但也会将本地历史记录减少到 n 个对象。
  • git clone --shallow-since- 据我了解,它的工作方式与--depth选项类似,但会存储自指定时间以来的提交。
  • git clone --separate-dir- 我自己的想法是使用同一个地方来存储所有 .git 目录。(因此,在进行克隆时,20 个存储库中的每一个都会链接到同一个位置。尚不知道是否可能,只是分享我的想法。

是否--depth意味着存储库最多有 n 次提交,或者仅在克隆时检查,然后存储库可以随时间增长?

tor*_*rek 5

\n
    \n
  • git clone --local- 每个存储库将与裸存储库共享 .git/objects,但这意味着 .bare 存储库必须在本地可用(所以它不能是 GitHub,对吧?)
  • \n
\n
\n\n

不太正确,不。您可以将其与任何本地克隆一起使用,无论是否裸露。但一般来说,在这完全有效的情况下,您也不需要--local:您只需从本地路径名进行克隆即可。

\n\n

例如,假设userA的主目录为/home/userA,克隆 GitHub 存储库,从而创建完整的非裸克隆。进一步假设userB可以读取. /home/userA因此,用户 B 可以执行以下操作:

\n\n
git clone /home/userA/BigRepository\n
Run Code Online (Sandbox Code Playgroud)\n\n

创造./BigRepository。如果他在他的主目录中执行此操作,他最终会得到,其中包含与\ 的克隆/home/userB/BigRepository相同的提交。userA

\n\n

因为 Git 将建立硬链接,所以如果用户 A 现在删除他的存储库,他不会重新获得他的空间(因此,如果磁盘配额生效,用户 A 不会恢复他的配额)。用户 B 仍然具有指向用户 A 拥有的文件的链接。一切仍然有效;只是无论是谁制作了第一个克隆,都已经“支付”了存储库的初始存储费用。

\n\n

(用户 B 为自己的工作树“付费” 。他共享用户 A 创建的.git/objects 文件,包括包文件。这些文件在任何时候都是只读的,无论用户 B 是否共享用户 A\' s 文件,因此用户 B 无法写入这些文件这一事实并不重要。)

\n\n

此过程的一个非常小的缺点是用户 B 可能想要更改其originURL 以指向 GitHub 存储库而不是用户 A 的克隆,并且在他这样做之前,他将看不到用户 A 看到的同一组远程跟踪名称 ( origin/*names)。

\n\n

用户 C 可以使用前面的任一存储库重复此过程。

\n\n
\n
    \n
  • git clone --depth <n>- 这将减少存储库的大小,但也会将本地历史记录减少到 n 个对象。
  • \n
\n
\n\n

大多数情况下,是的。不过,就数字n而言,技术上是错误的:

\n\n
\n

是否--depth意味着存储库最多有 n 次提交,或者仅在克隆时检查,然后存储库可以随时间增长?

\n
\n\n

它们不仅会随着时间的推移而增长,而且数字n并不意味着您所建议的内容。这是深度,而不是提交次数。在这种情况下,深度是一个技术术语,指的是图遍历。

\n\n

请记住,Git 使用提交作为其基本存储单元。(提交可以进一步细分,但出于我们的目的,它们是单位。)每个提交都有一个唯一的哈希 ID,并且可以表示为图中的节点或顶点。每个提交还存储其直接前一个提交的哈希 ID:这些提交形成连接节点的单向边或,从而形成图的其余部分。

\n\n

我们可以像这样绘制图表的各个部分:

\n\n
... <-F <-G <-H\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中每个字母代表提交哈希 ID。每次提交中存储的哈希 ID 充当指向早期提交的指针。为了方便地找到这条链的末端,我们\xe2\x80\x94或Git\xe2\x80\x94建立一个分支名称,或某种其他形式的名称,指向链中的最后一个提交:

\n\n
...--F--G--H   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

(我们变得懒惰并将连接弧绘制为线,原因很简单,任何提交都无法更改,因此此时箭头的走向并不重要\xe2\x80\x94,尽管在其他时候,重要的是要记住它们本质上指向向后指向的,这迫使 Git 始终向后工作)。

\n\n

现在,具有这些向后指向箭头的图形可以在其中包含分叉和连接:

\n\n
          o--o         o--o--H   <-- branch1\n         /    \\       /\n...--o--o--o---o--o--o--o--K   <-- branch2\n         \\          /\n          o--o--o--o\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我们遍历这个图时,我们从末端\xe2\x80\x94开始,在普通图中,我们从头开始,但Git向后工作\xe2\x80\x94 H,就像 name 所指向的commit 一样branch1。如果我们选择--depth 3,Git 将拾取H两个较早的提交,并且K和 两个较早的提交:

\n\n
          o--o--H   <-- branch1\n         /\n<snip>--o--o--K   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们--depth 3有 6 次提交,因为从每一端返回 3 次让我们将这些提交从完整的图表中剔除。如果我们去--depth 4我们得到:

\n\n
               o--o--H   <-- branch1\n              /\n  <snip>--o--o--o--K   <-- branch2\n         /\n<snip>--o\n
Run Code Online (Sandbox Code Playgroud)\n\n

这些“剪断”点中的每一个都代表一个浅层移植,我们知道其中更多提交,但我们故意省略了这些提交。当 Git访问.git/shallow其父项在.git/shallow,不会尝试查找父提交。

\n\n

参数--depth选择剪切点。git fetch这发生在\xe2\x80\x94git clone是一个奇特的六部分包装器时,其中包括git fetch第五步。剪切点保留在那里,除非您运行git fetch带有特定参数的命令来深化或进一步浅化存储库。新的提交以通常的方式添加,并使图表更深,包括任何git fetch任何用户运行的任何操作。

\n\n
\n
    \n
  • git clone --shallow-since- 据我了解,它的工作原理类似于--depth选项类似,但会存储自指定时间以来的提交。
  • \n
\n
\n\n

是的:这只是一种更有用、更不容易混淆的设置“剪切”点的方法。

\n\n
\n
    \n
  • git clone --separate-dir
  • \n
\n
\n\n

你的意思是--separate-git-dir。这没有什么实际意义:您在此处指定的目录由克隆操作创建并填充。如果与任何早期选项结合使用,这将有助于减少所需的空间,但否则它只是将工作树与存储库分开。

\n\n

在标准设置中,存储库本身出现工作树中名为 的子目录中.git。,仍然--separate-git-dir出现.git在工作树中,但这次它是一个包含存储库保存路径的文件。无论哪种方式,每个用户都独立支付存储成本,除非--local通过克隆其他用户的存储库暗示使用。

\n\n

每个用户都有自己的实际存储库,这一点很重要

\n\n

如果用户 A 进行新的提交,他的 Git 必须将一个或多个对象写入他的.git/objects. (由于提交始终是唯一的,因此该操作至少需要写入该对象。它可能还需要写入一些树对象,为了实现这一点,Git 可能必须创建一些 blob 对象。)

\n\n

同时,如果用户 B 进行新的提交,他的 Git 必须将一个或多个新对象写入他的.git/objects. 如果用户 A 和 B 实际上共享 Git存储库,则 A 和 B 必须对其他用户的文件和目录具有写权限。这种模式可以工作,但它有一个额外的缺点:每个用户必须非常小心,不要意外踩到其他用户。虽然存储库的大部分\xe2\x80\x94(包括建议共享的.git/objects部分\xe2\x80\x94)由写入后永远不会更改的对象组成,但其他部分,包括特殊文件.git/HEAD和许多其他文件(例如分支)头数据和引用日志,必须是每个用户私有的,否则\xe2\x80\x94并且这种替代方案通常是行不通的\xe2\x80\x94只有一个用户可以在任何时间做任何实际工作。

\n\n

理论上git worktree add可以用在这里

\n\n

然而,它并不是为这种用途而设计的。如果您愿意,您可以尝试一下:为每个用户添加一个工作树,然后授予该用户对与该用户关联的所有文件的权限(额外的文件位于 内的子目录中.git)。

\n\n

为此设计的东西--reference

\n\n

为解决这个问题而设计的是选项--reference。使用 时--reference,作为计算机管理员的您将首先对 GitHub 存储库进行完整克隆。你可以做这个--bare或不做\xe2\x80\x94它\并不重要\xe2\x80\x94但你可能想让它成为一个--mirror克隆,以便它获得每个引用并且可以更容易地更新。(我在之前的工作中对此进行了一些尝试,这里存在一些问题使得更新变得棘手,所以这可能不像您一开始想象的那么有用。)

\n\n

一旦这个“参考克隆”存在,每个用户都可以执行以下操作:

\n\n
git clone --reference <path> <github-url>\n
Run Code Online (Sandbox Code Playgroud)\n\n

他们的 Git 将联系 GitHub 上的 Git,并从中获取制作完整克隆所需的信息。但是,他们不会实际创建完整克隆,而是检查引用克隆以查看它是否已经具有他们想要的对象。无论何时何地,引用克隆已经拥有这些对象,他们的 Git 将仅使用该现有引用克隆中的那些现有对象。

\n\n

这意味着它git clone本身运行速度非常快,并且几乎不使用额外的磁盘空间。制作原始的约 3GB 参考克隆可能需要几分钟甚至几个小时,但当其中一个用户执行此git clone --reference操作时,它应该在几秒钟内完成。此外,它的工作方式“干净”,因为如果他们需要从 GitHub 获取新对象,他们只需像往常一样GitHub 获取它们。因为没有任何类型的 commit\xe2\x80\x94no Git 对象,实际上\xe2\x80\x94 可以更改,所以引用克隆仅用于提供最初放入其中的所有对象。新对象逐渐扩展每个用户的存储库。

\n\n

(您将来可以更新参考克隆。然后,各个用户可以重新克隆以减少磁盘使用量。这里棘手的部分是,您必须确保没有对象和包文件从参考中消失在您更新它的时间和他们重新克隆的时间之间进行克隆。您可以只创建一个新的引用克隆,等到所有用户都重新克隆了新的引用克隆,然后删除原始引用,以避免这种棘手的情况。)

\n