一台机器上相同存储库的多个副本

Multiple copies of same repository on a machine

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

/home/userA/BigRepository
/home/userB/BigRepository
/home/userC/BigRepository
/home/userD/BigRepository
/home/userE/BigRepository

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

我想知道最好的方法是什么。


我已经检查过的内容:

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

您可以尝试将 .git 目录从一个位置符号链接到所有其他工作区

git clone git@server:BigRepository /home/userA/BigRepository
mkdir /home/userB/BigRepository/
ln -s /home/userA/BigRepository/.git /home/userB/BigRepository/.git

但是,每个人都会更改其他人的分支,即您的 master 分支可能会意外移动。您的工作区不会更改,因此您的文件会按预期运行。但是Git会突然报变化

  • git clone --local - Each repository will share .git/objects with a bare repository, but this implies .bare repository mush be available locally (so it cant be GitHub, right?)

不太对,不。您可以将其与任何本地克隆一起使用,无论是否裸机。但总的来说,如果这完全有效,您也不需要 --local:您可以从本地路径名克隆。

例如,假设 userA,其主目录是 /home/userA,克隆了 GitHub 存储库,制作了一个完整的非裸克隆。进一步假设 userB 可以 /home/userA 读取 。因此,用户 B 可以:

git clone /home/userA/BigRepository

创建./BigRepository。如果他在他的主目录中执行此操作,他将得到 /home/userB/BigRepository,其中包含与 userA 的克隆相同的所有提交。

因为 Git 会创建硬链接,如果用户 A 现在 删除 他的存储库,他不会重新获得他的 space (所以如果磁盘配额是实际上,用户 A 没有取回他的配额)。用户 B 仍然具有指向用户 A 所拥有的文件的链接。一切仍然有效;只是制作第一个克隆的人拥有 "paid for" 适当的存储库的初始存储。

(用户 B "pays for" 他自己的 工作树 。他共享 .git/objects 文件 ,包括pack 文件,用户 A 创建。这些文件都是只读的,在任何时候,无论用户 B 是否共享用户 A 的文件,所以用户 B 不能 write 对那些文件并不重要。)

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

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

  • git clone --depth <n> - which will reduce the size of the repo, but also reduce local history to n objects.

大多数情况下,是的。尽管 n 在技术上是错误的:

Will --depth imply that repositories will have at most n commits, or is it checked only when cloning, and then the repository can grow with time?

它们不仅随着时间的推移而增长,数字 n 并不代表您的建议。它是深度,而不是提交次数。本例中的深度是指图遍历的技术术语。

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

我们可以像这样绘制图形的位:

... <-F <-G <-H

其中每个字母代表一个提交哈希 ID。每个提交中存储的哈希 ID 充当指向早期提交的指针。为了方便地找到这个链的 end,我们——或者 Git——建立一个 分支名称,或者其他形式的名称,指向链中的 last 提交:

...--F--G--H   <-- master

(在这里我们变得懒惰并将连接弧绘制为线,原因很简单,因为任何提交都不能更改,所以在这一点上箭头的方向并不重要——尽管在其他时候, 重要的是要记住它们固有地指向 向后 ,这迫使 Git 始终向后工作)。

现在,带有此类向后箭头的图形可以在其中进行分叉和连接:

          o--o         o--o--H   <-- branch1
         /    \       /
...--o--o--o---o--o--o--o--K   <-- branch2
         \          /
          o--o--o--o

当我们遍历这个图时,我们从终点开始——在普通图中我们从起点开始,但是Git向后工作——就像提交H,正如名称 branch1 所指向的那样。如果我们选择 --depth 3,Git 将选择 H 和两个较早的提交,以及 K 和两个较早的提交:

          o--o--H   <-- branch1
         /
<snip>--o--o--K   <-- branch2

我们的 --depth 3 得到了 6 次提交,因为从每一端返回 3 次使我们得到了完整图表中的这些提交。如果我们转到 --depth 4,我们会得到:

               o--o--H   <-- branch1
              /
  <snip>--o--o--o--K   <-- branch2
         /
<snip>--o

每个 "snip" 点代表一个 浅移植 ,我们知道那里有 更多提交,但我们我故意 省略了 那些提交。省略的提交的哈希 ID 被写入 .git/shallow 并且 Git 知道,当它访问其父项列在 .git/shallow 中的提交时,不会试图找到父提交。

--depth参数选择剪辑点。这发生在 git fetch 的时候——git clone 是一个花哨的六部分包装器,其中包括 git fetch 作为第五步。剪辑点保留在那里,它们所在的位置,除非并且直到您 运行 a git fetch 带有特定参数以加深或进一步浅化存储库。新提交以通常的方式添加并使图表更深,包括任何用户 运行.

的任何 git fetch 操作
  • git clone --shallow-since - as I understand it will work similarly to --depth option but will store commits since the specified time.

是的:它只是一种更有用且不易混淆的设置 "snip" 点的方法。

  • git clone --separate-dir

你是说 --separate-git-dir。这没有实际意义:您在此处指定的目录由克隆操作创建和填充。如果与任何较早的选项结合使用,那将有助于减少 space 的需要,否则它只会将工作树与适当的存储库分开。

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

重要的是每个用户都有自己的实际存储库

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

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

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

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

为此设计的东西是--reference

设计用来处理这个问题的是--reference选项。使用 --reference,您作为计算机的管理员,首先要完整克隆 GitHub 存储库。您可以将其设为 --bare 或不设为它——这并不重要——但您可能希望将其设为 --mirror 克隆,以便它获得每个引用并可以更轻松地更新。 (我在之前的工作中对此进行了一些试验,这里有一些问题使得更新变得棘手,所以这可能不像你一开始想象的那么有用。)

一旦 "reference clone" 存在,每个用户都可以:

git clone --reference <path> <github-url>

他们的 Git 将联系位于 GitHub 的 Git,并从中获取他们制作完整克隆所需的信息。但是,他们并没有真正制作完整的克隆,而是检查参考克隆以查看它是否已经拥有他们想要的对象。无论何时何地,参考克隆已经拥有这些对象,它们的 Git 将仅 使用 现有参考克隆中的那些现有对象。

这意味着 git clone 本身运行速度非常快,几乎不使用额外的磁盘 space。制作原始的 ~3GB 参考克隆可能需要几分钟甚至几小时,但是当其中一位用户执行此 git clone --reference 操作时,它应该会在几秒钟内完成。此外,它 "cleanly" 的工作原理是,如果他们需要来自 GitHub 的新对象,他们只需照常从 GitHub 获取它们。因为没有提交——没有 Git 任何类型的对象,真的——可以 改变 ,参考克隆仅用于提供 最初放入它。新对象逐渐扩展每个用户的存储库。

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