如何知道工作目录指的是git中的哪个分支?

How to know working directory refers to which branch in git?

根据一些研究,我发现 git 在两个地方保留了两个版本的代码:

首先,我的理解可以吗?

然后我需要知道 headworking directory 都指的是哪个分支。有两个命令:

  1. cat .git/head

  2. git branch

你能告诉我这两个命令指的是哪一个吗? (head 中的分支或 working directory 中的分支)?

当你运行git status时,你的更改是否会与headworking directory中的版本进行比较?

不,你的理解不对:.git/refs/中的实体是references,而不是work-trees .

(恐怕这会持续很长时间,因为你的问题有点不集中。)

术语定义

Git 曾经将术语 工作目录 与术语 work-tree 或 [=143 互换使用=]工作树。由于与工作目录的 operating-system 定义混淆,这在几年前开始发生变化,您在其中 cd <em>path</em> 和 运行 pwd 打印您的工作目录。我会坚持使用 work-tree.

这个词

默认情况下,Git 存储库带有一 (1) 个 work-tree。 work-tree 位于与包含存储库的 .git sub-directory 相同的目录中,因此如果您有一个 .git 目录,那么您位于该特定 Git work-tree.

Git 存储库的主要功能(至少就 Git 本身而言)是保存 提交 。提交是一组文件的完整快照。提交中存储的文件据说是 versioned,并使用 git checkout <em>commit-specifier</em> 提取提交将提交中所有文件的特定版本提取到 work-tree 中。以这种方式存储在提交中的文件被称为 version-controlled。您签出的提交(从存储库提取到 work-tree)是 当前提交.

请注意,存储在提交中的文件采用特殊的压缩(通常高度压缩)read-only 格式。它们无法更改。一旦 Git 将某些内容作为 Git-format 对象 1 存储在存储库数据库中,该内容将 永远不会 更改。它可以被复制——提取成文本格式,以某种方式进行操作,然后存储为一个新的不同版本——但每个 Git 对象都由其 apparently-random 哈希 ID 记录,这实际上是一个密码其数据的校验和。通过更改内部 Git 对象的某些内容并再次存储它,您只需创建一个 new 对象,并使用新的和不同的哈希 ID。2

Git 允许 work-tree 包含 而非 version-controlled 的其他文件。在Git的术语中,此类文件是未跟踪;通过逻辑反转,version-controlled 个文件必须 被跟踪 。未跟踪的文件也可以忽略(跟踪的文件不能忽略)。

A Git 存储库还附带一 (1) 个 index。 Git 的索引是一个关键数据结构,您在使用 Git 和 work-tree 时必须始终注意这一点。这个索引是如此重要,或者最初命名如此糟糕,以至于它有两个额外的名称:它也被称为 staging area,有时也被称为 cache.它最初被称为索引的原因是,在内部,它索引(或缓存)work-tree。不过,这不是索引的 important-to-you 方面。

索引为做的主要事情是保存每个文件的版本。也就是说,当您将当前提交提取到 work-tree 时,Git 真正做的第一件事是从 提交中复制每个文件 进入索引。 (这个副本快得离谱而且 light-weight 因为索引以相同的压缩 Git-only 格式保存文件,在这种情况下实际上 shares 底层 Git 对象。)一旦文件在索引中,Git 就可以并且确实将其提取到 work-tree 中,将其扩展为正常格式,以便 non-Git 程序——而你自己—可以查看and/or修改。

文件的索引副本与同一文件的当前提交副本之间的主要区别在于索引版本可以被覆盖。此外,文件 索引中的事实是 Git 决定文件 被跟踪 的方式。这为我们提供了跟踪文件的真实定义:当且仅当文件存在于索引中时,该文件才被跟踪。3

Git 有多个不同的东西叫做 head:b运行ch heads,它们是我认为只是叫 b运行ch names 更恰当,但是 Git 可以互换使用这些术语,并且有一个特殊的区别 HEAD,应该是像这样输入 all-capitals。它实际上存储在一个名为 .git/HEAD 的文件中。 (单个)HEAD 始终是 当前提交 的符号名称。 .git/refs/heads/ 中的项目包括(但不一定全部)b运行ch 名称,.git/refs/remotes/ 中的项目包括(但不一定全部)Git 调用的内容, 各种, remote-tracking b运行ch names, remote-tracking运行ches,以及其他几个误导性术语。我更愿意称这些为 remote-tracking 名称。稍后我们将对此进行更多介绍,但现在请记住,这些中的每一个都是 Git 称为 reference.[=75= 的实例]


