Git’s ref system is a tiny language for naming objects and mapping them across repositories. This post is a grammar reference first, and examples second.

Grammar reference

Ref and refname

1ref          := 'refs/' refpath
2refpath      := name ('/' name)*
3name         := 1+ valid-char
4valid-char   := printable ASCII EXCEPT {space, control, '~', '^', ':', '?', '*', '[', '\\'}
5constraints  := not (leading '/', trailing '/', '//', '..', '.lock', '@{')

Common namespaces (grammar applies equally):

  • refs/heads/<branch>
  • refs/tags/<tag>
  • refs/remotes/<remote>/<branch>
  • refs/notes/<name>

Symrefs (symbolic refs)

1symref    := 'ref: ' ref LF
2examples  := '.git/HEAD', '.git/worktrees/<wt>/HEAD'

Refspec (fetch/push)

1refspec    := [force] [neg] src [':' dst]
2force      := '+'
3neg        := '^'                    # fetch-only
4src        := pattern | ref | ''     # '' only when ':' present (delete/matching)
5dst        := ref | ''               # '' allowed for delete/matching
6pattern    := prefix '*' suffix      # exactly one '*'

Semantics:

  • + permits non-fast-forward update.
  • ^ removes matches from earlier positive fetch globs.
  • pattern maps the * substring into dst.
  • : with empty src deletes dst on push.
  • Bare : (no src/dst) is deprecated matching.

Revision-ish / <start-point>

1start-point := rev                               # as used by man pages
2rev         := ref | objid | rev parent | rev '~' [n] | rev '@{' sel '}' | rev peel
3parent      := '^' [n]                           # default n=1
4peel        := '^{}' | '^{' type '}'             # type ∈ {commit, tree, blob, tag}
5objid       := 40-hex | 64-hex                   # SHA-1 or SHA-256
6sel         := integer | date | 'upstream' | 'push'
7shorthand   := '@' == HEAD

Selectors:

  • rev^ / rev^n parent (n-th for merges)
  • rev~n first-parent ancestor
  • rev@{n} n-th reflog entry (0 = current)
  • rev@{<date>} reflog at time
  • rev^{} peel tag to referenced object
  • rev^{tree|commit|blob} peel to type when ambiguous

Storage notes (minimal)

  • Loose refs: files under .git/refs/... (object ID or symref text).
  • Packed refs: .git/packed-refs with optional peeled lines (^<object>).
  • Resolution prefers loose, then packed.

Examples and ready-to-use patterns

  • Track all branches: refs/heads/*:refs/remotes/origin/*
  • Exclude on fetch: ^refs/heads/wip/*
  • Force-push one branch: +refs/heads/topic:refs/heads/topic
  • Delete remote branch: :refs/heads/old-feature
  • Rename tag on push: refs/tags/v1:refs/tags/release-1
  • Parent/ancestor: feature^, feature^2, feature~3
  • Reflog selectors: main@{2}, main@{yesterday}
  • Peel annotated tag: v1.2.3^{} or v1.2.3^{commit}

Quick checklist

  • Only one * per refspec glob.
  • Avoid control chars, space, ~ ^ : ? * [ \\ in refname components.
  • No //, .., .lock, or @{ substrings; no leading/trailing /.
  • Provide a destination when globbing on push; fetch often supplies one for remote-tracking writes.