2

git 中有多种类型的 ref,其中最常见的一些是分支(存储在 中.git/refs/heads)、远程跟踪分支(.git/refs/remotes)和标签(.git/refs/tags)。

但是也可以创建和使用任意的非标准 refs,这些 refs 存在于.git/refs. 这对于将自定义元数据存储在您不希望用户直接与之交互的存储库中很有用。例如,GitHub 使用这些类型的 refs 来公开对 pull request 分支的引用,并且 Emacs git 客户端Magit在启用适当的设置时使用它们定期保存未提交的更改。此类引用通常需要使用 git 的所谓“管道”命令进行操作,因为面向用户的“瓷器”命令不了解或不支持它们。

我正在使用管道命令玩非标准参考,git update-ref并发现了一些奇怪的行为:

$ git init foo && cd foo
$ touch a && git add a && git commit -m init
$ tree .git/refs
.git/refs
├── heads
│   └── master
└── tags

2 directories, 1 file
$ git update-ref refs/foo/bar/baz HEAD
$ tree .git/refs
.git/refs
├── foo
│   └── bar
│       └── baz
├── heads
│   └── master
└── tags

4 directories, 2 files
$ git update-ref -d refs/foo/bar/baz
$ tree .git/refs
.git/refs
├── foo
├── heads
│   └── master
└── tags

3 directories, 1 file

当我创建 refrefs/foo/bar/baz时,git update-ref创建了必要的父目录。当我删除 ref 时,它很聪明地删除了 parent directory bar,该目录现在已变为空。但是,删除“祖父母”目录还不够聪明,删除后该目录foo现在也是空bar的。

这是一个错误吗?

4

2 回答 2

2

不,这是设计使然。这是源代码中的注释

/*
 * Remove empty parent directories associated with the specified
 * reference and/or its reflog, but spare [logs/]refs/ and immediate
 * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
 * REMOVE_EMPTY_PARENTS_REFLOG.
 */
static void try_remove_empty_parents(struct files_ref_store *refs,
                     const char *refname,
                     unsigned int flags)
{

如果我将我的非标准 ref 嵌套更深一层,例如refs/foo/bar/baz/xyzzy,我会注意到父目录和祖父目录都被删除了,但曾祖父目录没有,这使得这种行为更加明显是故意的.

.git/refs/我想这个想法是(如在我的示例中)下的顶级子目录foo代表一种类型的 ref 而不是作为 ref 名称的一部分,因此将它们与树下方的目录区别对待(如bar在我的示例中)使得感觉。

于 2021-05-19T13:49:08.937 回答
0

Git 2.32(2021 年第二季度)正式解决了这个问题(refs/heads仅适用于):当“ git update-ref -dman删除一个打包的 ref 时,它在$GIT_DIR/refs/.

请参阅Will Chandler ( ) 的提交 5f03e51(2021 年 5 月 8 日(由Junio C Hamano 合并 -- --提交 16f9145中,2021 年 5 月 16 日)wlvchandler
gitster

refs: 删除打包的 ref 时清理目录

签字人:Will Chandler
审核人:Jeff King

通过 ' update-ref -d' 删除打包的 ref 时,会在包含该 ref 的松散副本的目录中创建一个锁定文件,从而在 ref 的路径中创建任何不存在的目录。
事务完成后,锁定文件将被删除,但创建锁定文件时创建的任何空父目录都将保留在原处。
这些空目录不会被“pack-refs”或其他内务任务删除,并且会随着时间的推移而累积。

当删除一个松散的 ref 时,我们会在事务结束时删除所有空的父目录。

此提交也将删除松散引用时使用的父目录清理逻辑应用于打包引用。

测试表明:

directory not created deleting packed ref':

git branch d1/d2/r1 HEAD &&
git pack-refs --all &&
test_path_is_missing .git/refs/heads/d1/d2 &&
git update-ref -d refs/heads/d1/d2/r1 &&
test_path_is_missing .git/refs/heads/d1/d2 &&
test_path_is_missing .git/refs/heads/d1  <==== grand-parent is gone

但是,这不适用于refs/fooOP,如 OP:git update-ref -d refs/foo/bar/baz确实删除bar/baz,但不适用foo/

于 2021-05-23T19:27:02.500 回答