1Git 在主存储库数据库中有四种类型的对象:committree blob 注释标签 。您通常不需要关心任何事情,但 commit 在这里。数据库本身只是一个 key-value 存储,键是哈希 ID,值是对象的数据。 Git 可以(并且确实)在通过密钥提取对象时检测到对象损坏,因为数据的加密哈希 必须 与密钥匹配。

2请注意,每个提交都有自己独特的唯一哈希 ID。对于文件则不然:如果一个文件的内容在多次提交中相同,则 Git 中的文件内部对象在所有这些提交中具有 相同的 哈希 ID。这是因为散列 ID 是 内容 的散列。每个提交在某种程度上都是独一无二的——例如,它们都有时间戳,所以只要您的计算机时钟正常并且您每秒提交的次数不超过一次,每个提交都有一个唯一的时间戳。实际上,有两个个时间戳,一个是"author",一个是"committer",但我们还不用担心这个。

3当您 运行 git commit、Git 使用任何方式提交 new在索引/staging-area那一刻。因为索引中的文件已经是 Git-only 格式,所以提交非常快。在一个包含数万个文件的大型项目中,re-compressing 每个跟踪的 work-tree 文件都需要很长时间(有时很多秒),但是使用索引中的 already-compressed 数据只需要几秒钟毫秒。这就是为什么索引存在;并且鉴于索引存在并且 git commit 只是将其打包,这就是为什么文件 索引中的存在使得文件被跟踪的原因。


B运行ch 名称、参考资料及其意义 "on a branch"

正如我们刚才看到的,b运行ch name 是一种特定的 reference。 Git 引用恰好包含一 (1) 个哈希 ID。这适用于 b运行ch 名称、remote-tracking 名称和所有其他类型的引用(标签名称、来自 git replace 的替换条目等)。

B运行ch 名称只是全名以 refs/heads/ 开头的引用。这就是 为什么 其中一些可以在 .git/refs/heads 中找到。但是,b运行ch 名称 intended 为 case-sensitive:b运行ch xyzzy 不同于 b运行ch XyzzyxyZZy 不同,依此类推。这目前在 Windows 和折叠大小写的 MacOS 系统中部分损坏,但有时引用存储在文件中 (.git/packed-refs) 然后它在 Windows 和 MacOS 上工作。4 将来,引用可能会存储在真实的数据库中(与存储库对象数据库相邻),这将使它们 case-sensitive 并且无法通过在 .git/refs/heads/.

Remote-tracking 名称是全名以 refs/remotes/ 开头的引用。他们的名字继续包含一个元素,通常是 origin/。您的 Git 使用 remote-tracking 名称来记住 b运行ch 名称,以及它们的单个对应哈希 ID,您的 Git 在其他 Git 存储库中找到了这些名称。虽然它们实际上是 您的 名称,您可以随意更改它们,但最好让您的 Git 自动更新它们以匹配 b运行ch 存储在其他 Git.

中的名称

请注意,Git 通常会去掉 refs/<em>whatever</em>/ 部分,因此 b运行全名为 refs/heads/master 的 ch name 只是 referred-to 和 master。这对下一步至关重要。

在Git的术语中,你可以on a b运行ch或者detached HEAD.当你 运行 git checkout <em>b运行chname</em>, Git 填充索引和work-tree 来自哈希 ID 存储在 b运行ch 名称 branchname 中的提交。它还copy b运行ch name HEAD中,因此.git/HEAD包含文字串ref: refs/heads/<em>b运行chname</em>。此时 Git 将声称您在那个 b运行ch 上。另一方面,如果您 运行 git checkout <em>hash-id</em>, Git 填充索引和您提供的哈希 ID 提交中的 work-tree——这需要是有效的、现有的提交——然后将哈希 ID 本身写入 .git/HEAD。此时 Git 将声称您有一个分离的 HEAD。

