如何在没有提交的情况下克隆裸git repo,并在克隆过程中获得正确的HEAD ref



这个答案声称这个问题在版本1.8.4.3中得到了修复,但我在版本2.25.1中仍然遇到它。它似乎在2.32.0版本中按预期工作,所以我不确定它是什么时候真正修复的。

是否有一种方法可以使用clone子命令在git版本2.25.1中获得预期行为(而不必在克隆后签出/切换分支)?

以下是复制步骤:


  1. 初始化裸回购:
BARE_DIR="$PWD/bare"
WORKING_DIR="$PWD/working"
mkdir -p $BARE_DIR/repo
cd $BARE_DIR/repo
git init --bare
  1. 更改HEAD ref:
git symbolic-ref HEAD refs/heads/very-unlikely-to-be-your-configured-default-branch
  1. 克隆repo的工作副本:
mkdir $WORKING_DIR
cd $WORKING_DIR
git clone $BARE_DIR/repo
cd repo
  1. 检查HEAD参考:
cat .git/HEAD

我预计输出为:

ref: refs/heads/very-unlikely-to-be-your-configured-default-branch

但它是:

ref: refs/heads/master

当没有提交时,就没有分支。

尽管在现代Git中,Git可以从其他Git中读取默认分支名称,因此可以使用正确的ref: refs/heads/name条目创建新的克隆,但Git 2.25不能。如果是,则新克隆中未出生的分支将与裸存储库中未出生分支相匹配。这是你想要的。但直到Git 2.31.0,Git才做到这一点。

如果无法升级,解决方案是使正在克隆的存储库至少包含一个提交,这样它就可以有无限多的分支名称。然后在该存储库中至少创建一个分支名称——尽管实际上,创建第一个提交将为您创建一个分支名称——并使其HEAD引用一个所需的分支名称。

是否有一种方法可以使用clone子命令(在克隆后不必签出/切换分支)获得git 2.25.1版本中的预期行为?

即使有办法,也不总是足够的:

"CCD_ 10">(man)来自一个具有未出生HEAD的引用的存储库,该引用没有在生成的存储库中正确设置HEAD,这已经用Git 2.38(2022年第三季度)进行了更正。

请参阅Jeff King(peff)的提交daf7898(2022年7月11日)和提交cc8fcd1、提交3d8314f、提交f77710c(2022年6月7日)
(由Junio C Hamano合并——gitster——提交cf92cb2,2022年7月19日)

clone:即使与其他分支一起传播空的远程HEAD

签字人:Jeff King

除非"CCD_ 14";则克隆通常会尝试将本地HEAD与远程HEAD相匹配
对于大多数存储库来说,这很容易:远程系统会告诉我们HEAD指向哪个分支,我们会调用该分支上的本地checkout()函数。

当克隆一个空的存储库时,这有点棘手:我们有特殊的代码来检查传输的";未出生的";扩展,或者回到默认分支应该是什么的本地想法。
在任何一种情况下,我们都将新的HEAD指向它,并设置branch.*配置。

但有一种情况没有得到处理:远程存储库不是空的,但它的HEAD未出生
checkout()函数足够聪明,可以意识到我们没有获取远程HEAD,它会发出警告
但我们会忽略遥控器通过未出生的扩展提供给我们的任何信息
这导致了无意义的结果:

  • 如果遥控器的头指向未出生的"胎儿";CCD_ 18";并包含另一个分支";CCD_ 19";,克隆将得到分支";CCD_ 20";但使局部HEAD指向"0";CCD_ 21";(或者无论我们的本地默认值是什么),这都是无用的
    该项目不使用"CCD_ 22";作为一个分支
  • 更糟糕的是,如果另一个分支";CCD_ 23";而是被称为";CCD_ 24";(但同样,远程HEAD没有指向它),然后我们最终得到了一个本地未出生的分支";CCD_ 25";,未连接到遥控器"CCD_ 26";(它没有共享历史记录,也没有branch.*配置)

相反,我们应该尝试使用遥控器的HEAD,即使它未出生,也要与其他情况保持一致。

遗漏此案例的原因是cmd_clone()在条件的两个不同侧处理空和非空存储库

if (we have any refs) {
fetch refs;
check for --branch;
otherwise, try to point our head at remote head;
otherwise, our head is NULL;
} else {
check for --branch;
otherwise, try to use "unborn" extension;
otherwise, fall back to our default name name;
}

