一个仓库的全部数据是否总是在
.git
目录(或裸仓库)中以某种格式存储,能够随时呈现所有文件?
是的,这些文件及其完整历史记录都存储在
.git/packed-refs
和
.git/refs
以及
.git/objects 中。
当您克隆一个仓库(裸或非裸)时,您始终会有带有其 Git 管理和控制文件的
.git
文件夹(或按命名约定带有
.git
扩展名的文件夹,用于裸仓库)。(
请参阅术语表)
Git 可以使用 git unpack-objects 随时解包它所需的内容。
诀窍是:
从一个裸仓库,你可以查询日志(在 git 裸仓库中使用
git log
就可以了:不需要工作树),或者
列出裸仓库中的文件。
或者
显示裸仓库中文件的内容。
这就是 GitHub 如何能够呈现一个页面的文件,而无需检出整个仓库。
我不知道 GitHub 是否确切地这样做,因为大量的仓库数量迫使
GitHub 工程团队进行各种优化。
例如,请参见
他们如何优化克隆/获取仓库。
使用
DGit,这些裸仓库实际上被复制到多个服务器上。
这是裸仓库的原因,而工作副本只有给定时间的文件吗?
对于GitHub来说,维护一个工作树需要太多的磁盘空间,并且在更新时(当每个用户请求不同的分支时)成本太高。最好从唯一的裸仓库中提取所需内容以呈现页面。
通常情况下(在GitHub的限制之外),裸仓库用于推送,以避免工作树与刚刚推送的内容不同步。请参见“但我为什么需要一个裸仓库?”以获取具体示例。
话虽如此:
但对于GitHub来说,这是不可能的,因为它无法为存储的每个仓库维护一个(或服务器)工作树。
文章 "使用裸的 Git 仓库来进行我的 dot files 的版本控制" 来自 Greg Owen ,原作者是 报道人员 aifusenno1,补充如下:
裸仓库是一个没有快照的 Git 仓库。
它只存储历史记录。它还以稍微不同的方式存储历史记录(直接在项目根目录),但这并不那么重要。
裸仓库仍然会存储您的文件(请记住,历史记录有足够的数据来重建任何提交时的文件状态)。
您甚至可以从裸仓库创建非裸仓库:如果您从一个裸仓库进行 git clone
,Git 将自动为您在新仓库中创建一个快照(如果您想要一个裸仓库,请使用 git clone --bare
)。
Greg 补充道:
为什么我们要使用裸的Git存储库?
几乎所有我找到的关于裸存储库的解释都提到了它们用于集中存储想要在多个用户之间共享的存储库。
请参见
Git存储库布局:
一个
<project>.git
目录,它是一个裸存储库(即没有自己的工作树),通常用于通过将其推送和从中获取来与他人交换历史记录。
基本上,如果你想要编写自己的 GitHub/GitLab/BitBucket,你的集中式服务将把每个 repo 存储为裸仓库。
但是为什么呢?没有快照如何与共享相连呢?
答案是,如果唯一与你的 repo 交互的服务是 Git,那么就不需要快照了。
基本上,快照只是为人类和非 Git 工具提供方便,但 Git 只与历史记录交互。你的集中式 Git 托管服务只会通过 Git 命令与 repos 进行交互,所以为什么要一直生成快照呢?
快照只会占用额外的空间而没有任何好处。
GitHub 在访问该页面时即时生成快照,而不是永久存储在 repo 中(这意味着 GitHub 只需要在你请求时生成快照,而不是每次有人推送更改时都更新一个)。
从2022年第三季度开始,Git 2.38引入了一个名为
safe.bareRepository
的配置变量,允许用户禁止发现裸仓库。
请查看提交 8d1a744, 提交 6061601, 提交 5b3c650, 提交 779ea93, 提交 5f5af37 (2022年7月14日) 由Glen Choo (chooglen
)提交。
(于提交 18bbc79中被Junio C Hamano -- gitster
--合并,日期为2022年7月22日)
setup.c
:创建safe.bareRepository
签名作者:Glen Choo
已知一种社会工程攻击利用了现有的工作树可能包括整个裸库,包括配置文件这一事实。用户可能在裸库内运行Git命令,认为将使用“外部”存储库的配置文件,但实际上使用的是裸库的配置文件(由攻击者控制),这可能导致任意代码执行。详细描述和深入讨论请参见
this thread。
一个简单的缓解方法是禁止裸库,除非通过
--git-dir
或
GIT_DIR
指定。在不使用裸库的环境下,这将带来最小的干扰。
创建一个配置变量
safe.bareRepository
,告诉Git是否在使用裸库时
die()
。此配置是一个枚举:
- “all”:允许所有裸库(默认值)。
- “explicit”:仅允许通过
--git-dir
或
GIT_DIR
指定的裸库。
如果我们想默认保护用户免受此类攻击,则两个值都不足够,“all”提供不了保护,但“explicit”对于裸库用户来说不切实际。更可用的默认值将仅允许非嵌入式裸库(
this thread包含一个这样的建议),但检测存储库是否嵌入可能是不可行的,因此这项工作未在此系列中实现。
git config
现在在其手册页面中包含了:
safe.bareRepository
指定 Git 将使用哪些裸仓库。目前支持的值有:
all
:Git 使用所有的裸仓库。这是默认设置。
explicit
:Git 仅使用通过顶级 --git-dir
命令行选项或 GIT_DIR
环境变量指定的裸仓库。
如果您的工作流程中不使用裸仓库,则将 safe.bareRepository
设置为 explicit
可能会有益。这将保护您免受涉及克隆包含裸仓库并在该目录中运行 Git 命令的存储库的攻击。
此配置设置仅在受保护的配置(请参阅定义)中受到尊重。这可以防止不受信任的存储库篡改此值。
在 Git 2.41 (Q2 2023) 中,跟踪机制学会了注意并报告自动发现的裸仓库是否正在使用,因为允许这样做而没有明确说明用户打算这样做(例如通过设置 GIT_DIR
)可以被用作社交工程攻击向量。
请参见 commit e35f202(2023年5月1日),作者是 Glen Choo (chooglen
)。
(由 Junio C Hamano -- gitster
-- 在 commit fa88934 中合并,2023年5月15日)
签名作者:Glen Choo
签名作者:Josh Steadmon
safe.bareRepository=explicit
是更安全的默认操作模式,因为它可以防范 裸仓库攻击。
大多数终端用户不直接使用裸仓库,因此他们应该能够设置 safe.bareRepository=explicit
,并期望通过指定 GIT_DIR
或 --git-dir
来重新启用裸仓库。
但是,用户可能会使用一个在没有设置 GIT_DIR
的情况下调用 Git 的工具(例如 "go mod
" 将克隆裸仓库,请参见 go.dev/ref/mod
),因此即使用户想要使用 safe.bareRepository=explicit
,除非他们的工具学会设置 GIT_DIR
,否则这是不可行的。
为了使这个过渡更容易,添加一个跟踪消息来记录我们在没有设置 GIT_DIR
的情况下尝试设置裸仓库时的情况。
这使得用户和工具开发人员可以审核哪些工具有问题,并报告/修复问题。
当他们足够自信时,他们将转换到 "safe.bareRepository=explicit"。
请注意,这使用了 trace2_data_string()
,它不受 "normal" GIT_TRACE2
目标的支持,只有 _EVENT
或 _PERF
。
github
中的存储库是裸库(bare),但它包含了所有源代码,这与“它们不包含您的源文件的任何工作副本或检出副本”的说法相矛盾。 - Albert