跳转至

2026-05-09 学习日志

今日主题

  • Git commit 时间重写原理
  • skills CLI 内部机制

新增认知

Git commit 时间重写原理

  • Git commit object 包含两个独立时间戳:author date 和 committer date,分别对应 jgit 中 CommitBuilder 的 author 和 committer PersonIdent。git log 默认显示 author date,GIT_COMMITTER_DATE 环境变量控制 committer date,--date 参数控制 author date,两者需同时覆盖才能完整重写时间。
  • git rebase -i edit 模式的底层机制是 cherry-pick 链:每次 cherry-pick 后暂停让你 amend,amend 因为动了 commit 内容(时间戳是内容的一部分)必然生成新 object ID,然后 rebase --continue 用新 ID 作为 parent 继续 cherry-pick 下一个。这就是为什么改一个 commit 的时间会导致所有子孙 commit 的 hash 全部重生——内容寻址的必然结果。
  • GIT_COMMITTER_DATE 只在当前 shell 进程生效,rebase -i 的 exec 行 fork 出的子进程不一定继承该环境变量,所以用 exec 设时间不可靠。正确做法是用 edit 模式,在 rebase 停住的 shell 里直接设环境变量执行 git commit --amend,保证变量作用于 amend 进程。
  • jgit API 中改 commit 时间的等价操作:用 RevWalk 解析旧 commit,创建新 CommitBuilder,通过 setAuthor/setCommitter 传入新的 PersonIdent(带新时间戳),保持 tree/parent/message 不变,ObjectInserter.insert 即可生成新 commit。底层和 CLI 的 amend 一样都是重建 commit object。

skills CLI 内部机制

  • skills CLI 的 update 不是 diff 文件内容,而是用 GitHub Trees API 获取 skill 文件夹的 tree SHA(fetchSkillFolderHash),将其存入 ~/.agents/.skill-lock.jsonskillFolderHash 字段。update 时重新请求 API 比对 hash,不一致就执行 skills add -g -y 重新安装。关键前提:只对 GitHub source、且有 skillFolderHashskillPath 记录的 skill 生效;local/git/well-known 类型的 source 会被 getSkipReason 跳过。
  • skills remove 正常情况会调用 removeSkillFromLock 清理锁文件中的对应条目,但手动删除文件夹、旧版本 bug、remove 过程出错中断都可能导致 ~/.agents/.skill-lock.json 残留脏数据。目前工具没有内置的 check/clean 命令来校验锁文件与实际磁盘状态一致性,只能手动编辑 JSON 或删除重建。排查命令:cat ~/.agents/.skill-lock.json | python3 -m json.tool 查看,对比 ls ~/.agents/skills/ 目录。

  • skills add 安装时决定的目录名来自 SKILL.md frontmatter 的 name 字段,而非 SKILL.md 所在目录名。parseSkillMd(gray-matter 解析 frontmatter)→ skill.name → installSkillForAgent 中 sanitizeName(skill.name)。只有当 name 为空时才 fallback 到 basename(skill.path)。sanitizeName 规则:转小写、非 [a-z0-9._] 替换为 -、去首尾 . 和 -、截断 255 字符。well-known/remote skill 使用 installName 而非 name,同样经过 sanitizeName。