因此,最小的变化将是重复";未出生的";第一块末尾的逻辑
但我们可以注意到其他一些重叠和不一致之处:

  • 双方都必须处理--branch(但请注意,对于空回购情况,这总是一个错误,因为根据定义,空回购没有匹配的分支)
  • 在空回购的情况下,回退到默认名称要明显得多
    非空情况最终会从checkout()中退出并发出警告,这会产生类似的结果,但无法设置我们在空情况下所做的分支配置

因此,让我们将HEAD设置从这个条件中完全拉出
这消除了部分代码的重复,结果很容易理解,因为像find_ref_by_name()这样的辅助函数即使在空的repo情况下也能做正确的事情(即返回NULL)。

有两个微妙之处:

  • 对于具有分离HEAD的远程,它将为HEAD通告一个oid(我们将其存储在"remote_head"变量中),但我们找不到匹配的refname(因此我们的"remote_head_points_at"为NULL)
    在这种情况下,我们制作一个本地分离的HEAD来匹配
    现在,这是通过使用非NULLremote_head到达update_head()而隐式发生的(因为我们跳过了所有未出生的回退)
    在执行回退之前,我们现在需要明确说明它。

  • 对于一个空的repo,我们向用户发出警告,说他们已经克隆了一个空repo
    该警告的文本对于未出生HEAD的非空回购没有意义,因此我们必须区分这两种情况
    我们可以使用不同的文本,但让我们允许代码继续到checkout(),这将发出适当的警告,如:

    远程HEAD引用不存在的ref,无法签出

    继续执行checkout()将更容易进行更多修复顶部(见下文)。

请注意,此补丁修复了对方使用协议扩展向我们报告未出生头部的情况
并不能解决另一方不告诉我们的情况,我们在本地猜测"CCD_ 35";,而另一侧恰好有一个";CCD_ 36";它的头没有指向它
但这并没有让任何事情变得更糟,实际上应该会让解决问题变得更容易。


此外,您需要将正确的HEAD转换为正确的哈希格式(可以是SHA256而不是SHA1)。

在Git 2.41(2023年第二季度)中;CCD_ 37">(man)从空存储库学习将散列算法的选择从源存储库传播到新创建的存储库。

参见Junio C Hamano(gitster)提交的8b214c2(2023年4月5日)
(由Junio C Hamano合并--gitster--提交96f4113,2023年4月11日)

clone:从void克隆时传播object-format

用户可以准备一个空的存储库,并将其设置为使用SHA256作为对象格式
由"CCD_ 42"><来自这样一个存储库的sup>(man)不会记录它期望的对象是相同的SHA256格式
如果源存储库不为空,则此操作会正常工作。

就像我们开始从远程存储库复制主分支的名称一样,即使它在3d8314f中未出生("clone:即使与其他分支一起传播空的远程HEAD",2022-07-07,Git v2.38.0-rc0-merge在第5批中列出),将记录对象格式的代码从仅在从实例化存储库克隆时执行的块中取出,以便在从空存储库进行克隆时也能正常工作。


使用Git 2.41(Q2 2023);CCD_ 44">(man)现在向客户端发布必要的提示,以帮助他们从空存储库中克隆,并学习HEAD指向的对象哈希算法和(未出生的)分支,即使是在旧的v0/v1协议上。

参见brian m.carlson(bk2204)的提交933e3a4(2023年5月17日)
(由Junio C Hamano合并——gitster——提交633390b,2023年5月19日)

upload-pack:克隆空转发时播发功能

签字人:brian m.carlson

克隆空存储库时,协议版本0和1当前只提供/info/refs端点的标头和刷新数据包
这意味着没有提供任何功能,因此客户端不知道存在哪些功能。

然而,当使用SHA-256存储库时,这确实会带来问题,因为我们使用这些功能来了解远程端的对象格式(哈希算法)
8b214c2("clone:从void克隆时传播对象格式",2023-04-05,Git v2.41.0-rc0——第9批中列出的merge),对于协议v2,这一点已经得到了修复,因为我们总是从远程读取哈希算法。

幸运的是,该协议的推送版本已经为如何解决这一问题提供了线索
当访问/info/refs端点进行推送并且远程为空时,我们包括一个伪";CCD_ 51";ref指向全零对象ID。
协议文档已经指出,即使是提取和克隆,也应该始终发送,所以让我们这样做,这意味着我们将正确地宣布哈希算法作为功能的一部分
这只适用于现有代码,因为我们共享相同的获取和克隆参考代码,libgit2、JGit和dulwich也可以。

最新更新