无法删除名称包含变音符号的远程分支

Cannot remove remote branch which name contains diacritics

我犯了一个错误,用一些变音符号命名了一个分支(比如 temp-à-définir),然后我将它推送到远程。

很明显,下一个git fetch给了我:

error: * Ignoring funny ref 'refs/remotes/origin/temp-?-d?finir' locally

然后我改名本地分支没有变音符号,没问题。但是我无法删除远程分支。

$ git push origin :temp-?-d?finir
fatal: invalid refspec ':temp-?-d?finir'

$ git push origin :temp-à-définir
error: unable to delete 'temp-à-définir': remote ref does not exist

所以,我有一些问题:

  1. 为什么 git ls-remote --heads 显示我的 funny 分支,而 git branch --remote 不显示?

  2. 如何删除这个远程分支?我尝试了我在 SO 上找到的关于远程分支的多个帖子中看到的所有命令。我想我必须用一些时髦的代码替换 àé,但我不知道是哪些。

知道如何实现吗? 提前致谢。

TL;DR

您可能需要让可以直接访问服务器的人找出要删除的内容,然后将其删除。这可能是他们 .git/refs/heads/ 目录中的一个 文件,其中包含他们的存储库副本。

长(大概)

远程是另一个 Git 存储库。因此,如果您可以在远程 登录 ,您可以在那里 运行 Git 命令。1 这将允许您了解他们在 他们的 存储库中如何拼写分支名称。

这里的根本问题是 Unicode 只能部分解决的问题:7 位 ASCII 子集(也称为“US-ASCII”)以外的字符在任何地方都没有单一的标准表示.因此,“définir”一词,如您的情况,或德语中的“schön”,没有一个 每个人 使用 的通用 agreed-upon 编码无处不在.

Git,在内部,尝试使用 UTF-8,但它依赖于 OS 和本地 C 库,如果 UTF-8 支持OS and/or 本地libc 缺少,可能会出事。此外,Git 将 branch 名称存储在两个位置之一或两个位置:

  • 一个“平面文件”(即糟糕的)数据库,由 .git/packed-refs 中的 对组成; and/or
  • tree-structured directory-and-file 区域由 OS 提供,在 .git/refs.

如果我们查看“définir”的 Unicode 拼写,我们会发现有两个:

LATIN SMALL LETTER D (U+0064)
LATIN SMALL LETTER E WITH ACUTE (U+00E9)
LATIN SMALL LETTER F (U+0066)
LATIN SMALL LETTER I (U+0069)
LATIN SMALL LETTER N (U+006E)
LATIN SMALL LETTER I (U+0069)
LATIN SMALL LETTER R (U+0072)

和:

LATIN SMALL LETTER D (U+0064)
LATIN SMALL LETTER E (U+0065)
COMBINING ACUTE ACCENT (U+0301)
LATIN SMALL LETTER F (U+0066)
... the rest is the same ...

也就是说,é 字符可以占用一个 Unicode 点 (e-with-acute),或两个:e,后跟一个组合锐音符。

将这两种不同的表示形式(一种是“NFC”或“组合”,另一种是“NFD”或“分解”)转换为UTF-8,我们分别得到:

64 (`d`), c3 a9 (`é`), 66 (`f`), 69 (`i`), 6e (`n`), 69 (`i`), 72 (`r`)

和:

64 (`d`), 65 (`e`), cc 81 (combining `´`), 66 (`f`), 69 (`i`), 6e (`n`), 69 (`i`), 72 (`r`)

因此,假设 UTF-8,OS 和 C 库 可以 .git/refs/heads/définir 作为文件名存储在文件系统中的两种方式(如果 OS 使用其他编码,就像 Windows 通常那样,我们还有其他变量在起作用)。

(除了 NFC 和 NFD,还有 NFKC 和 NFKD。参见,例如,Normalizing Unicode. For Unicode tables, there are many sites; here's one。)

当您使用 git push 要求服务器创建或删除引用名称时,您的 Git 软件会提供一个 C-style 字符串,其中包含对另一端的引用(即,“wire encoding”,可以说,主要只是一个原始的 C 字符串——ref 和它的哈希 ID 是 space-separated,所以 ref 必须是一个 valid Git ref,因此不会包含一个space)。没有什么 真的 说这是否是 UTF-8,如果是的话,它是 NFC 还是 NFD 或其他什么。

然后服务器可能会将 ref 存储在文件系统中,这可能会稍微破坏它。您的服务器可能就是这种情况。

当服务器将此 返回 发送给您的 Git 客户端时,您的 Git 客户端可能 re-interpret possibly-mangled ref作为别的东西。这似乎是这里的情况:特别是你的服务器已经用 合法的字符替换了 àé refname 中的任何编码(服务器可以通过网络执行此操作,而不会注意到它已经这样做了;客户端将检测到它并称其为“有趣的参考”,就像你的一样)。

通常情况下,您希望能够删除带有 git push --delete 的“错误”参考以及您拼写的参考——但如果 name-mangling 发生 服务器Git将它放入文件系统之后,name-mangling在这个过程中发生得太迟了:服务器认为它被询问了删除不存在的未损坏的名称。您必须发送损坏的名称。但是,您的 client Git 不会让您请求,因为这是非法的引用名称。

(现在有人正在处理 Git 中的一些 Unicode 问题,为将来的 Git 版本——2.36 或更高版本做准备。有很多丑陋和棘手的问题这里有案例,我不希望很快解决这个问题,但可能会有一些进展。)


1这假设他们正在使用 command-line Git 系统。 Git 的替代实现使用 Git 协议,但不提供 command-line 命令,在这种情况下,所有赌注都将关闭。