如何将缺失的分支添加到克隆存储库的克隆中?

How do you add missing branches to a clone of a cloned repository?

git clone 和 git clone --mirror 完全不同:

标准 git 克隆可以用作工作区。 源已知的一组分支可供检查和处理。

镜像更像是备份。您不能直接将其用作工作区。

现在假设您克隆了一个已经被克隆的存储库 根据这个问题:

生成的存储库 (clone2) 只有在第一个克隆 (clone1) 中使用过的分支。但是,clone1 仍然知道起源处的分支。有没有办法将 clone1 已知的分支添加到 clone2 而无需将(原始)原点设置为远程?

如果不清楚,我们有:

repo1:

clone1 = git 克隆 repo1.git:

clone2 = git 克隆 clone1.git:

clone2 似乎不了解 branch3 或 branch4,因此无法检查它们。 我们如何从 clone1 获取该信息?

其实这里有两个问题:

我相信 branch3 和 branch4 可用于 clone1 而 repo1 处于离线状态。

有几个用例:

我相信这应该没什么区别,但我正在使用本地文件而不是 URL 对此进行测试。所以clone2实际上是通过git clone /local/path/.git

制作的

更新

有一个我最初没有注意到并报告的并发症: git分支-r 在 clone2 上应该按照答案中的建议将 clone1 上的分支列为 origin/branch3origin/branch4 。但是,对于这个特定的回购协议,它没有。不知道为什么。

这个 repo 的特殊之处包括:

用 git pull origin 'refs/replace/*:refs/replace/*' 拉替换没有区别。

还有其他建议吗?

我已经确定了 git branch -r 工作的回购和不工作的回购之间可能存在的显着差异。

clone1 和 clone2 都应该在 .git/packed-refs 中列出遥控器,其中的行类似于 (/path/to/clone1/.git/packed-refs):

2c3c761fbac82556c2178cb28a4e728360093e67 refs/remotes/origin/branch1

出于某种原因,受影响存储库上的 clone2 没有 .git/packed-refs 中应有的所有条目。

我检查了 packed-ref 文件(在 clone2 中)引用的一些提交 ID 存在于克隆的存储库 packed-ref(clone2)中,但有些不存在。 我们似乎既失去又获得了分支!

如果我实验性地将 packed-ref 文件从 clone1 复制到 clone2,分支会出现在

git branch -r
下,它们可以被签出,但会导致分离头状态。

这是 'affected' 存储库的 git 配置。

>cat .git/config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = /path/to/clone1/.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "develop"]
    remote = origin
    merge = refs/heads/develop

