实测 git 配置的层级与优先级:system → global → local 后读的覆盖先读的;worktree 层(extensions.worktreeConfig)在 local 之后;-c 与 GIT_CONFIG_COUNT 环境变量更高;include 按位置插入,includeIf gitdir: 可按路径引入不同身份;多值键(如 remote.origin.fetch)叠加,--get 取最后一个,写操作默认只改 local;XDG 与 ~/.gitconfig 同时存在时都读。
实测子命令的查找:git foo 找可执行文件 git-foo(--exec-path 和 PATH);拼错命令的提示与 help.autocorrect=immediate;别名展开,! 开头的别名在仓库根目录用 shell 运行,GIT_PREFIX 保存原目录;别名不能覆盖内置命令,且与外部子命令同名时外部命令胜出(顺序:内置 → 外部 → 别名);别名循环被检测;git help -a 列出 External commands 与 Command aliases。
真实 git add 两个文件后,手动按字节解析 .git/index 这个二进制文件,解出的 mode/文件大小/SHA-1/文件名跟 git ls-files --stage 的结果逐字段对上;真实验证文件末尾 20 字节是对前面所有内容重新算一遍 SHA-1 的自校验值;真实发现 commit 之前 git add 就已经把 blob 对象写进了 .git/objects,commit 并没有新建文件内容,只是把索引里已经就位的引用打包成 tree/commit;真实制造工作目录、暂存区、HEAD 三份不同内容的同一个文件,逐一验证 git diff / git diff --cached / git diff HEAD 分别比较的是哪两份。
真实对同一个大文件连续提交 31 次小改动,真实测出 git gc 之前 .git/objects 有 93 个松散对象、占 496K,gc 之后只剩 1 个 pack 文件、占 36K;用 git verify-pack -v 真实看穿 pack 内部的 delta 链,揪出一个反直觉的细节——某个 blob 在 verify-pack 输出里显示只有 38 字节,但 git cat-file -s 一查,真实内容其实是 116321 字节,38 字节只是变成这一版所需的增量指令大小;真实验证删除分支后悬挂的 commit 对象,在默认宽限期内还能用哈希读到,强制清空 reflog 并立即 gc 之后,同一个哈希真实读取失败,对象被真正清理掉了。
真实建两次提交后创建分支,现场数出 .git/objects 对象总数创建前后完全没变(6→6),分支文件本身只有 41 字节;真实验证分支 ref 文件里就是一个 40 位提交哈希加换行符,不是什么特殊结构;在新分支上真实再提交一次,证明 main 分支的 ref 文件内容一字未动;真实对比普通分支和 detached HEAD 下 .git/HEAD 文件内容的区别;真实删掉一个分支后,用它原来指向的哈希直接 cat-file,证明底层 commit 对象根本没被删,只是没有引用再指着它了。
真实建两个路径不同、内容完全相同的文件提交进 git,真实数出 .git/objects 目录只多了一份 blob,不是两份;手动用 zlib 解压这个 blob 文件、重新计算一遍 SHA-1,现场验证文件名(哈希)就是内容本身算出来的,不是随便分配的编号;手动按字节解析 tree 对象的二进制格式,还原出 mode/文件名/20 字节原始哈希;真实只改 1 个字节,新哈希跟旧哈希完全不沾边,并且真实数出这个改动会牵连从这层文件到根目录一路上的几个 tree 对象跟着变,没改的兄弟文件全部复用旧对象。