File states
Beginner Core Git Git CLI
Why this matters
git status describes files in terms of states, and every Git command that touches files moves them from one state to another. Learn the map once and you can predict what any command will do before you run it.
The states
| State | Meaning | git status shows it as |
|---|---|---|
| Untracked | The file exists in the working tree; Git has never been told about it | under Untracked files: |
| Unmodified (tracked, clean) | Tracked, and identical to the last commit | nothing (the file is not mentioned) |
| Modified | Tracked, changed in the working tree, not yet staged | under Changes not staged for commit: |
| Staged | The change has been copied to the staging area and will be in the next commit | under Changes to be committed: |
| Ignored | Matches .gitignore; Git pretends it does not exist |
nothing, unless --ignored |
A file that was staged and then edited again is in two states at once (staged with the older content, modified with the newer), and appears in both groups.
The moves
| From | To | Command | Reverse |
|---|---|---|---|
| Untracked | Staged | git add <file> |
git rm --cached <file> (before the first commit) or git restore --staged <file> |
| Unmodified | Modified | edit the file in any editor | git restore <file> (discards the edit, no undo) |
| Modified | Staged | git add <file> |
git restore --staged <file> (keeps the edit) |
| Staged | Unmodified | git commit |
git revert or git reset on the commit (Section 12) |
| Tracked | Untracked | git rm --cached <file> |
git add <file> |
| Any | Ignored | add a pattern to .gitignore |
remove the pattern |
Two of these are the only ones that lose work: git restore <file> throws away uncommitted edits, and git rm variants remove files. Everything else is reversible.
Deleted and renamed files
Deleting a tracked file with your editor or with rm makes Git report it as deleted in the modified group; staging the deletion (git add <file> works for deletions too, or git rm <file> does both steps) moves it to the staged group:
$ git statusOn branch main
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: docs/faq.md
no changes added to commit (use "git add" and/or "git commit -a")Renaming or moving with git mv stages the rename directly; renaming in the editor produces a deletion plus an untracked file, which Git recognizes as a rename once both are staged:
$ git mv docs/faq.md docs/questions.md
$ git statusOn branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: docs/faq.md -> docs/questions.mdLesson 4.2 practises these.
Ignored files
Anything matching a pattern in .gitignore never appears in git status and is skipped by git add .. The playground ignores __pycache__/, .venv/, .env and editor folders. To see what is being hidden:
$ git status --ignoredIgnored files are still on your disk; Git just looks away. If a file you need is missing from git status, --ignored is the first thing to check.
Common mistakes
- Reading "nothing to commit" as "the file is not tracked". It means every tracked file is unmodified. An untracked file would be listed.
- Using
git restoreto unstage. Without--stagedit discards edits instead. Read the hint lines ingit status; they always name the right form. - Adding a pattern to
.gitignorefor a file that is already tracked. Tracked files stay tracked; rungit rm --cached <file>once, then the pattern applies.
Try it yourself
Goal: move one file through every state and watch git status change.
- In the playground, create
docs/notes.mdwith one line; rungit status -s(untracked,??). git add docs/notes.md; rungit status -s(staged new file,A).git commit -m "docs: add notes"; rungit status -s(nothing: unmodified).- Add a second line to the file;
git status -s(modified,M). git add docs/notes.md; then add a third line;git status -s(both:MM).git restore --staged docs/notes.md, thengit restore docs/notes.md;git status -s(nothing).
Expected result: the short-status codes change from ?? to A, to nothing, to M, to MM, and back to nothing.
Show solution
The two-column short format shows the staging area in the first column and the working tree in the second: A staged new file; M modified only in the working tree; MM staged and modified again; ?? untracked. Step 6 first unstages (keeping the edits), then discards the edits; the file is back to its committed content.