git notes用于为已存在提交附加不可变元数据,而非修改提交信息;add覆盖原有notes,append追加内容;需配置notes.displayref才能在git log中显示,且notes推送需显式执行。

Git Notes 不是用来“补救”写错的提交信息的,它专为给已存在、已推送、甚至已被多人拉取的提交附加不可变元数据而设计——审查结论、CI 状态、安全扫描结果、设计决策上下文,都该走 git notes,而不是重写历史。
git notes add 和 git notes append 的行为差异
两者都写入 refs/notes/commits,但语义和覆盖逻辑完全不同:
-
git notes add -m "Reviewed by @alice":如果该提交已有 notes,会直接覆盖整段内容,旧内容丢失 -
git notes append -m "[2026-06-08] LGTM after rebase":在原有 notes 内容末尾追加新行,保留全部历史记录 - 实际 CI 流程中建议默认用
append,避免覆盖人工评审意见;只有明确要“重置状态”(如从CI: failed切换为CI: passed)才用add - 注意:
append不会自动加换行符,建议手动在消息开头或结尾加\n保证可读性
如何让 git log 自动显示 notes 内容
默认 git log 不展示 notes,必须显式启用显示逻辑:
- 全局开启(推荐):
git config --global notes.displayRef refs/notes/commits - 只对当前仓库生效:
git config notes.displayRef refs/notes/commits - 临时查看某次提交的 notes:
git show --notes=refs/notes/commits <commit></commit> - 若使用了自定义命名空间(如
refs/notes/review),必须把notes.displayRef设为对应路径,否则git log仍不显示 - 注意:该配置不影响
git log --oneline输出格式,但会影响--pretty=medium及以上级别中的Notes:区域
CI 自动化脚本中写入 notes 的关键防护点
在 Jenkins/GitLab CI 等环境中批量写入 notes,容易因环境变量缺失或网络异常导致失败或脏数据:
- 始终校验
$GIT_COMMIT是否非空:test -n "$GIT_COMMIT" || exit 1 - 避免空 notes 导致操作被跳过(某些 Git 版本会静默失败):
git notes add --allow-empty -m "$NOTE_CONTENT" "$GIT_COMMIT" - 写入前先 fetch 最新 notes,减少本地 stale 数据冲突:
git fetch origin refs/notes/commits:refs/notes/commits - 推送 notes 必须显式执行:
git push origin refs/notes/commits;git push --all不包含 notes - 若使用多个命名空间(如
refs/notes/security和refs/notes/review),需分别推送,不能用通配符
为什么 notes 不会在 git log --oneline --all 里重复出现提交哈希
你看到的不是“重复提交”,而是 git log --all 把 refs/notes/commits 当作一个普通 ref 来遍历,而 notes 对象本身是 commit 类型的 blob,其 SHA 是独立生成的。这不是 bug,是设计使然:
- 真正的问题在于误用了
--all:它本意是列出所有 ref(包括 tags、remotes、notes),不是为了看“带注释的提交历史” - 正确做法是去掉
--all,改用git log --oneline --notes=refs/notes/commits - 如果团队已习惯
--all,可在.gitconfig中设置别名:alias.lg = log --oneline --notes=refs/notes/commits --graph - 最易忽略的一点:notes 引用本身没有分支生命周期,
git gc不会自动清理孤立 notes 对象,长期运行的仓库需定期git notes prune











