最近有人向我介绍了 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 时,我得到:
在分支开发上您的分支是最新的“起源/开发”。
没有什么可提交的,工作树干净
谢谢,戴夫
这里问题的根源是git clone
\ 的选项也--depth
打开了。--single-branch
要在克隆时克服这个问题,请使用--no-single-branch
. 要随后击败它,请参阅How do I "undo" a --single-branch clone?的已接受答案。
请注意,在对您拥有的克隆进行取消单分支后,您将必须git fetch --depth 1
再次运行。这将从您克隆的存储库中检索其余的分支名称\xe2\x80\x94,所有这些名称都将成为远程跟踪名称;请参阅下面的详细信息\xe2\x80\x94,并允许您在每个此类名称上运行git checkout
以创建具有相同名称的本地分支。您还可以使用git remote set-branches --add
将个人名称添加到现有遥控器;同样,您将需要另一个git fetch --depth
.
Git 存储库\xe2\x80\x94 从技术上讲,非裸存储库\xe2\x80\x94 实际上由以下三个部分组成:
\n当你运行时git clone
,你让你的 Git 复制主数据库\xe2\x80\x94——保存所有提交和文件的数据库,比如\xe2\x80\x94或多或少批发,但让它读取另一个数据库,解析它并理解并将其写入到您的克隆中,另一个数据库。
该--depth
标志会影响主数据库,因此您不会批量复制它。--single-branch
正如我们所指出的,标志\xe2\x80\x94自动--depth
打开\xe2\x80\x94 会影响辅助数据库。在继续之前,让我们给这两个数据库命名,这样我们就不会一直引用一些尴尬的短语,比如“第一部分的一方”:
我一直称之为“主数据库”的是 Git 的对象存储。这是一个简单的键值数据库,其中键是哈希 ID,值是 Git 的提交和其他内部对象。1 通常这是 Git 存储库的最大部分。2
\n第二个数据库也是一个简单的键值存储,键是名称 \xe2\x80\x94 分支和标签名称,但也几乎所有 Git\ 的其他名称3 \xe2\x80\x94 和值是哈希身份证。每个名称仅存储一个哈希 ID,因为这就是所需的全部内容。
\n因此,回顾一下,git clone
\xe2\x80\x94without--single-branch
和--depth
flags \xe2\x80\x94 会调用其他一些Git并让它列出其所有分支、标签和其他名称。然后,它将使用这些名称来查找原始存储库中的所有提交和其他 Git 对象,并让其他 Git 发送所有这些对象。结果是对象数据库的完整副本。4 您现在拥有来自其他 Git 存储库的所有提交。
但与此同时,您自己的 Git 会获取它们的所有名称,并挑选要获取的名称以及如何处理它们。一般来说,您的 Git 会获取所有分支名称\xe2\x80\x94(其完整拼写为refs/heads/master
、refs/heads/topic
等)\xe2\x80\x94并将它们重命名为您自己的远程跟踪名称:refs/remotes/origin/master
、refs/remotes/origin/topic
等。然后,您的 Git 创建自己独立的名称到哈希 ID 数据库,其中没有分支名称。5
最终结果是,在这一步之后git clone
,您立即拥有所有提交,但没有任何分支! 不过,这种情况很快就通过最后一步得到了纠正git clone
。如果你没说--no-checkout
,最后一步git clone
就是运行git checkout
,这一步实际上创建了一个分支。Git 创建的分支名称是您随该选项提供的名称-b
。如果您没有提供-b
选项,您的 Git 会询问其他 Git 推荐哪个分支,如果其他方法都失败,您的 Git 会采用您自己的默认初始分支名称。6
1每个提交对象引用一个(单个)树对象,该对象保存该提交的快照并具有元数据。每个树对象都包含一个部分文件名\xe2\x80\x94name组件的数组,这些组件将根据需要\xe2\x80\x94和另一个哈希ID串在一起。该哈希 ID 标识另一棵树或存储某些文件内容的blob对象。Git 通过根据需要读取所有子树来构建文件的全名,并将完整文件名存储在其索引中,然后使用索引中看到的名称和 blob 哈希 ID 来提取文件。这不是完整的描述,但却是 Git 无法存储空目录的原因:没有办法将空目录放入 Git 的索引中。
\n对象数据库还可以包含带注释的标记对象,每个对象都保存一个哈希 ID,通常是提交的哈希 ID。这就是 Git 提供带注释标签的方式。
\n2但也有例外:旧存储库由于某种原因不断积累新名称,例如新分支和标记名称,但几乎从未获得任何新提交。但一般来说,对象数据库是使用大部分空间的地方,并且大部分时间用于初始克隆。
\n3其他名称包括注释、正在进行的二分、某些交互式变基期间所需的名称等。基本上,任何存储单个哈希 ID 的名称都会进入此数据库。不这样做的名称,例如像 之类的遥控器origin
名称,不要输入此处。这些通常位于目录config
中的文件中.git
。
该数据库目前实施得相当差。有时,名称在文件系统中存储为目录和文件名,这意味着在不区分大小写的文件系统(例如 Windows 和 macOS 系统上的默认文件系统)上,分支名称将不区分大小写。有时,名称存储在名为 的纯文本文件中,这使得它们都区分大小写,正如 Git 始终希望的那样。一些特殊名称,例如,根本不会进入 Packed-refs 文件,而是始终作为单独的文件存储在目录中。目前正在进行的工作是提供一个合适的数据库,以解决这里的一系列问题。packed-refs
HEAD
.git
4从技术上讲,结果可以而且通常会忽略使用名称无法找到的任何对象。不过,我们在这里将忽略这个细微的区别。
\n5您的 Git 通常也会省略所有非分支非标签名称。它如何处理它们的标签名称很复杂,但在正常的(非单分支,非深度限制)克隆中,您通常会复制它们的所有标签名称。
\n6这过去只是硬编码为master
,但现在变得可配置。
--single-branch
这有何影响使用该--single-branch
选项,您的 Git不会使用它们的所有名称。相反,您的 Git 仅使用您选项中的一个分支名称-b
,并具有相同的默认值:如果您不提供-b
,您的 Git 会询问他们的 Git 他们推荐什么,或者退回到另一个默认值。然后,您的 Git 将该一个分支名称转换为一个远程跟踪名称。它确保只向他们的 Git 询问该分支上、其他 Git 存储库中的提交。
最终结果是您获得一个远程跟踪名称以及所有提交的某个子集。最后git checkout
一步创建一个本地分支名称:与您的 Git 在选择要获取的提交子集时使用的名称相同。
--depth
这有何影响除了自动打开--single-branch
\xe2\x80\x94 之外,请注意,您可以使用\xe2\x80\x94将其关闭,所做的就是创建一个浅克隆。为了完全理解浅克隆,我们必须进入图论。(不过,我们不会在这里讨论太多。)--no-single-branch
--depth
在 Git 中,每个分支名称恰好标识一次提交。但是 Git\xe2\x80\x94 中的分支如果我们忽略“分支”到底是什么意思?(我们不应该忽略它,但我们会在这里)\xe2\x80\x94通常有一堆提交。这是如何运作的?
\n答案是 Git 中的每个提交都包含一些早期提交的哈希 ID 。在通常的简单情况下,我们最终会得到一长串提交,每个提交都向后指向一个较早的提交。该链中的最后一次提交是分支的尖端,或尖端提交。
\n让我们画一个简单的链,其中我们使用一个大写字母来代表每次提交的真实哈希 ID。哈希H
将是链中的最后一个,我们会说这是分支br1
:
... <-F <-G <-H <-- br1\n
Run Code Online (Sandbox Code Playgroud)\n该名称 br1
保存了最后一次提交的哈希 IDH
。这就是我们如何让 Git 从对象数据库中取出它(记住,这是一个简单的键值存储:哈希 ID 是键)。但在提交主体内部,Git 存储了先前提交H
的哈希 ID 。这样我们就可以得到\的ID,并让Git在键值存储中查找提交。同时commit有\的ID,所以我们可以从到向后走。G
H
G
G
G
F
G
F
这就是 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*
分支上的最后两次提交对于其分支来说是唯一的。
这种可达性思想是 Git 的核心。这也是--depth
工作原理。如果我们说--depth 1
,我们就是在告诉我们的 Git:当你从另一个 Git 获取提交时,只走一步。 如果我们在这里使用--depth 1
,我们会得到:
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 获取提交时,请执行两步操作。 这次我们得到:
I--J <-- br1\n /\n f--G--H <-- shared\n \\\n K--L <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n请注意,如果br2
有更多独特的提交,我们将不会有从br2
back 到 的连接shared
。
这里的小写提交字母表示 Git 知道有一个父级,但这些父级被标记为“故意丢失”。更准确地说,浅移植提交的哈希 ID保存在目录shallow
中调用的文件中.git
。Git 知道不要尝试从对象存储库加载这些提交,并且这不是它们丢失的错误。通常,这将是一个错误。
由于它们是故意丢失的,因此git log
不能也不会显示这些提交,并且浅嫁接的提交就好像根本没有父级一样。这在某种程度上具有误导性,但也是您应该期待的。在大多数情况下,它是无害的。
7这假设我们使用的名称是分支名称。如果我们使用标签名称,则这些是可从标签访问的提交;如果我们使用远程跟踪名称,则这些是可从远程跟踪名称访问的提交。由于所有名称都使用相同的系统,因此每个名称都提供了某种方式来达到某些提交集。
\ngit fetch
获得提交的操作当我们使用 时git clone
,我们实际上正在运行相当于六个命令的序列,其中五个是 Git 命令:
mkdir
,创建一个新的空目录/文件夹;git init
,在步骤1中创建的目录中创建一个新的空存储库;git remote add
,添加 nameorigin
或我们选择的其他名称,以及 URL 和fetch
配置\xe2\x80\x93,这是我们为克服单分支性而更改的配置;git config
,如果需要,添加git clone
命令中指定的配置选项;git fetch
,获取提交并为步骤 3 中选择的一个或多个分支创建远程跟踪名称;和git checkout
,创建一个本地分支名称并填写 Git\ 的索引和我们的工作树。该--depth
选项被传递到git fetch
第 5 步。因此,如果我们必须调整origin
远程配置,以取消克隆的单分支,因为第 3 步仅添加了具有一个特定分支的远程(请参阅文档)git remote
,我们必须运行新的git fetch
。这个新的git fetch
需要相同的--depth
选项。
--depth
打开git clone
Both 的选项--single-branch
,这限制了名称集\xe2\x80\x94,从而提交从其他 Git 存储库获得的\xe2\x80\x94,并将传递--depth
到 fetch 步骤,这限制了获得的提交图的深度来自其他 Git 存储库。在克隆时使用--no-single-branch
可以抑制名称限制,同时保持深度限制。如果您需要撤消名称限制,或者如果您用于git remote
更新受限制的分支名称集,则必须git fetch
再次运行。如果你希望它git fetch
有深度限制,你必须--depth
再次通过。
请注意,git fetch
确实尊重现有的浅移植点,因此在某些情况下,--depth
省略 是无害的。例如,如果您有一个存储库的单分支克隆,如下所示:
...--V--W--X <-- main\n \\\n Y--Z <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n并且您的单分支克隆的深度为 1 main
,因此该提交W
被标记为浅移植点:
w--X <-- main\n
Run Code Online (Sandbox Code Playgroud)\n然后添加topic
不带 a--depth
即可:
w--X <-- main\n \\\n Y--Z <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n也就是说,main
这次没有再深入。但如果图表是:
...--V--W--X <-- main\n \\\n Y--Z <-- topic\n
Run Code Online (Sandbox Code Playgroud)\ntopic
并且您在没有 new 的情况下添加和获取--depth
,您将得到:
...--V w--X <-- main\n \\\n Y--Z <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n在你的克隆中,这意味着你必须V
更早地提交和一切。请注意,提交W
仍然是标记和缺失的:因为它缺失了,你的Git 无法看到它w
会连接回V
,而你自己的 Git 会显示为:
X <-- main\n\n..--V--Y--Z <-- topic\n
Run Code Online (Sandbox Code Playgroud)\n\xe2\x80\x94 这并没有错,从技术上讲,它只是误导。
\n