在探索Git内部工作的过程中,我进入了我的git项目的refs / remotes / origin目录并运行了ls
命令。这就是我所看到的。
$ ls
HEAD sp2013dev
然后我跑了cat HEAD
并在这里打印了。
$ cat HEAD
ref: refs/remotes/origin/master
但是,refs / remotes / origin目录中没有master名称的文件或目录。这个目录只有' HEAD'和' sp2013dev'
我在这里遗漏了什么吗?为什么HEAD指的是某些东西(顺便说一句,是'参考'这个正确的术语'某些东西'?)哪些不存在?
答案 0 :(得分:7)
只是为了强调:你在git的内部徘徊;您找到了一个名为.git/refs/remotes/origin
的目录,它包含两个文件HEAD
和sp2013dev
。 .git/refs/remotes/origin/HEAD
的内容为ref: refs/remotes/origin/master
。
由文字字符串ref:
后跟另一个引用名称组成的引用是"符号"参考,用git术语。这意味着"虽然这是一个有效的名称,但是通过阅读另一个引用来找到该引用所解析的SHA-1。"但是,正如您所注意到的那样,master
目录中没有名为.git/refs/remotes/origin
的文件,因此您想知道这是如何工作的。
答案是并非所有引用都必须在文件中。引用可以是,并且是"打包"。目前,打包引用可以在.git/packed-refs
中找到,它是一个纯文本文件。您自己的Git将refs/remotes/origin/master
与{1}}中的SHA-1哈希配对。
注意:引用可能出现在.git/packed-refs
文件和 packed-refs
子目录中的文件中。在这种情况下,第二个版本会覆盖第一个版本。这允许.git/refs
(由git pack-refs
调用)打包所有引用,然后让引用变为"解压缩"根据需要,当他们更新时。 (但这是一个实现细节,你不应该假设这个;在脚本中,使用git gc
和git update-ref
来读取和更新引用,并让这些程序执行更新规则。)
目前,似乎没有符号引用的打包格式,因此所有这些都存在于"真实文件"现在。
当您克隆另一个存储库时 - 或者就此而言,只要您运行git symbolic-ref
或git fetch
,但是那些不处理git push
- 就会有两个< / em>涉及的存储库,两个规则由两个控制这两个存储库的不同Git强制执行。除了命令refs/remotes/origin/HEAD
之外,它只是git remote set-head
首先创建git clone
,因此我们可以专注于origin/HEAD
本身。
由于有两个存储库,让我们给它们命名。我们可以在git clone
调用您的本地存储库 L 和存储库,您正在进行克隆, O 。由于 O 是 Git存储库,因此它有自己的origin
。这个HEAD
通常是一个符号引用:例如HEAD
。但是,它所命名的分支是 O 的本地:它是HEAD -> master
,它是 O 上的本地分支。如果您要登录计算机托管存储库 O ,并查看其refs/heads/branch
文件,则它将包含.git/HEAD
。 O 上的Git正在执行此规则,ref: refs/heads/branch
始终将本地(本地到 O )分支命名。
现在,您自己的Git(在您的系统上本地运行)正在创建存储库 L 。 L 上的Git想要在HEAD
上创建一个全名为L
的远程跟踪分支。这将是一个象征性的参考,它将指向refs/remotes/origin/HEAD
。但有两个并发症:
refs/remotes/origin/branch
。在我们的案例中,它是HEAD
。refs/heads/branch
作为远程跟踪分支。 (您的Git会将其存储在refs/remotes/origin/branch
文件中。)然后,您的Git会创建 L .git/packed-refs
作为refs/remotes/origin/HEAD
的符号引用。< / LI>
醇>
在这两点中的任何一点都可能出错。例如,如果您使用refs/remotes/origin/branch
,则步骤2会失败。你告诉你的Git, L 根本不应该有git clone -b otherbranch --single-branch
,而只有refs/remotes/origin/branch
。但是如果第2步失败了,那么你的Git根本就不会创建refs/remotes/origin/otherbranch
。这样你就不会在 L 中找到指向不存在的远程跟踪分支的refs/remotes/origin/HEAD
。
步骤1&#34;失败&#34; (在某种程度上)如果您的Git,构建 L 或他们的Git,为您的Git提供 O ,则太旧了。在这些互联网连接期间,有一个用于查找参考名称的已定义协议&#34;电话呼叫&#34;该副本提交(refs/remotes/origin/HEAD
,git fetch
)并查看分支和标记名称(git push
)等。 git ls-remote
命令使用相同的定义协议。在连接的早期,您的Git和他们的Git协商使用的协议选项。较旧的Gits没有表达和解析符号引用的选项,因此如果Git不支持该选项, O 只声称 O &#39; s {{1是一些特殊的SHA-1 ID。构建克隆 L 的Git必须猜测 O 上的哪个分支实际上是clone
。它是通过扫描 O 获取的分支名称的 rest 来实现的。如果 O 的HEAD
是HEAD
,并且还有一个分支也是HEAD
,那么,那一定是他们的1234567
点!
这可能错误,即您的Git可能猜错了。但如果是这样,那么你的Git就会采用错误的假设继续担心第2步。
如果他们( O &#39; s)1234567
被分离,步骤1可能会完全失败。在这种情况下,可能没有匹配的分支。如果是这样,你的Git不会猜错,它只会知道在存储库 O 中没有检出分支。如果你的Git和他们的Git都没有太老,你的Git会协商正确的协议选项,你的Git会确定 O 是否有一个分离的HEAD,如果没有,那么检查哪个分支在 O 。
很容易让第2步故意失败:只使用HEAD
克隆某些知识库HEAD
引用某个特定分支(例如HEAD
)的存储库和master
使您的克隆 L 避免该分支。让步骤1故意失败有点困难,但你可以通过登录导出 O 并分离 O 的系统来做到这一点。 HEAD
如果您这样做,然后在您自己的系统上使用--single-branch
来制作 L ,您只需找到您的克隆 L 否 -b
。这维持了正常的规则,其中包括这三个(如果还有更多,我不确定副手):
git clone
,通常(但不总是)符号,仅包含对本地分支的引用,即origin/HEAD
名称中的名称-space。特殊情况例外是特殊引用名称HEAD
可能包含尚未实际存在的本地分支的名称。这个(称之为&#34;未出生的分支&#34;或者#34;孤儿分支&#34;)是refs/heads/
在一个新的空库中出现的方式:HEAD
即使master
尚不存在,也指向HEAD
。在此状态下进行的提交会导致分支开始存在,指向刚才提交的提交;刚刚提交的提交有 no 父项,即是root提交。
您可能认为可以使用master
破坏这些规则,但事实上,它不会让您:
使用
master
设置symbolic-refgit remote set-head
明确。例如,&#34; git remote set-head origin master&#34;将设置 symbolic-ref<branch>
torefs/remotes/<name>/HEAD
。 这只适用于refs/remotes/origin/HEAD
已经存在;如果不是必须的话 首先取得。
(强调我的)。有关refs/remotes/origin/master
的更多信息,请参阅the git remote
documentation。
您可以使用直接访问refs/remotes/origin/master
目录或使用git remote set-head
来破坏这些规则。显然,如果你在.git
里面徘徊,你可以mess things up pretty good,:-)所以这需要小心。 git symbolic-ref
的尖锐边缘有点令人惊讶,但现在你知道你也一定要小心。