Skip to content
masterclass Buddy

Git · Command

git notes

git notes lets you attach, read, change and remove extra text (notes) on Git objects, usually commits, without touching the objects. The commit hash stays the same, so you can annotate history that has already been published.

Last updated

How it works

Each note is a blob. Notes live in a separate notes ref, which is refs/notes/commits by default. That ref works like a branch whose tree holds files named after the object IDs they annotate, split into two-hex-digit directories for speed. Subcommands such as add, append, edit, show, copy, remove and prune change this tree. git log and git show then print matching notes under a "Notes:" heading after the commit message. You can pick a different notes ref with --ref, GIT_NOTES_REF or core.notesRef, which lets you keep separate groups of notes, such as review notes or build notes.

Syntax

git notes [list [<object>]] | add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [-e] [<object>] | append ... | edit [<object>] | show [<object>] | copy [-f] (--stdin | <from-object> [<to-object>]) | merge [-s <strategy>] <notes-ref> | remove [--ignore-missing] [--stdin] [<object>...] | prune [-n] [-v] | get-ref

Parameters

NameTypeDescription
<object>object nameThe object the note is attached to. Most subcommands default to HEAD.
-m <msg> / --message=<msg>stringSets the note text. If you give it more than once, each value becomes its own paragraph.
-F <file> / --file=<file>pathReads the note text from a file. Use '-' to read from standard input.
-C / -c <object>blobUses an existing blob as the note text. -C copies it word for word; -c opens it in an editor first.
-f / --forceflagReplaces an existing note instead of stopping with an error.
--allow-emptyflagKeeps an empty note. Without it, empty notes are removed automatically.
--separator[=<paragraph-break>] / --no-separatorstringSets the text placed between paragraphs. The default is a blank line.
--[no-]stripspaceflagTurns whitespace cleanup on or off. It is on by default, except with -C.
--ref=<ref>refWorks on this notes ref instead of the default one. Overrides GIT_NOTES_REF and core.notesRef.
-s <strategy> / --strategy=<strategy>manual|ours|theirs|union|cat_sort_uniqSets how merge resolves conflicting notes.
--stdinflagReads object names (for remove) or object pairs (for copy) from standard input.
-n / --dry-runflagFor prune: lists the notes that would be removed without removing them.

Key points

  • If the notes ref does not exist yet, you don't need to create it. Git creates it automatically when you store your first note.
  • Running 'git notes' with no subcommand runs 'list'. Without an object, list prints every pair of note blob and annotated object.
  • Use append when you want to add to a note instead of replacing it. Each new message goes after the existing text with a separator between them. If there is no note yet, append creates one.
  • Notes have their own history: every change commits to the notes ref, so you can review it with git log -p on that ref.
  • git notes merge brings another notes ref into the current one. Besides the default manual mode, you can choose ours, theirs, union or cat_sort_uniq to resolve conflicts automatically.
  • prune removes notes whose target objects no longer exist or can't be reached. Add -n to preview what would go.
  • notes.displayRef controls which extra notes refs git log shows. A ref that doesn't exist gives a warning, but a glob that matches nothing is ignored.

Common mistakes

  • Notes are not copied to new commits on amend or rebase unless you set notes.rewriteRef (for example to refs/notes/commits). Without it, rewritten commits lose their notes.
  • -F is not safe for binary files. To attach binary data, store it with git hash-object -w and attach the blob with -C.
  • Whether whitespace is cleaned up depends on the order of options. For example, a -m that comes after -C turns cleanup back on.
  • cat_sort_uniq removes all duplicate lines, including duplicates that were already inside the local or remote note before the merge.

Example: Annotate a commit, append to the note, and use a separate notes ref

#!/usr/bin/env bash
set -euo pipefail

# Attach a note to HEAD without changing its hash
git notes add -m 'Tested-by: CI pipeline #482'

# Add another paragraph to the same note
git notes append -m 'Perf: p95 latency 120ms'

# Print the note, and see it in the log
git notes show HEAD
git log -1

# Keep review notes in their own ref (refs/notes/review)
git notes --ref=review add -m 'LGTM - alice' HEAD
git log -1 --notes=review

# Show which notes ref is currently in use
git notes get-ref

# Keep notes when amending/rebasing
git config notes.rewriteRef refs/notes/commits

# Preview removal of notes on objects that no longer exist
git notes prune -n -v

This script adds a note to a commit and appends to it, keeps review notes in a separate ref, turns on note copying for amend and rebase, and previews a prune.

When to use it

After-the-fact sign-offs

Add Tested-by or Reviewed-by lines to commits that are already published, without rewriting history or changing hashes.

CI and build metadata

Store build results, test reports or artifact blobs in a dedicated ref such as refs/notes/built, so they never clutter commit messages.

Patch cover commentary

Keep per-commit explanations as notes, then include them below the '---' line of emailed patches with git format-patch --notes.

Team annotations

Different teams keep separate notes refs and combine them with git notes merge, using a strategy such as union or cat_sort_uniq.

Interview questions

QHow do git notes differ from amending a commit message?

Amending creates a new commit with a new hash, which rewrites history. A note is stored as a separate blob in a notes ref, so the original commit stays exactly the same. git log still shows the note under a 'Notes:' heading, which is why notes work for annotating commits that are already published.

Follow-up: How are notes stored internally? The notes ref points to a commit whose tree has files named after the annotated object IDs, split into two-hex-digit directories for speed; each file is the note blob.

QWhat happens if you run 'git notes add' on a commit that already has a note?

With -m or -F it stops with an error, so nothing is overwritten by accident. Use -f to replace the note, or use append to add text to it. If you run add with no message, it opens the existing note in your editor, the same way edit does.

Follow-up: How do you delete a note, and what happens with an empty message? Use git notes remove (add --ignore-missing to skip objects that have no note). Saving an empty message has the same effect, because empty notes are removed unless you pass --allow-empty.

QHow do you resolve conflicts when merging two notes refs?

git notes merge <notes-ref> uses the manual strategy by default. It checks out the conflicting notes into .git/NOTES_MERGE_WORKTREE so you can fix them by hand. You then run git notes merge --commit to finish or --abort to cancel. You can also pick ours, theirs, union or cat_sort_uniq with -s or the notes.mergeStrategy setting.

Follow-up: Can you set a different strategy for each notes ref? Yes: notes.<name>.mergeStrategy applies to merges into refs/notes/<name> and takes priority over the general notes.mergeStrategy setting.

  • git log
  • git show
  • git format-patch
  • git config
  • git hash-object
  • git stripspace
  • git commit

Source: Git - git-notes Documentation. Checked 2026-11-02.