Last updated: 2026-05-17

Tracking and Staging Changes

Once you have a repository, the next thing to understand is how Git monitors the files inside it. Not every file in your project is automatically tracked — Git requires you to be deliberate about what it watches and what goes into each commit. That deliberateness is by design, and understanding it is what separates developers who use Git confidently from those who feel like they're guessing.


The File Lifecycle

Every file in your working directory is in one of two broad states: tracked or untracked.

  • Tracked files are files Git knows about — they were part of the last snapshot. A tracked file can be in one of three substates: unmodified, modified, or staged.
  • Untracked files are everything else — files Git sees in the directory but has never been told to include.

When you first clone a repository, all files are tracked and unmodified. As you edit them, they become modified. When you run git add, they become staged. When you commit, they become unmodified again — and the cycle repeats.

Untracked → Staged → Committed (Unmodified)
                ↑
Unmodified → Modified → Staged

Checking File Status — git status

git status is the command you will run more than any other. It shows you which files are in which state at any given moment.

A clean working directory looks like this:

git status
On branch main
nothing to commit, working tree clean

When you create a new file, Git immediately notices it but does not track it:

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    app.js

Git is explicit: it sees the file but will not include it in any commit until you tell it to.

Short Status

The full git status output can be verbose. For a compact view, use the -s or --short flag:

git status -s
 M README.md
MM app.js
A  styles.css
?? notes.txt

The output uses two columns. The left column reflects the staging area; the right column reflects the working directory.

SymbolMeaning
??Untracked file
ANew file added to the staging area
MModified file
MMModified, staged, then modified again — has both staged and unstaged changes
DDeleted file

Staging Files — git add

git add is a multipurpose command. It serves three distinct purposes:

  1. Begin tracking a new file — tells Git to start watching an untracked file.
  2. Stage a modified file — queues an already-tracked file's changes for the next commit.
  3. Mark merge conflicts as resolved — covered in the branching topics.

The most accurate mental model for git add is not "add this file to the project" but rather "add precisely this content to the next commit."

Staging a single file

git add app.js

Staging multiple files

git add app.js styles.css README.md

Staging a directory (all files inside it recursively)

git add src/

Staging everything in the current directory

git add .

After staging, git status moves the file under "Changes to be committed":

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file: app.js

The staging gotcha

git add captures the file as it exists at the moment you run the command. If you stage a file and then edit it again, git status will show the file in both sections — staged (the version from when you ran git add) and modified (the newer unsaved version):

Changes to be committed:
    modified: app.js

Changes not staged for commit:
    modified: app.js

If you commit at this point, the older staged version goes into the commit — not the current one. To include the latest edits, run git add again before committing.


Removing Files — git rm

To stop tracking a file and remove it from the project entirely, use git rm. This removes the file from both the staging area and the working directory:

git rm PROJECTS.md

After the next commit, the file will be gone from the repository and will no longer appear as untracked.

If you simply delete a file from your filesystem without using git rm, Git will notice the deletion and show it as an unstaged change — but it won't be included in the commit until you stage the removal explicitly.

Keeping the file on disk but removing it from tracking

Sometimes you accidentally staged a file that should be ignored — a large log file, a compiled output, or a credentials file. To untrack it without deleting it from your computer:

git rm --cached .env
git rm --cached -r node_modules/

This removes the file from Git's tracking but leaves it in your working directory. After this, add it to .gitignore so it doesn't get accidentally staged again.


Renaming and Moving Files — git mv

Git does not explicitly track file movement. However, it provides git mv as a convenience command for renaming files:

git mv old-name.js new-name.js

Running git status after this shows:

Changes to be committed:
    renamed: old-name.js -> new-name.js

Under the hood, git mv is equivalent to three separate commands:

mv old-name.js new-name.js
git rm old-name.js
git add new-name.js

Git figures out the rename from the combination of a deletion and an addition. Either approach works — git mv is simply faster.


Ignoring Files — .gitignore

Some files should never be tracked: dependency folders, build output, log files, environment files containing secrets, and operating system metadata. The .gitignore file tells Git to completely ignore these files — they won't appear as untracked and won't be accidentally staged.

Create a .gitignore file in the root of your repository:

# Dependencies
node_modules/

# Build output
dist/
.next/

# Environment files
.env
.env.local
.env.*.local

# Logs
*.log
npm-debug.log*

# OS files
.DS_Store
Thumbs.db

.gitignore pattern rules

PatternWhat It Matches
*.logAll files ending in .log anywhere in the project
build/The entire build directory
/TODOOnly TODO in the root directory, not subdirectories
doc/*.txt.txt files directly inside doc/, but not nested deeper
doc/**/*.pdfAll .pdf files inside doc/ and any subdirectory within it
!lib.aTrack lib.a even though other .a files are ignored
#Comment — ignored by Git

Important: .gitignore only works on untracked files

If a file has already been committed, adding it to .gitignore will not stop Git from tracking it. You need to explicitly untrack it first:

git rm --cached filename

Then add it to .gitignore and commit the change. From that point on, Git will ignore it.

.gitignore templates

GitHub maintains a collection of ready-made .gitignore templates for most languages and frameworks at https://github.com/github/gitignore. Starting from one of these is strongly recommended — they cover all the common files for a given stack that beginners might not think to exclude.


Summary

CommandWhat It Does
git statusShows the state of all files in the working directory and staging area
git status -sCompact version of git status
git add <file>Stages a file for the next commit
git add .Stages all changes in the current directory
git rm <file>Removes a file from tracking and deletes it from the working directory
git rm --cached <file>Removes a file from tracking without deleting it from disk
git mv <old> <new>Renames or moves a file and stages the change