因此,如果您的 HEAD 文件包含ns 一个原始哈希 ID——分离——Git 可以读取 .git/HEAD 以获取当前提交的哈希 ID。如果你的HEAD是附加的,Git可以读取.git/HEAD得到b运行ch name,然后读取b运行 ch name——可能存储也可能不存储在.git/refs/heads/<em>name</em>中,但肯定存储某处——获取当前的提交哈希 ID。无论哪种方式,Git 都可以使用您的 HEAD 找到当前提交。

每当您进行 new 提交时,Git:

  • 创建提交对象。 Git 基本上冻结索引中的所有文件版本以用作快照。它用这些文件构建一个新的提交,你是作者和提交者,"now" 作为时间戳。它使用您的提交消息作为提交日志。而且,至关重要的是,它使用 当前提交 ID 作为新提交的 parent 提交。

  • 现在提交已经存在并且有自己唯一的哈希 ID,Git 更新 current b运行ch,如果您的 HEAD 已连接。也就是说,如果 .git/HEAD 中有一个 b运行ch 名称,Git 会用新名称覆盖 b运行ch 名称的存储哈希 ID。如果您的 HEAD 已分离,Git 会用新的哈希 ID 覆盖 .git/HEAD。无论哪种方式,HEAD 继续命名 当前提交 ,因为新提交现在是当前提交。

请注意,因为新提交恰好包含索引 / staging-area 中的那些文件,一旦您提交,索引和 HEAD 提交匹配,就像您第一次提交时一样运行 git checkout 检查 之前的 提交。 work-tree根本不进这张图!


4因为 Windows 和 MacOS 不能有两个不同的文件命名为 MASTERmaster,例如,将 b 运行ch 名称仅大小写不同。出于同样的原因,提交包含名称仅大小写不同的文件是一个坏主意——例如,在旧的 Linux 内核中就是如此。当你签出这样的提交时,你的索引 / staging-area 得到两个文件,例如 README.TXTreadme.txt,但是你的 work-tree 只能保存 一个 个,使用 Git.

变得太难了

关于你的问题

Then I need to know both head and working directory are referring to which branch.

There are two commands:

  1. cat .git/head
  2. git branch

正如我上面提到的,文件 .git/HEAD 包含 b运行ch 名称(如果您的 HEAD 已附加)或原始提交哈希(如果您的 HEAD 已分离)。所以 cat .git/HEAD——你应该全部使用大写字母,这样才能在其他系统上工作——如果你在 b运行ch 上,它会告诉你你在哪个 b运行ch 上。

默认情况下,git branch 命令会列出您的 b运行ch 名称——所有 .git/refs/heads/ 文件加上存储在别处的任何 b运行ch 名称——并添加如果您在 b运行ch 上,则在 .git/HEAD 中的那个前面添加前缀 *。如果您有一个分离的 HEAD,git branch 将在其输出列表中包含字符串 * HEAD detached at ...* HEAD detach from ...。确切的细节因 Git 的一个版本而异。

还有几个命令旨在编写使用 Git 的代码:git symbolic-ref 将读取附加了 HEAD 的 b运行ch 名称,并打印它,或者如果 HEAD 分离则简单地失败;如果你在 b运行ch 上,git rev-parse --symbolic-full-name HEAD 将打印全名,例如 refs/heads/master,如果 HEAD 已分离,则只打印 HEAD。使用 git rev-parse --abbrev-ref HEAD 你可以获得 b运行ch 的短名称(refs/heads/ 剥离),或者再次 HEAD 如果 HEAD 分离。

And when you run git status, will your changes be compared with the version which is in the head or working directory?

无法按照提问的方式回答这个特定问题。 git status 所做的是 运行 两次 比较——两次 git diff --name-status 或多或少:

  • HEAD 提交和索引之间有什么不同(如果有的话)?
  • 索引和 work-tree 之间有什么不同?

第一个 diff 的结果是 准备提交的更改——如果您现在提交,使用当前索引,新快照将不同于旧快照。第二个 diff 的结果是 not 暂存提交的更改。您可以使用 git add 将 work-tree 文件复制到索引文件上,以便索引版本与 work-tree 版本匹配。

请记住,索引中的任何内容实际上都是提议的提交。更新每个文件的索引/staging-area 副本会更改您接下来要提交的内容。