如何克隆一个没有提交的裸git仓库,并在克隆过程中获得正确的HEAD引用?

o2rvlv0m  于 2023-04-28  发布在  Git
关注(0)|答案(2)|浏览(116)

This answer声称这个问题在1.8.4.3版本中得到了解决,但我仍然在2.25.1版本中遇到了这个问题。在2.32.0版本中,它看起来像预期的那样工作,所以我不确定它实际上是什么时候修复的。
是否有一种方法可以使用clone子命令在git版本2.25.1中获得预期的行为(而不必在克隆后 checkout /切换分支)?
以下是复制步骤:
1.初始化裸存储库:

BARE_DIR="$PWD/bare"
WORKING_DIR="$PWD/working"

mkdir -p $BARE_DIR/repo
cd $BARE_DIR/repo
git init --bare

1.更改HEAD参考:

git symbolic-ref HEAD refs/heads/very-unlikely-to-be-your-configured-default-branch

1.克隆存储库的工作副本:

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
von4xj4u

von4xj4u1#

如果没有提交,就没有分支。
虽然Git可以从其他Git读取默认分支名称,因此可以使用正确的ref: refs/heads/*name*条目创建一个新的克隆,Git 2。25没有。如果是这样,新克隆中未诞生的分支将与裸存储库中未诞生的分支匹配。这就是你想要的。直到Git 2。31.0,Git没有这样做。
如果你不能升级,解决方案是让你克隆的仓库至少包含一个提交,这样它就可以有无限多的分支名称。然后在那个仓库中创建至少一个 * 分支name*--尽管,实际上,创建第一个提交会为你创建一个分支名---并使它的HEAD引用一个想要的分支名。

30byixjq

30byixjq2#

有没有办法在git版本2中获得预期的行为。25.1使用clone子命令(克隆后无需 checkout /切换分支)?
即使有办法,也不总是足够的:
git clone“(man)来自一个repository,其引用的HEAD未出生,没有正确设置结果仓库中的HEAD,这已经在Git 2中得到纠正。38(二零二二年第三季度)。
参见commit daf7898(2022年7月11日)和commit cc8fcd1commit 3d8314fcommit f77710c(2022年7月7日)至Jeff King ( peff )
(由Junio C Hamano -- gitster --合并于commit cf92cb2,2022年7月19日)

clone:甚至与其他分支一起传播空远程HEAD

签字人:杰夫·金
除非给出“--branch“,否则clone通常会尝试将本地HEAD与远程HEAD匹配。
对于大多数存储库,这很容易:远程命令告诉我们HEAD指向的是哪个分支,然后我们在那个分支上调用本地的checkout()函数。
当克隆一个空的仓库时,这有点棘手:我们有特殊的代码来检查传输的“未出生的”扩展,或者福尔斯我们对默认分支应该是什么的本地想法。
在这两种情况下,我们都将新的HEAD指向它,并设置branch.*配置。
但还有一个案子没处理当远程仓库 * 不是 * 空的,但是它的HEAD还没有出生。
checkout()函数足够聪明,它意识到我们没有获取远程HEAD,并发出警告。
但我们会忽略遥控器通过未出生的分机给我们的任何信息。
这导致了无意义的结果:

  • 如果远程的HEAD指向一个未出生的“foo”,并包含另一个分支“bar”,克隆将获得分支“bar”,但将本地HEAD指向“master”(或任何我们的本地默认值),这是无用的。

项目不使用“master”作为分支。

  • 更糟糕的是,如果另一个分支“bar”被称为“master”(但同样,远程HEAD没有指向它),那么我们最终会得到一个未出生的本地分支“master”,它没有连接到远程“master”(它没有共享历史,也没有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(尽管注意对于空repo的情况总是错误的,因为根据定义,空repo没有匹配的分支)
  • 在空回购的情况下,回退到默认名称更加明确。

非空的case最终会从checkout()中跳出,并发出警告,这会产生类似的结果,但无法设置我们在空case中所做的分支配置。
所以让我们把HEAD设置完全从这个条件中拉出来。
这消除了一些代码的重复,结果很容易理解,因为像find_ref_by_name()这样的助手函数即使在空仓库的情况下也能做正确的事情(即:即,通过返回NULL)。
这里有两个微妙之处:

  • 对于一个带有分离HEAD的远程,它将为HEAD通告一个oid(我们将其存储在"remote_head"变量中),但我们不会找到匹配的refname(因此我们的“remote_head_points_at”为NULL)。

在本例中,我们创建一个本地分离的HEAD来匹配。
现在,这通过使用非NULL remote_head到达update_head()来隐式地发生(因为我们跳过了所有未出生的回退)。
现在我们需要在回退之前显式地说明它。

  • 对于一个空的repo,我们向用户发出警告,他们已经克隆了一个空的repo。

该警告的文本对于具有未出生HEAD的非空repo没有意义,因此我们必须区分这两种情况。
我们可以只使用不同的文本,但让我们允许代码继续到checkout(),它将发出适当的警告,如:
远程HEAD引用不存在的ref,无法 checkout
继续向下到checkout()将更容易在顶部执行更多修复(见下文)。
请注意,此补丁修复了另一方使用协议扩展向我们报告未出生头部的情况。
它 * 没有 * 修复另一方没有告诉我们的情况,我们在本地猜测“master”,而另一方恰好有一个“master”,它的HEAD没有指向。
但这并没有使事情变得更糟,而且实际上应该更容易在顶部解决这个问题。
另外,您需要将正确的HEAD转换为正确的哈希格式(可能是SHA 256而不是SHA1)。
在Git 2.41(2023年第2季度)中,来自空存储库的“git clone”(man)学会了将哈希算法的选择从源存储库传播到新创建的存储库。
参见Junio C Hamano ( gitster )commit 8b214c2(2023年4月5日)。
(由Junio C Hamano -- gitster --合并于commit 96f4113,2023年4月11日)

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

用户可以准备一个空的存储库,并将其设置为使用SHA 256作为对象格式。
但是,由“git clone”(man)从这样的存储库创建的新存储库不会记录它期望相同SHA 256格式的对象。
如果源资料库不为空,则会按预期工作。
就像我们开始从远程存储库复制主分支的名称一样,即使它在3d8314f中尚未诞生(“clone:propagate empty remote HEAD even with other branches”,2022-07-07,Git v2.38.0-rc 0--merge列在batch #5中),将记录对象格式的代码从仅在从示例化存储库克隆时执行的块中提取出来,以便它在从空存储库克隆时也能工作。

相关问题