首页 > 解决方案 > 为什么 for-each-ref 和 show-ref 在裸存储库中的行为不同?

问题描述

在标准(非裸)Git 存储库中,以下命令都给出了基本相同的结果,因为它们列出了所有分支(每个命令后面的数字是命令的输出,wc -l只是为了保持简短):

R1) bare: false
    pwd                           .../R1/.git
    git branch                    8
    git for-each-ref refs/heads   8
    git show-ref --heads          8
    git for-each-ref refs/heads/* 8
    git show-ref refs/heads/*     8
    find ./refs/heads -type f     8

但在一个裸存储库中,显然并非总是如此:

R2) bare: true
    pwd                           .../R2.git
    git branch                    24
    git for-each-ref refs/heads   24
    git show-ref --heads          24
    git for-each-ref refs/heads/* 17
    git show-ref refs/heads/*     13
    find ./refs/heads -type f     14
R3) bare: true
    pwd                           .../R3.git
    git branch                    15
    git for-each-ref refs/heads   15
    git show-ref --heads          15
    git for-each-ref refs/heads/* 6
    git show-ref refs/heads/*     6
    find ./refs/heads -type f     6
R4) bare: true
    pwd                           .../R4.git
    git branch                    36
    git for-each-ref refs/heads   36
    git show-ref --heads          36
    git for-each-ref refs/heads/* 36
    git show-ref refs/heads/*     0
    find ./refs/heads -type f     0

因此,通过查看比这里显示的更多的存储库,前 3 个命令总是给出一致的答案,无论是裸的还是非裸的,但后 3 个给出的结果并不总是与前三个相同,而且彼此之间并不总是一致. 有人可以解释为什么吗?

从使用非裸存储库开始,我一直假设使用类似branch,的命令show-reffor-each-ref处理 refs/heads 目录的实际内容,但在裸情况下这不可能是真的,因为它们显示的结果比文件中的更多refs/heads 目录。

标签: git

解决方案


简短的回答:

通过引用参数来抑制 shell 的通配符处理:

git for-each-ref "refs/heads/*"

并再次运行您的命令。

因为packed-refs,find不会(或很少)给你正确的答案。


详细回答:

您的命令有几个问题:

  1. 最重要的第一个:Git 有两个地方来存储 refs(即标签、分支等):.git/refs目录和文件.git/packed-refs。有关详细信息,请参阅git-pack-refs

    这排除find ./refs/heads -type f了获得正确答案的可能性。

  2. 接下来的件事:这些命令并不像您认为的那样:

    git for-each-ref refs/heads/*
    git show-ref refs/heads/*
    

    假设是一个类 Unix 环境,shell会尝试*通过匹配现有文件名来扩展通配符。所以git看到实际上是这样的命令:

    git for-each-ref refs/heads/master refs/heads/topic1
    

    因此,只要 ref 仅存储文件packed-refs中,shell 就不会将该名称传递给git,因此没有输出。

    通配符匹配的第二个问题:shell 不会递归到子目录中,因此feature/foo即使 ref 存储在.git/refs/heads/feature/foo.

    您可以通过引用参数来抑制 shell 的通配符处理。有几种方法可以做到这一点,一个简单的方法是:

    git for-each-ref "refs/heads/*"
    

区别并不在于裸存储库和非裸存储库之间,而是“shell 看到至少一个匹配”和“shell 根本没有匹配”之间的区别,因为在后一种情况下,shell 按原样(包括*)将字符串转发到命令。在非裸仓库中就是这种情况,因为.git/您的参数中缺少前缀。


推荐阅读