git log --pretty=format: Custom Output in CI
git log --pretty=format:"%H %an %s" prints only the fields you name using placeholders, the right way to get machine-readable commit data instead of parsing the default layout.
Pipelines that build changelogs or extract commit metadata should never parse the human git log output. --pretty=format with explicit placeholders gives a stable, scriptable result; -z and %x00 make it safe to split.
What it does
git log --pretty=format:<string> renders each commit using placeholders: %H full SHA, %h short SHA, %an/%ae author name/email, %ad author date, %s subject, %b body, %d ref names. %x00 inserts a NUL for unambiguous field splitting. Combined with --no-merges, range args, and -n it produces exactly the slice a job needs.
Common usage
git log --pretty=format:'%h %an %s' -n 20
# changelog since the last tag
git log --pretty=format:'- %s (%an)' v1.4.0..HEAD --no-merges
# NUL-separated fields for safe parsing
git log --pretty=format:'%H%x00%an%x00%s' -z
# ISO dates
git log --pretty=format:'%h %ad %s' --date=shortOptions
| Placeholder / flag | What it does |
|---|---|
| %H / %h | Full / abbreviated commit SHA |
| %an / %ae | Author name / email |
| %ad / %cd | Author / committer date (see --date) |
| %s / %b | Subject / body of the message |
| %x00 | Insert a NUL byte as a field separator |
| --date=<fmt> | Format dates (short, iso, unix, relative) |
In CI
git log starts a pager when attached to a terminal; in CI it is not, so it runs straight through, but add --no-pager (or set GIT_PAGER=cat) if a wrapper allocates a TTY and the job hangs. Use %x00 with -z and split on NUL so commit subjects containing spaces or quotes do not corrupt your parser. A range like v1.4.0..HEAD needs that tag present (not shallow).
Common errors in CI
"fatal: your current branch 'main' does not have any commits yet" means an empty repo. "fatal: ambiguous argument 'v1.4.0..HEAD'" means the tag is missing (shallow clone, tags not fetched). A hang with no output is almost always a pager on an allocated TTY; use --no-pager.
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached