当克隆完成后,使用 GIT 获取另一个分支 --深度 1

Dav*_*ave 5 git github

最近有人向我介绍了 git clone 的 --深度 1。显然这并没有获取所有历史记录并且速度要快得多。我用了:

git克隆--深度1-b开发https://github.MyCompany.com/CoolProduct/CoolProduct.git

这使我能够发挥、修改和分支开发分支。

但是,现在我想查看另一个分支“BillsFeature”,我尝试过: git checkout BillsFeature 并收到错误:pathspec 'BillsFeature' 与 git 已知的任何文件不匹配

这对我来说有一定道理。大概是因为我使用了--深度1,所以我没有拉下分支名称。我如何获得另一个分支? 我也不需要 BillsFeature 的历史记录。我应该说我尝试过: git fetch --depth 1 origin BillsFeature 似乎发生了一些事情。但是,当我执行 git status 时,我得到:

在分支开发上您的分支是最新的“起源/开发”。

没有什么可提交的,工作树干净

谢谢,戴夫

tor*_*rek 4

这里问题的根源是git clone\ 的选项也--depth打开了。--single-branch要在克隆时克服这个问题,请使用--no-single-branch. 要随后击败它,请参阅How do I "undo" a --single-branch clone?已接受答案。

\n

请注意,在对您拥有的克隆进行取消单分支后,您将必须git fetch --depth 1再次运行。这将从您克隆的存储库中检索其余的分支名称\xe2\x80\x94,所有这些名称都将成为远程跟踪名称;请参阅下面的详细信息\xe2\x80\x94,并允许您在每个此类名称上运行git checkout以创建具有相同名称的本地分支。您还可以使用git remote set-branches --add将个人名称添加到现有遥控器;同样,您将需要另一个git fetch --depth.

\n

可选阅读:详细信息,或者为什么上述方法有效

\n

Git 存储库\xe2\x80\x94 从技术上讲,非裸存储库\xe2\x80\x94 实际上由以下三个部分组成:

\n
    \n
  • 一对数据库,如下所述;
  • \n
  • 一个索引,Git 通过它知道要提交哪些文件,即要跟踪哪些文件,尽管索引不仅仅是文件列表;和
  • \n
  • 您可以在其中使用和修改文件的工作树或工作。这些文件实际上是您的,实际上根本不在 Git 中。Git 内部的文件,在主数据库中,都是只读的,并且是一种特殊的压缩和去重形式,只有 Git 本身可以使用。
  • \n
\n

当你运行时git clone,你让你的 Git 复制主数据库\xe2\x80\x94——保存所有提交和文件的数据库,比如\xe2\x80\x94或多或少批发,但让它读取另一个数据库,解析它并理解并将其写入到您的克隆中,另一个数据库。

\n

--depth标志会影响主数据库,因此您不会批量复制它。--single-branch正如我们所指出的,标志\xe2\x80\x94自动--depth打开\xe2\x80\x94 会影响辅助数据库。在继续之前,让我们给这两个数据库命名,这样我们就不会一直引用一些尴尬的短语,比如“第一部分的一方”

\n
    \n
  • 我一直称之为“主数据库”的是 Git 的对象存储。这是一个简单的键值数据库,其中键是哈希 ID,值是 Git 的提交和其他内部对象。1 通常这是 Git 存储库的最大部分。2

    \n
  • \n
  • 第二个数据库也是一个简单的键值存储,键是名称 \xe2\x80\x94 分支和标签名称,但也几乎所有 Git\ 的其他名称3 \xe2\x80\x94 和值是哈希身份证。每个名称仅存储一个哈希 ID,因为这就是所需的全部内容。

    \n
  • \n
\n

因此,回顾一下,git clone\xe2\x80\x94without--single-branch--depthflags \xe2\x80\x94 会调用其他一些Git并让它列出其所有分支、标签和其他名称。然后,它将使用这些名称来查找原始存储库中的所有提交和其他 Git 对象,并让其他 Git 发送所有这些对象。结果是对象数据库的完整副本。4 您现在拥有来自其他 Git 存储库的所有提交。

\n

但与此同时,您自己的 Git 会获取它们的所有名称,并挑选要获取的名称以及如何处理它们。一般来说,您的 Git 会获取所有分支名称\xe2\x80\x94(其完整拼写为refs/heads/masterrefs/heads/topic等)\xe2\x80\x94并将它们重命名为您自己的远程跟踪名称:refs/remotes/origin/masterrefs/remotes/origin/topic等。然后,您的 Git 创建自己独立的名称到哈希 ID 数据库,其中没有分支名称5

\n

最终结果是,在这一步之后git clone,您立即拥有所有提交,但没有任何分支! 不过,这种情况很快就通过最后一步得到了纠正git clone。如果你没说--no-checkout,最后一步git clone就是运行git checkout,这一步实际上创建了一个分支。Git 创建的分支名称是您随该选项提供的名称-b。如果您没有提供-b选项,您的 Git 会询问其他 Git 推荐哪个分支如果其他方法都失败,您的 Git 会采用您自己的默认初始分支名称。6

