为什么"git update-ref -d"不删除空的父目录?



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

但也可以创建和使用.git/refs下其他地方的任意非标准引用。这对于将自定义元数据存储在存储库中非常有用,您不希望用户直接与之交互。例如,GitHub使用这些类型的refs来公开对拉请求分支的引用,Emacs git客户端Magit在启用适当设置时使用它们来定期保存未提交的更改。这样的refs通常需要使用所谓的";管道";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

当我创建引用refs/foo/bar/baz时,git update-ref创建了必要的父目录。当我删除ref时,它很聪明地删除了父目录bar,该目录现在已变为空。然而,删除";祖父母;目录foo,在删除bar之后该目录现在也是空的。

这是个虫子吗?

不,这是设计的。以下是源代码的注释:

/*
* 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(区别对待是有意义的。

这是用Git 2.32(2021年第二季度(正式解决的(仅适用于refs/heads(:;CCD_ 16">(man(删除一个已打包的ref,它在$GIT_DIR/refs/下留下了空目录。

参见Will Chandler(wlvchandler(提交的5f03e51(2021年5月8日(
(由Junio C Hamano合并——gitster——于2021年5月16日提交16f9145(

refs:删除压缩引用时清除目录

签字:Will Chandler
审核:Jeff King

通过"update-ref -d"删除压缩的引用时,会在包含该引用的松散副本的目录中创建一个锁定文件,从而在引用的路径中创建任何不存在的目录
事务完成后,锁定文件将被删除,但创建锁定文件时创建的任何空父目录都保留在原位
这些空目录不会被"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/foo,因为在OP中:git update-ref -d refs/foo/bar/baz确实删除了bar/baz,但不删除foo/

最新更新