用于抓取所有远程跟踪分支的 [https://git.wiki.kernel.org/index.php|standard 说明] 即使对于损坏的 repo 也有效:

git clone --mirror original-repo.git /path/cloned-directory/.git          
cd /path/cloned-directory
git config --bool core.bare false
git checkout anybranch

因此,即使是损坏的存储库也有多种解决方法。

Git 可以有多个远程连接。它不像其他一些类似系统那样仅限于单个主存储库。您可以将 repo1 添加为另一个远程存储库:

git remote add repo1 /path/to/your/original/complete/repository

然后你就可以像往常一样得到你的分支了:

git fetch repo1 branch3
git fetch repo1 branch4

如果需要,您稍后可以签出以进行处理:

git checkout branch4

首先:您只能看到上次 git fetchgit pull.

时的 repo1 视图

repo1 中存在的所有分支将存储在 clone1 中的 origin 下。

您可以列出这些分支:git branch -r

在我看来,您正在查看具有 --mirror 和不具有克隆的非常不同的典型用例,让您认为它们根本不同。其实它们只是一个更普遍的事物的常用特例。

这主要是题外话,但我认为如果你研究 git 概念并着眼于真正理解上述陈述,那么其余部分可能也会更加清晰。

所以:在clone1中,其他分支的"knowledge"是remote branch refs的形式(refs/remotes/origin/branch3, ...)检出的分支有,此外,"local" 分支引用 (refs/heads/branch1, ...)。克隆(无论是镜像还是其他)中使用的默认 refspec 设置为获取 refs/heads/*。 (区别在于 mirror 在本地将它们映射为 refs/heads/* 而 "regular" 克隆默认将它们映射到 refs/remotes/origin/*。)

您可以在 clone2 中设置 refspec - 通过设置,或在特定 fetchpull 的参数中 - 从中​​读取 refs/remotes/origin/* 引用clone1。但是还有一些问题需要思考。

首先,如果您要从 clone1 映射本地和远程引用,那么您需要在 clone2 中为它们提供不同的命名空间。也就是说,clone1 中的 refs/heads/masterclone1 中的 refs/remotes/origin/master 不同,它们可能在任何给定时间引用不同的提交;所以他们不能 both 映射到 clone2.

中的相同名称

其次,clone2关于 - 例如 - branch3 的知识在这一点上是相当间接的。 "The last time I spoke to clone1, it told me that the last time it spoke to repo1, branch3 was at commit XYZ." 了解 branch3 "from the horse's mouth" 可能更有意义。您可以通过在 clone2.

上添加 repo1 作为第二个遥控器来做到这一点

无论您是通过添加 repo1 作为来源,还是通过使用非默认 refspecs 从 clone1 复制信息,最终在 clone2 中您将拥有对应于某些分支名称的多个远程引用(例如 refs/remotes/origin/branch3refs/remotes/repo1/branch3)。这意味着可能并不总是很清楚哪个分支应该被视为本地 refs/heads/branch3 的 "upstream"。您可以通过配置来管理它,and/or 通过特定 pushfetch 命令的参数来告诉他们您在该实例中作为上游的意图。

将所有这些转化为特定的命令实际上取决于您要完成的任务;有太多的可能性,无法将它们全部列出并解释何时使用其中任何一个。如果您需要那种程度的详细信息,我建议您可以从 git configgit fetchgit push 以及 git pull 的文档开始。

这应该是一条评论,但我需要格式化,我无法在评论中做到这一点(而且又变得冗长了:-))。

除了 (这是正确的,你应该阅读它),我认为,你的措辞中的这一点揭示了理解这一点的关键:

branch3 - not checked out [on clone1] but can be at any time

关键是在这个状态下,branch3不存在.

这是我在其他答案中说过的话,我会重复:远程跟踪分支不是分支。它是远程跟踪分支名称:名称,如origin/branch3,是以refs/remotes/开头的全名的缩写形式(与分支名称[=49相比) =] 就像 branch3 一样,它是以 refs/heads/).

开头的全名的缩写形式

(事实上,"a branch"在Git中并不总是"a branch",因为Git作者习惯用不同的词重用相同的词含义——公平地说,这在英语中也一直存在,这是我们获得 puns. See also What exactly do we mean by "branch"? 的一种方式,在这种特殊情况下,我们所说的 "a branch" 是一个分支 name ,即全名以refs/heads/开头的引用。)

当您要求 git checkout 签出一个不存在的分支时,例如 branch3,Git 将扫描您所有的远程跟踪分支名称。如果恰好有 "resembles" branch3 正确,git checkout 创建 一个,使用远程跟踪分支名称派生新分支的初始哈希 ID 值。

对于存储库中存在的分支名称,该存储库必须有一个全名以 refs/heads/ 开头的引用。第二个斜线之后出现的是分支的名称。要列出某些存储库中的所有引用,运行 git for-each-ref,其默认输出是每个引用的列表、其哈希 ID 以及由该哈希标识的 Git 对象的类型。

当你制作一个克隆时,你告诉你的 Git——制作克隆的那个——如何操作它从另一个 Git 获得的引用。这是 --mirror 所做的关键:它说 使用他们的引用来制作我的引用,根本没有任何更改。 普通克隆不会那样做,因为一旦你 那样做,当你从另一个 Git 重新获取时,你会遇到问题:你已经完全被它奴役了;您在自己的副本中更改的任何引用,您用它们的值替换您的值,并且 return 成为纯副本。