\n
\n

1每个提交对象引用一个(单个)对象,该对象保存该提交的快照并具有元数据。每个树对象都包含一个部分文件名\xe2\x80\x94name组件的数组,这些组件将根据需要\xe2\x80\x94和另一个哈希ID串在一起。该哈希 ID 标识另一棵树或存储某些文件内容的blob对象。Git 通过根据需要读取所有子树来构建文件的全名,并将完整文件名存储在其索引中,然后使用索引中看到的名称和 blob 哈希 ID 来提取文件。这不是完整的描述,但却是 Git 无法存储空目录的原因:没有办法将空目录放入 Git 的索引中。

\n

对象数据库还可以包含带注释的标记对象,每个对象都保存一个哈希 ID,通常是提交的哈希 ID。这就是 Git 提供带注释标签的方式。

\n

2但也有例外:旧存储库由于某种原因不断积累新名称,例如新分支和标记名称,但几乎从未获得任何新提交。但一般来说,对象数据库是使用大部分空间的地方,并且大部分时间用于初始克隆。

\n

3其他名称包括注释、正在进行的二分、某些交互式变基期间所需的名称等。基本上,任何存储单个哈希 ID 的名称都会进入此数据库。不这样做的名称,例如像 之类的遥控器origin名称,不要输入此处。这些通常位于目录config中的文件中.git

\n

该数据库目前实施得相当差。有时,名称在文件系统中存储目录和文件名,这意味着在不区分大小写的文件系统(例如 Windows 和 macOS 系统上的默认文件系统)上,分支名称将不区分大小写。有时,名称存储在名为 的纯文本文件中,这使得它们都区分大小写,正如 Git 始终希望的那样。一些特殊名称,例如,根本不会进入 Packed-refs 文件,而是始终作为单独的文件存储在目录中。目前正在进行的工作是提供一个合适的数据库,以解决这里的一系列问题。packed-refsHEAD.git

\n

4从技术上讲,结果可以而且通常会忽略使用名称无法找到的任何对象。不过,我们在这里将忽略这个细微的区别。

\n

5您的 Git 通常也会省略所有非分支非标签名称。它如何处理它们的标签名称很复杂,但在正常的(非单分支,非深度限制)克隆中,您通常会复制它们的所有标签名称。

\n

6这过去只是硬编码为master,但现在变得可配置。

\n
\n

--single-branch这有何影响

\n

使用该--single-branch选项,您的 Git不会使用它们的所有名称。相反,您的 Git 仅使用您选项中的一个分支名称-b,并具有相同的默认值:如果您不提供-b,您的 Git 会询问他们的 Git 他们推荐什么,或者退回到另一个默认值。然后,您的 Git 将该一个分支名称转换为一个远程跟踪名称。它确保只向他们的 Git 询问该分支上、其他 Git 存储库中的提交。

\n

最终结果是您获得一个远程跟踪名称以及所有提交的某个子集。最后git checkout一步创建一个本地分支名称:与您的 Git 在选择要获取的提交子集时使用的名称相同。

\n

--depth这有何影响

\n

除了自动打开--single-branch\xe2\x80\x94 之外,请注意,您可以使用\xe2\x80\x94将其关闭,所做的就是创建一个克隆。为了完全理解浅克隆,我们必须进入图论。(不过,我们不会在这里讨论太多。)--no-single-branch--depth

\n

在 Git 中,每个分支名称恰好标识一次提交。但是 Git\xe2\x80\x94 中的分支如果我们忽略“分支”到底是什么意思?(我们不应该忽略它,但我们会在这里)\xe2\x80\x94通常有一堆提交。这是如何运作的?

\n

答案是 Git 中的每个提交都包含一些早期提交的哈希 ID 。在通常的简单情况下,我们最终会得到一长串提交,每个提交都向后指向一个较早的提交。该链中的最后一次提交是分支的尖端,或尖端提交

\n

让我们画一个简单的链,其中我们使用一个大写字母来代表每次提交的真实哈希 ID。哈希H将是链中的最后一个,我们会说这是分支br1

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

名称 br1保存了最后一次提交的哈希 IDH。这就是我们如何让 Git 从对象数据库中取出它(记住,这是一个简单的键值存储:哈希 ID 是键)。但在提交主体内部,Git 存储了先前提交H的哈希 ID 。这样我们就可以得到\的ID,并让Git在键值存储中查找提交。同时commit有\的ID,所以我们可以从到向后走。GHGGGFGF

\n

这就是 Git 的工作原理:向后。名称(例如分支名称、标签名称或远程跟踪名称)存储一个哈希 ID。这就是我们想要的提交,然后,如果我们想要所有提交,Git 就会从该提交向后走到上一个提交,然后继续走。这个名字让我们开始;提交本身提供了路径的其余部分。

