Read git status Without Misreading the Codes
The two-letter codes in git status --short look cryptic until you know the trick, and then they never confuse you again. You will finish with a reliable way to inspect a Git worktree, tell staged changes from unstaged ones, find untracked files, and pick a stable output format for scripts. The examples match Git 2.43.0, the version installed on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a Git repository and an ordinary shell account; none of the checks below needs sudo.
Checkpoint
This guide uses git status as an inspection command. It does not add, commit, discard or delete anything. Treat any command containing git add, git restore, git reset or git clean as a separate, state-changing decision.
1. Confirm the repository and Git version
Change to the repository you want to inspect, then ask Git for its version and repository state:
$ cd /path/to/repository
$ git --version
git version 2.43.0
$ git status
The final command uses the default long format. A clean repository normally reports:
On branch main
nothing to commit, working tree clean
- Branch name may differ, and a repository without an upstream may omit tracking information.
- Not a repository? Check the path before trying a different command.
- Running from a subdirectory is fine. Displayed paths are normally relative to that directory, handy for copying a path straight into a command.
2. Understand what status is comparing
Git status compares three places: the current HEAD commit, the index, and the working tree. The index is also called the staging area. A change in the index is prepared for the next commit. A change only in the working tree is not prepared yet. An untracked path exists on disk but is not in the index, unless an ignore rule hides it.
This explains a common surprise: one file can appear twice, because status is describing two separate comparisons, not contradicting itself. The first copy of its change is staged, while a later edit is unstaged.
Try a small, reversible test in a disposable repository if the two comparisons are unfamiliar:
$ printf 'first line\n' > example.txt
$ git add example.txt
$ printf 'second line\n' >> example.txt
$ git status --short
Expected output is similar to this:
AM example.txt
The first column, A, says the index contains an added file. The second column, M, says the working tree differs from the index. Remove the disposable repository afterwards with your normal, careful file-management process. Do not use a broad cleanup command in a real worktree just to make status look clean.
3. Use short output for a quick review
Use --short, or its -s spelling, when the long explanatory text would distract from the paths:
$ git status --short
AM example.txt
?? notes.txt
The two characters are conventionally called XY: X describes the index, Y describes the working tree. The useful cases:
| Code | Meaning |
|---|---|
M | Modified in the index, with no later worktree change. |
M | Modified in the worktree, but not staged. |
MM | Modified once in the index and again in the worktree. |
A | Added to the index. |
D | Deletion staged in the index. |
?? | Untracked and not ignored. |
!! | Ignored, but visible because --ignored was used. |
A blank position is meaningful, so copy the two columns exactly when documenting a state.
Warning
During a conflict, codes containing U indicate an unmerged path. Stop before committing and resolve the conflict using the project workflow; status is reporting the problem, not selecting the correct side for you.
4. Inspect branch and stash information
Add --branch to short output when branch or upstream details matter:
$ git status --short --branch
## main...origin/main [ahead 1]
M src/config.ini
The exact tracking line depends on your branch and remote. The ahead and behind counts show when an upstream and the relevant commits are available. To include the number of stash entries, use the newer explicit option shown in the installed manual:
$ git status --short --branch --show-stash
## main...origin/main
# stash 2
The stash line is omitted when the count is zero. Neither command changes the branch, remote, index or stash.
5. See untracked and ignored paths deliberately
By default, Git shows untracked files and directories but not ignored paths, which stops build output and editor caches from overwhelming a normal review. Use the option explicitly when checking whether a new file is being hidden:
$ git status --short --ignored
!! build/
?? draft-notes.txt
- Default ignored display mode is
traditional. - More detail inside untracked directories:
--untracked-files=all. - Speed in a very large worktree:
--untracked-files=nosuppresses untracked paths.
$ git status --short --untracked-files=no
M src/config.ini
That faster output is incomplete by design: it can hide a newly created file you forgot to add, so use it only when you have another way to account for new files. The option must be attached when using the short form, as in -uno; -u no is not the documented spelling.
6. Choose porcelain output for scripts
Do not parse the human long format: its contents can change, and even ordinary short output can be affected by user configuration. Use porcelain output instead:
$ git status --porcelain=v1
M src/config.ini
?? draft-notes.txt
Porcelain version 1 keeps the short-style status while disabling colour and making paths relative to the repository root. For a parser that must preserve arbitrary filenames, use NUL-terminated output:
$ git status --porcelain=v1 -z | od -An -t x1
20 4d 20 73 72 63 2f 63 6f 6e 66 69 67 2e 69 6e
69 00
Do not copy the displayed hexadecimal bytes as a filename result; the important check is the 00 terminator. With -z, Git does not quote unusual characters, and rename paths use NUL separation, so a shell loop should read NUL records rather than split on lines.
Porcelain version 2 provides more machine-readable detail, including optional branch headers when --branch is supplied. Its changed-entry records are more structured, but parsers must still accept records in an undefined order and ignore headers they do not recognise.
Common traps
- Untracked search is slow in a big worktree. Run ordinary status again first: an existing cache may already make the second run faster.
- Untracked files not relevant? Use
--untracked-files=no, and remember the result is partial. - Untracked cache and FSMonitor can help repeated work in a large repository, but they affect Git's index or configuration, so do not enable them blindly on a managed checkout; check the repository's contribution or operations guidance first.
- Lock collisions in background scripts: a status command can refresh cached index stat information and write the index as an optimisation.
git --no-optional-locks statusavoids that, provided you accept the limitations of skipping optional index writes. - Submodules hide detail by default. Use
--ignore-submodules=nonewhen you need Git to consider untracked content, modified content and a changed submodule commit; this can be slow and noisy, so use it for a focused check rather than every prompt.
Done means
- Confirmed the repository and installed Git version.
- Distinguished index from worktree, reading column
Xagainst columnY. - Checked untracked and ignored paths when a clean-looking result needed proof.
- Used porcelain output (
--porcelain=v1or version 2) instead of parsing human output in scripts. - Knew the tradeoff:
--untracked-files=nomakes status faster by hiding information. - Changed nothing while inspecting the repository.