bigywater.tech
Git · 7

tree 对象:100644、100755、120000、160000 四种模式

tree 对象里每个条目前面的 100644、100755、120000、160000 到底是什么?这篇把四种条目放进同一个提交,读原始字节,看符号链接的 blob 里存了什么、子模块的条目指向哪个根本不在本仓库里的对象,还试了试写别的模式会怎样。

4 种
tree 条目的模式:100644 文件、100755 可执行、120000 符号链接、160000 子模块
9 字节
符号链接的 blob 内容就是链接目标 plain.txt,没有别的标记
找不到
子模块条目的 commit 在主仓库里 git cat-file 报错,对象在子仓库

四种模式

实测 · 本机执行输出
  $ 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 里就这些,不保存时间戳、所有者和其它权限位。

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。权限位只剩“是否可执行”一个信息。

分步演示

四种模式
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。子模块用本机另一个目录当远程(git -c protocol.file.allow=always submodule add);符号链接在 macOS 文件系统上创建;原始字节来自对象文件的 zlib 解压
    • 没有验证:Windows 上检出符号链接的行为(core.symlinks=false);--cacheinfo 之外(如 git add 在不同 core.fileMode 下)的模式规整,见后面 core.fileMode 一篇;旧仓库里历史遗留的 100664 在 fsck 下的告警;子模块的更新流程(后面有专门一篇)
    京ICP备2026067502号-1京公网安备11010502063487号