\n

我们遍历的路径,以及我们在走这条路径时收集的所有提交,都是该分支上可到达的提交。7 当两个分支分歧时,它们有一些共同的顺序:

\n
             I--J   <-- br1\n            /\n...--F--G--H   <-- shared\n            \\\n             K--L   <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,向上提交H是在所有三个分支上,并且每个br*分支上的最后两次提交对于其分支来说是唯一的。

\n

这种可达性思想是 Git 的核心。这也是--depth工作原理。如果我们说--depth 1,我们就是在告诉我们的 Git:当你从另一个 Git 获取提交时,只走一步。 如果我们在这里使用--depth 1,我们会得到:

\n
             i--J   <-- br1\n\n        g--H   <-- shared\n\n             j--L   <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n

如果我们使用--depth 2,我们会告诉我们的 Git:当您从其他 Git 获取提交时,请执行两步操作。 这次我们得到:

\n
             I--J   <-- br1\n            /\n     f--G--H   <-- shared\n            \\\n             K--L   <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,如果br2有更多独特的提交,我们将不会有从br2back 到 的连接shared

\n

这里的小写提交字母表示 Git 知道有一个父级,但这些父级被标记为“故意丢失”。更准确地说,浅移植提交的哈希 ID保存在目录shallow中调用的文件中.git。Git 知道不要尝试从对象存储库加载这些提交,并且这不是它们丢失的错误。通常,这将是一个错误。

\n

由于它们是故意丢失的,因此git log不能也不会显示这些提交,并且浅嫁接的提交就好像根本没有父级一样。这在某种程度上具有误导性,但也是您应该期待的。在大多数情况下,它是无害的。

\n
\n

7这假设我们使用的名称是分支名称。如果我们使用标签名称,则这些是可从标签访问的提交;如果我们使用远程跟踪名称,则这些是可从远程跟踪名称访问的提交。由于所有名称都使用相同的系统,因此每个名称都提供了某种方式来达到某些提交集。

\n
\n

这是git fetch获得提交的操作

\n

当我们使用 时git clone,我们实际上正在运行相当于六个命令的序列,其中五个是 Git 命令:

\n
    \n
  1. mkdir,创建一个新的空目录/文件夹;
  2. \n
  3. git init,在步骤1中创建的目录中创建一个新的空存储库;
  4. \n
  5. git remote add,添加 nameorigin或我们选择的其他名称,以及 URL 和fetch配置\xe2\x80\x93,这是我们为克服单分支性而更改的配置;
  6. \n
  7. git config,如果需要,添加git clone命令中指定的配置选项;
  8. \n
  9. git fetch,获取提交并为步骤 3 中选择的一个或多个分支创建远程跟踪名称;和
  10. \n
  11. git checkout,创建一个本地分支名称并填写 Git\ 的索引和我们的工作树。
  12. \n
\n

--depth选项被传递到git fetch第 5 步。因此,如果我们必须调整origin远程配置,以取消克隆的单分支,因为第 3 步仅添加了具有一个特定分支的远程(请参阅文档git remote,我们必须运行新的git fetch。这个新的git fetch需要相同的--depth选项。

\n

结论

\n

--depth打开git cloneBoth 的选项--single-branch,这限制了名称集\xe2\x80\x94,从而提交从其他 Git 存储库获得的\xe2\x80\x94,并将传递--depth到 fetch 步骤,这限制了获得的提交图的深度来自其他 Git 存储库。在克隆时使用--no-single-branch可以抑制名称限制,同时保持深度限制。如果您需要撤消名称限制,或者如果您用于git remote更新受限制的分支名称集,则必须git fetch再次运行。如果你希望它git fetch有深度限制,你必须--depth再次通过。

\n

请注意,git fetch 确实尊重现有的浅移植点,因此在某些情况下,--depth省略 是无害的。例如,如果您有一个存储库的单分支克隆,如下所示:

\n
...--V--W--X   <-- main\n            \\\n             Y--Z   <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n

并且您的单分支克隆的深度为 1 main,因此该提交W被标记为浅移植点:

\n
        w--X   <-- main\n
Run Code Online (Sandbox Code Playgroud)\n

然后添加topic不带 a--depth即可:

\n
        w--X   <-- main\n            \\\n             Y--Z   <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n

也就是说,main这次没有再深入。但如果图表是:

\n
...--V--W--X   <-- main\n      \\\n       Y--Z   <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n

topic并且您在没有 new 的情况下添加和获取--depth,您将得到:

\n
...--V  w--X   <-- main\n      \\\n       Y--Z   <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n

在你的克隆中,这意味着你必须V更早地提交和一切。请注意,提交W仍然是标记和缺失的:因为它缺失了,你的Git 无法看到它w会连接回V,而你自己的 Git 会显示为:

\n
           X   <-- main\n\n..--V--Y--Z   <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x94 这并没有,从技术上讲,它只是误导。

\n