是否可以维护两个具有完全不同内容的本地Git存储库,但是将它们推送到同一个远程存储库?

时间:2017-12-13 20:17:43

标签: git

我是一名研究生,正在尝试使用Github进行科学研究。通常我可能会在不同的服务器上为同一个项目执行不同的任务。我并不总是希望同时在同一台服务器上使用所有这些功能,但将它们组织在一个远程位置会很不错。

是否可以将不同内容的两个目录推送到相同的GitHub存储库/源?基本上,在这种设置中,我几乎不会内容;只是推动它以保持自己的组织,并可能与合作者分享。或者也许使用sparse-checkout偶尔克隆单个文件/子目录。

2 个答案:

答案 0 :(得分:3)

可以做到;但是,与维持两个远程回购相比,它可能并没有真正获益。

基本上,您为每个不同的内容集合都有一个分支(或一组分支)。然后,您可以使用refspecs来控制在给定仓库中映射的(一组)分支。

例如,您可以创建一个像这样的回购

git init
git checkout --orphan content-A/master
# stage content for the "A" clones
git commit -m "Init A"
git checkout --orphan content-B/master
# stage content for the "B" clones
git commit -m "Init B"

现在您可以将其推送到github仓库,任何人都可以克隆它,然后选择要检出的分支集。

如果你想获得幻想,并确保一个克隆"根本没有在其数据库中拥有B历史记录(即,如果每组具有历史记录的内容占用大量磁盘空间并且您只想保留所需内容),那么也可以这样做。你必须克服git复制整个回购的倾向。一种方法是

git clone --single-branch --branch=content-A/master url/of/remote/repo

这为只拉动一个分支设置了合适的refspec。如果每个content-x/命名空间包含多个分支,则必须略微调整配置;

之类的东西
git config remote.origin.fetch +/refs/heads/content-A/*:/refs/remotes/origin/content-A/*

答案 1 :(得分:3)

是可能的。

虽然你可以通过谨慎的训练使它变得有用,但它并不是特别有用。

Git基本上是一个由两部分组成的系统:它是一个对象数据库,它是一个简单的键/值存储;并且有一个命名系统,它是第二个简单的键/值存储。命名系统中的键是 references ,它们是refs/heads/masterrefs/heads/branchrefs/tags/v1.2等字符串,它们的值是哈希ID。对象数据库中的键是哈希ID,它们的值是Git对象。

事实上,命名系统使用任意密钥 - 除了领先的refs/要求之外,任意密钥 - 允许您执行您建议的操作。它就是这些键的方式'值工作,以及对象数据库键/值系统的工作方式,这使它不那么有用。

如果您将名称键想象为一个简单的平面表,您可以:

name                value
------------------------------
refs/heads/master   1234567...
refs/heads/branch   fedcba9...

运行git push时,git push操作的最后一部分是向服务器提供一些名称/值对(在本例中为GitHub)并要求他们设置这些名称/值在这个表中对。他们要么接受设置这些请求,要么拒绝它(根据具体情况,即如果你提供两个名称/值对,他们可以接受一个并拒绝另一个)。

它是复杂的数据库的哈希ID /对象部分。每个哈希ID对于其特定对象是唯一的。有四种对象类型:提交标记 blob 。所有共享标题编码(ASCII中的类型名称,ASCII空白,对象的大小编码为十进制数,没有前导零,以及全零位字节),后跟类型特定数据。 (实际的散列ID只是此标头的SHA-1散列加上特定于类型的数据字节。)

blob对象表示原始数据,通常是文件,因此它只包含标题后的原始字节。具有相同大小和内容的两个文件具有相同的散列,即,是相同的blob,因此如果存在文件README,然后是具有相同内容的第二个文件README~,那么&# 39;两个文件只有一个存储库blob。

对于Git按名称存储文件,它还需要存储文件名,因此它使用树对象来完成。一个树对象有一个特殊的,众所周知的内部格式,虽然它是二进制的,但基本上是你在运行git ls-tree时看到的:对于树或子树中的每个文件,Git存储一个模式,一个Git对象哈希ID和文件名。 (通过读取对象找到对象类型。)

为了让Git在提交中存储快照,它需要存储与该提交相关联的树对象。因此,作为对象数据的一部分,提交对象具有树形哈希ID。每个提交还记录其父或子哈希ID,以便提交对象表示有向非循环图。父哈希ID充当提交所代表的顶点 V 的出站边 E G =(V,E)是提交DAG。在此提交DAG中,每个提交都指向一个树,该树指向子树和/或blob,因此Git可以使用提交DAG查找提交,这些提交查找树以获取文件名和blob ID以提取这些提交的文件

为了存储带注释的标签,Git使用最后一个对象类型,它也存储一个哈希ID,保存带注释标签的目标对象。因此,带注释的标记对象指向提交(通常,Git允许每个带注释的标记对象指向任何对象类型,尽管提交是常态)。

由于所有这些编码,任何Git对象都可以从某个其他Git对象到达(通过从某个适当的起点遍历提交图),或者无法访问。给定所有Git对象散列ID的完整列表,可以使用任何广度优先或深度优先搜索算法来查找哪些提交可以从某些起始点到达,哪些不是。

这是我们将此对象数据库与reference-name / hash-ID-value数据库放在一起的地方。所有这些引用名称及其对应的哈希ID值都是图中的入口点。我们可以从这些起点哈希中获得的提交是Git将保留的提交。从这些引用名称无法访问的任何标记或提交都有资格进行垃圾回收。任何通过垃圾收集标记和/或提交对象而丢失所有引用的树和blob也可以处置。

因此,如果我们有一个如下图:

o <--o <--o   <-- refs/start1
 \
  o <--o    <-- refs/start2
   \
    *
然后我们从两个起点开始,标记那些可达的提交,标记他们的父母(向左)可达,并标记他们的祖父母可达。祖父母没有父母 - 它是根提交 - 过程停止;我们没有标记的所有提交都是无法访问并且可以被丢弃。在这种情况下,这只是提交*

图表不需要像这样完全连接。我们可以有两个不相交的子图:

o--o--o   <-- refs/start1

o--o--o   <-- refs/start2

所有这些提交也都被引用,因此可以安全地收集。

当您运行git push时,您的Git会调用另一个Git并提供它,而不仅仅是名称/值对,还提供由这些名称标识的提交(和其他对象) /值对。如果尚未拥有它们,接收Git将要求提交这些提交,然后查看其父ID并在必要时请求提交,等等。接收Git将要求创建图表所需的其他树和blob对象。因此,如果您有两个独立的Git存储库,其中包含不同的名称,并且每个存储库都有git push,那么您将获得我们在上面看到的那种不相交的子图。

但是当你运行git clone时,克隆Git要求发送Git(通常)所有的名称/值对,然后所有可以从这些值访问的提交(和其他对象)。所以做克隆的人得到了所有不相交的子图。

您可以设置Git存储库,以便不会询问所有名称/值对。此存储库将在git fetch上(不是git pull - 只有git fetch后跟第二个Git命令,因此git fetch是有趣的部分),只接受一些的名称/值对,因此只接受提交和其他对象的一些。这样您就可以提取GitHub存储库的部分或全部独立子图。

你从这样做中获得的,与使用两个不同的GitHub存储库,每个子图一个,在克隆和推送时会遇到很多麻烦:你不能使用直的git clone或者你会得到所有东西,当你从分离的存储库推送时,你必须非常小心避免引用名称冲突。因此最终效果是你自己很难使用,基本上没有任何好处。