# git blame --porcelain and -L: Machine-Readable Blame

> git blame --porcelain gives stable machine-readable output, -L scopes to line ranges, and -C/-M follow moved code. Reference for the flags CI tooling parses.

Source: https://latchkey.dev/learn/command-reference/git-blame-porcelain  
Updated: 2026-06-30

git blame --porcelain emits a stable, parseable format (one block per line with author, time, and SHA), while -L<start>,<end> scopes blame to a line range.

Automated ownership and regression tooling parses blame, and the human format is not stable. --porcelain (and --line-porcelain) give a documented machine format; -L, -C/-M, and --ignore-revs-file refine what it reports.

## What it does

git blame annotates each line of a file with the commit that last changed it. --porcelain outputs a stable format suitable for scripts: a header line with SHA and line numbers, followed by author/committer/summary fields. -L limits to a line range. -C/-M detect copied/moved lines. --ignore-revs-file skips noise commits (like a mass reformat).

## Common usage

```Terminal
git blame --porcelain -L 40,60 src/app.js
# follow code moved between files
git blame -C -M src/app.js
# one record per line (easiest to parse)
git blame --line-porcelain src/app.js
# ignore a bulk-reformat commit
git blame --ignore-revs-file .git-blame-ignore-revs src/app.js
```

## Options

| Flag | What it does |
| --- | --- |
| --porcelain | Stable machine-readable output (dedupes commit headers) |
| --line-porcelain | Like --porcelain but full header per line |
| -L <start>,<end> | Restrict to a line range |
| -C / -M | Detect copied / moved lines across or within files |
| --ignore-revs-file <f> | Skip commits listed in the file |
| -w | Ignore whitespace changes when assigning blame |

## In CI

Blame needs history: on a shallow clone, lines from before the boundary are attributed to the boundary commit, giving wrong owners, so fetch-depth: 0 for ownership tooling. Commit a .git-blame-ignore-revs file and reference it (or set blame.ignoreRevsFile) so mass-reformat commits do not pollute blame in every job.

## Common errors in CI

"fatal: no such path '<file>' in HEAD" means the path is wrong or absent at that commit. "fatal: file <x> has only N lines" means an -L range exceeds the file. Blame pointing everything at one recent commit is the classic shallow-clone symptom: unshallow the clone.

## 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.

```.github/workflows/ci.yml
- 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
```

> `git rev-parse --abbrev-ref HEAD` returns the literal string `HEAD` on a detached checkout rather than a branch name. On GitHub Actions read `github.ref_name` instead; the git command cannot know what it was checked out for.

## FAQ

### git blame --porcelain and -L: Machine-Readable Blame?

Automated ownership and regression tooling parses blame, and the human format is not stable. --porcelain (and --line-porcelain) give a documented machine format; -L, -C/-M, and --ignore-revs-file refine what it reports.

### What it does?

git blame annotates each line of a file with the commit that last changed it. --porcelain outputs a stable format suitable for scripts: a header line with SHA and line numbers, followed by author/committer/summary fields. -L limits to a line range. -C/-M detect copied/moved lines.

### In CI?

Blame needs history: on a shallow clone, lines from before the boundary are attributed to the boundary commit, giving wrong owners, so fetch-depth: 0 for ownership tooling. Commit a .git-blame-ignore-revs file and reference it (or set blame.ignoreRevsFile) so mass-reformat commits do not pollute blame in every job.

### Common errors in CI?

"fatal: no such path '<file>' in HEAD" means the path is wrong or absent at that commit. "fatal: file <x> has only N lines" means an -L range exceeds the file. Blame pointing everything at one recent commit is the classic shallow-clone symptom: unshallow the clone.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
