tree 对象里每个条目前面的 100644、100755、120000、160000 到底是什么?这篇把四种条目放进同一个提交,读原始字节,看符号链接的 blob 里存了什么、子模块的条目指向哪个根本不在本仓库里的对象,还试了试写别的模式会怎样。
$ stat -f '%Sp %N' plain.txt run.sh link; readlink link -rw-r--r-- plain.txt -rwxr-xr-x run.sh lrwxr-xr-x link plain.txt $ git ls-tree HEAD 100644 blob 0bfa7e1721cf7850c5606ac62d332e17b4db9fa9 .gitmodules 120000 blob dab8c79946b1756dcd7db770a986ad40d00c07f4 link 100644 blob b9bca019c83a65e6d717d0b6da86215f45dde1b3 plain.txt 100755 blob 4163036efa65bd4a469e752267498f01ea36a55c run.sh 040000 tree a5283891438cf33b96c176341fb34f85e7ed4861 vendor $ git ls-tree -r HEAD 100644 blob 0bfa7e1721cf7850c5606ac62d332e17b4db9fa9 .gitmodules 120000 blob dab8c79946b1756dcd7db770a986ad40d00c07f4 link 100644 blob b9bca019c83a65e6d717d0b6da86215f45dde1b3 plain.txt 100755 blob 4163036efa65bd4a469e752267498f01ea36a55c run.sh 160000 commit f0b877b63efcb025a40e96d709db91ed096443a3 vendor/lib
一个条目的模式决定它被检出成什么:100644 普通文件、100755 可执行文件、120000 符号链接、160000 子模块(gitlink),再加上目录本身的 40000。tree 里就这些,不保存时间戳、所有者和其它权限位。
原始字节里的模式: '100644' 名字 .gitmodules 原始字节里的模式: '120000' 名字 link 原始字节里的模式: '100644' 名字 plain.txt 原始字节里的模式: '100755' 名字 run.sh 原始字节里的模式: '40000' 名字 vendor vendor/ 这个子 tree: $ git ls-tree a5283891438cf33b96c176341fb34f85e7ed4861 160000 commit f0b877b63efcb025a40e96d709db91ed096443a3 lib
模式在 tree 对象里是 ASCII 数字,目录写 40000(没有前导零),git ls-tree 显示时才补成 040000。
$ git cat-file -s dab8c79946b1756dcd7db770a986ad40d00c07f4; git cat-file -p dab8c79946b1756dcd7db770a986ad40d00c07f4; echo; printf plain.txt | wc -c
9
plain.txt
9
原始字节: b'blob 9\x00plain.txt'符号链接的 blob 内容就是链接目标的路径字符串,没有别的标记;“这是个链接”这件事由 tree 条目的 120000 表达。所以在不支持符号链接的系统上检出,会得到一个内容为路径的普通文本文件。
vendor/lib 条目的 hash = f0b877b63efcb025a40e96d709db91ed096443a3 $ git cat-file -t f0b877b63efcb025a40e96d709db91ed096443a3 fatal: git cat-file: could not get object info (退出码 128) $ git --git-dir=/work/l3sub/.git cat-file -t f0b877b63efcb025a40e96d709db91ed096443a3 commit .gitmodules 的内容和 git 本地对象里是否有 vendor/lib/lib.txt: $ cat .gitmodules; git ls-tree -r HEAD | grep -c lib.txt || true [submodule "vendor/lib"] path = vendor/lib url = /work/l3sub 0
子模块在父仓库里只占一个 160000 commit 条目,hash 是子仓库的某个 commit。这个对象不在父仓库的对象库里,父仓库自己的 git cat-file 找不到它;路径与 URL 记录在 .gitmodules 里。
--cacheinfo 100664 → 实际写入的模式: 100644 --cacheinfo 100600 → 实际写入的模式: 100644 --cacheinfo 100777 → 实际写入的模式: 100755 --cacheinfo 120755 → 实际写入的模式: 120000
写入索引时 git 把非标准的文件模式规整:100664、100600 → 100644,100777 → 100755,120755 → 120000。权限位只剩“是否可执行”一个信息。