skip to content

How do you read the two status columns in git status --short output?

level: middleimportance: should knowfreq 52%

answer

  1. two characters, two comparisons
  2. left is closer to HEAD
  3. a space means nothing on that side
  4. double question marks are their own case

basics

~20 s

In git status --short each path gets two columns: the left column is index versus HEAD (staged), the right is working tree versus index (unstaged). A space means no change on that side, and ?? marks an untracked file.

solid answer

~50 s

`git status --short` (or `-s`) prints one line per path with a two-character status prefix. The **left** character describes the index against HEAD — what is staged. The **right** character describes the working tree against the index — what is not staged. A space in a column means nothing changed on that side. The letters are `M` modified, `A` added, `D` deleted, `R` renamed, `C` copied, `T` type change, and `U` unmerged. So `M ` is a staged modification only, ` M` is an edit you have not staged, and `MM` means you staged an edit and then edited the file again — the commit will capture only the first version. Untracked paths print `??`, and `!!` appears for ignored paths when you pass `--ignored`. For scripting, use `--porcelain`, whose format is stable across versions and unaffected by user configuration.

code

console · 7 lines
console
$ git status --short
M  src/app.js
 M src/util.js
MM README.md
A  src/new.js
 D old.txt
?? build/

go deeper

for a junior

Learn to spot the three cases you meet daily: a letter on the left is staged, a letter on the right is not, and ?? means Git is not tracking the file yet.

for a middle

Explain both columns as the HEAD-to-index and index-to-working-tree comparisons, and decode combinations such as MM, AM and D on either side without hesitating.

for a senior

Show you use the short format as a pre-commit review reflex, and that you reach for --porcelain with -z whenever tooling parses status rather than scraping the human output.

for a principal

Own the convention that automation depends on stable Git output contracts, and push back on scripts and hooks that parse human-facing formats which can shift under a version upgrade.

## The two-column grammar The short format exists because the long `git status` output is three paragraphs of prose about the same two comparisons. Compress those comparisons into two characters per path and the whole repository state fits on one screen. Every line looks like `XY path`. **X** is the difference between HEAD and the index: the staged change. **Y** is the difference between the index and the working tree: the change you have not staged. Reading left to right is reading the three trees from HEAD outwards to your disk. The status letters are shared by both columns: - `M` — modified - `A` — added (a path that exists in the index but not in HEAD) - `D` — deleted - `R` — renamed - `C` — copied - `T` — type changed, for example a regular file replaced by a symlink - `U` — unmerged, meaning a conflict is still unresolved - a space — unchanged on that side ## Common combinations `M ` (letter then space) is the tidy case: you staged an edit and have not touched the file since; committing now records exactly what you see in `git diff --cached`. ` M` (space then letter) is an edit sitting only in the working tree; committing now records nothing of it. `MM` is the trap: content was staged, then the file changed again, so the commit will contain the older snapshot. `A ` is a newly tracked file whose content is staged. `AM` is a new file that was staged and then edited further. `D ` is a staged deletion; ` D` is a file deleted on disk but still present in the index. `??` is special — both columns together mean the path is untracked, present on disk and absent from the index. With `--ignored`, ignored paths appear as `!!`. Conflict states use `U` on one or both sides in combinations such as `UU` (both modified), `AA` (both added), `DU`, `UD`, `AU` and `UA`; those paths carry multiple stage entries in the index until you stage a resolution. A rename prints with an arrow, for example `R old.txt -> new.txt`. Rename detection is a heuristic based on content similarity, not something Git records at `git add` time. ## --short versus --porcelain The two produce the same two-column layout, but they are not interchangeable. `--short` is meant for humans: it honours user configuration such as `status.relativePaths` and colour settings, so its exact output can differ between machines and Git versions. `--porcelain` is the scripting contract: version 1 of that format is guaranteed stable, colour is off, and paths are always relative to the repository root. Add `-z` to get NUL-terminated records so that filenames containing spaces, quotes or newlines survive parsing. `--porcelain=v2` is a richer line-oriented format that also carries file modes and object ids, plus optional branch headers with `--branch`. ## Practical use The habit worth building is scanning the left column before every commit. If a line you expect to ship shows a space on the left, it will not be in the commit; if you see `MM`, decide whether the second edit belongs in this commit or the next one. Untracked `??` lines are the other pre-commit check: a build artefact appearing there is a signal to fix your ignore rules rather than to stage it.

  • Why should a script parse --porcelain instead of --short?
    `--short` is a human format: it respects user config such as relative paths and colour, so its output varies by machine and version. `--porcelain` is a stability contract — fixed format, no colour, paths relative to the repository root — and `-z` gives NUL-terminated records so filenames containing spaces or newlines parse safely.
  • What does the line MM src/app.js tell you, and what will the commit contain?
    You staged a modification and then modified the same file again. The commit records only the staged version, because git commit writes the index. Run `git diff` to see the unstaged remainder and `git add` again if it belongs in this commit.

saying these in an interview costs you the question

  • Reads the right column as the staged change
  • Thinks a leading space means the file is unchanged
  • Treats ?? as a merge conflict marker
  • Assumes --short and --porcelain are interchangeable in scripts

context