The working tree (also "working copy") is simply the project files as you
see and edit them. Git is not directly tied to them: it sits alongside
them in .git/ and watches.
What "tracked" means
Every file in the working tree is in one of four states from Git's point of view:
- Untracked. The file exists on disk, but Git knows nothing about it. It has never been in the index or a commit.
- Tracked, unmodified. The file is in the repository (in the latest commit) and is identical on disk to that commit.
- Tracked, modified. The file is in the repository, but the version on disk differs from what is in HEAD.
- Tracked, staged. The file's changes have been added to the index
via
git addand are ready to commit.
A file can be both staged and modified at the same time: you staged one
change, then made another. In that case git status shows the file in
both sections simultaneously.
A clean working tree
A "clean" working tree means no modified, no staged, and no untracked
files. git status prints:
nothing to commit, working tree clean
This state matters. Many commands (for example, git pull or
git switch) may refuse to run when the working tree is "dirty" and the
operation would overwrite your changes.
To bring the working tree to a clean state quickly:
- commit the changes (
git add+git commit), - stash them (
git stash), - discard them (
git restore <file>orgit reset --hard).
.gitignore and the working tree
Files listed in .gitignore physically exist in the working tree, but
Git does not show them as untracked and does not offer to add them. This
is useful for build artifacts, local config files, and IDE metadata.
To check why a specific file is being ignored:
git check-ignore -v <file>
# .gitignore:5:node_modules/ node_modules/main.js
Pitfalls
- The working tree is not the same thing as a checkout. A checkout is an operation (replace the working tree's state with the contents of another commit). The working tree is the state itself.
- Bare repositories (
git init --bare) have no working tree at all: only the contents of.git/. They are used for the server side of git push/fetch. - When you switch branches, Git rewrites the working tree with the
contents of the target commit. Unsaved changes can block that. Running
git stashbefore switching solves the problem.