为什么没有针对特定提交的Git克隆选项?

43

针对Stack Overflow上的一个最近的问题,我想知道为什么在git clone中没有一个选项,使得新创建分支的HEAD指向特定的提交。在上述问题中,提问者试图提供关于应该克隆哪个特定提交的说明。

请注意,这个问题不是关于如何使用reset克隆到特定版本;而是关于为什么没有这样的选项?

4个回答

40
目前已有两个答案(在我写这篇文章的时候,现在有更多)虽然说得没错,但并没有真正回答“为什么”的问题。当然,“为什么”这个问题确实很难回答,除了 Git 各个部分的作者之外没有人能够回答(即使 Git 的两位经常贡献者给出不同的答案又怎样呢?)。
不过,考虑到 Git 的“哲学”,一般来说,各种传输协议都是通过命名一个“参考对象”来工作的。如果他们提供了一个 SHA-1,那么它就是“该参考对象的 SHA-1”。对于没有直接访问存储库(例如命令行)的人,内置命令中没有任何一个命令允许使用 ID 引用提交。我能找到的与此最接近的“原因”是这段git upload-archive 文档中的代码
“安全性:为了保护已从历史记录中删除但可能尚未被修剪的对象的隐私,git-upload-archive 避免为那些不可从存储库的 refs 中到达的提交和树提供服务。但由于计算对象可达性是计算密集型的,因此 git-upload-archive 实现了一组更严格但易于检查的规则……”
然而,它继续说道:
“如果配置选项 uploadArchive.allowUnreachable 设置为 true,则会忽略这些规则,并且客户端可以使用任意 sha1 表达式。如果您不关心不可到达对象的隐私或者您的对象数据库已经可以通过非智能 HTTP 访问公开,请使用此选项。”
这非常有趣,因为git clone首先获取所有可到达的对象,然后您的本地克隆可以轻松地按 SHA-1 ID 检出提交(如果需要,创建一个指向该 ID 的本地分支名称,或者只需将您的克隆保持在“游离 HEAD”模式下)。
鉴于这两个交叉的问题,我认为目前的真正答案是“没有人关心添加它”。:) 隐私论点是合理的,但是Git并没有理由不能在克隆之后通过 ID 检出提交,就像可以使用 git clone -b… 告诉 Git 检出除 master 以外的某些分支一样。唯一的缺点是允许 -b sha1,Git 无法事先检查(在克隆过程开始之前)是否会收到 sha1。它可以检查参考名称,因为那些名称与它们的分支提示或其他 SHA-1 值一起传输,所以 git clone -b nonexistentbranch ssh://... 会快速终止并且不会创建副本:
fatal: Remote branch nonexistentbranch not found in upstream origin
fatal: The remote end hung up unexpectedly
如果-b允许使用ID,那么你会获得整个克隆库,然后它会告诉你:"哦,天啊,抱歉,无法检出该ID,我会让你保持在主分支上"或其他情况。(这与破损的子模块现在发生的事情差不多)。
虽然git upload-archive现在执行了这个“隐私”规则,但这并不总是这样(它在1.7.8.1版本中引入);许多(大部分?)git-web服务器,包括Git本身分发的服务器,允许按任意ID查看。这可能是为什么upload-archive之后几年添加了allowUnreachable(但请注意,在1.7.8之后2.0.0之前发布的Git版本没有放松规则的方法)。因此,虽然这个“安全性”想法是有效的,但在其未强制执行的时期(1.7.8.1之前)也存在过。
有许多方法可以从Git存储库中“泄漏”似乎是私有数据的信息。Git 2.11.1将推出一个新文件Documentation/transfer-data-leaks,而Git 2.11.0添加了一些内部功能(请参见提交722ff7f87等内容)以立即丢弃推送但未被接受的对象。这些对象最终将被垃圾收集,但在此期间它们仍然会暴露出来。
实际上,默认情况下git clone会创建一个本地检出与远程的HEAD引用相对应的分支。通常那是master

这个链接 https://github.com/git/git/blob/565301e41670825ceedf75220f2918ae76831240/Documentation/git-upload-archive.txt#L26-L31 是否合适? - VonC
“allowUnreachable” 这个东西似乎是比较新的(Git 2.0+):https://github.com/git/git/commit/7671b63211712e5163ed46d4c93d0b75680c886c - VonC
并且(在嵌入该链接后)对任意SHA-1的限制是在1.7.8.1中引入的。 - torek
@VonC:脚注1已编辑以包括版本,并微调有关日期的措辞。(我仍然认为git 1.6之后的所有内容都是“比较新的”,我记得在一些公司系统上被困在1.5.x版本中...) - torek
@EdwardThomson:是的,这就是为什么所有内容都被标记为间接的原因,“安全”方面最多只能算不完整。我认为这可以算作是一种哲学上的理由。 - torek
显示剩余2条评论

9

克隆一个存储库是一种不同于checkout的操作。你不能“克隆特定的提交”。为方便起见,您可以同时克隆并checkout特定的预先存在的分支,因为这是大多数人想要的。如果这不能满足您的需求(没有特定SHA的分支),只需使用或别名某种形式的命令即可。

git clone -n <some repo> && cd <some repo> && git checkout SHA

5

如果你的特定提交被分支引用,你可以执行以下操作:

git clone -b yourBranch /url/of/the/repo

克隆的仓库将直接位于该分支引用的提交处。

1
好的。在我的情况下,我们想要使用的submodulesmaster中特定(较旧的)提交相关联。 - ivan.sim
你随时可以从旧提交创建一个分支 - git branch clone_me OLD_SHA - Andrew C
其他地方提出的另一种解决方案是先fork,然后创建一个指向所需SHA的标签,最后使用git子模块来跟踪已标记的SHA。 - CMCDragonkai
@CMCDragonkai 我同意。我怀疑有一个简单的解决方案。 - VonC

5
正如其他答案所说,这通常不是什么大问题,但他们没有说为什么你不能克隆特定的提交。原因在于安全性。
如果您意外地推送了机密信息,然后强制推送一个修复后的历史记录,在服务器的 Git 垃圾回收器找到它不再需要之前,包含机密信息的提交仍将存储在服务器上。如果哈希值是已知的(例如可能在日志中可用),恶意用户可能会请求特定的提交,即使您能够验证,在您强制推送修复后的历史记录时,没有人先获取了这些提交。
确保只能从 refs 克隆可以确保只向客户端发送“可达”的提交。

一个恶意用户如何阻止推送指向该提交ID的新分支?(客户端可以愉快地创建一个指向服务器已经拥有的提交的新分支,而无需推送任何其他提交。)恶意用户随后可以克隆并获取机密提交。 - Edward Thomson
@EdwardThomson 假定恶意用户没有推送访问权限,但是这个观点很有道理。 - user743382
我猜想这个想法是克隆操作将过滤掉那些不应该被访问的提交?如果是这样,为什么不能在服务器上做到这一点,而要通过向服务器发送所需的提交来浪费带宽和宝贵的时间(以及磁盘空间)?我目前正在绝望地观察着Linux内核克隆的进度指示器缓慢地向前移动,知道我想要的提交已经远在过去... - RJVB
安全并不是Git设计的重点(否则他们会将用SHA2系列中的哈希替换SHA1作为优先考虑)。也就是说,安全不能成为主要原因(如果存在两个在其他方面都相等的设计替代品,则安全可以作为辅助原因,但具有哈希下载和不具有哈希下载不是相同的替代品)。 - toolforger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接