首页 > 解决方案 > 为什么`git update-ref -d`不删除空的父目录?

问题描述

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的。

这是一个错误吗?

标签: gitimplementationgit-plumbinggit-update-ref

解决方案


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

/*
 * 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在我的示例中)使得感觉。


推荐阅读