The Git workflow is the cycle every developer follows when working with Git — from making changes to recording them permanently in the project history. Understanding this cycle is the bridge between knowing what Git is and actually using it day to day.
The workflow maps directly onto the three sections covered in How Git Works: the working directory, the staging area, and the Git directory. Every action you take in Git moves work between these three places.
The Core Cycle
At its most fundamental, the Git workflow repeats the same cycle for every piece of work:
Modify → Stage → Commit
- Modify — make changes to files in your working directory.
- Stage — select which changes to include in the next snapshot using
git add. - Commit — permanently record the staged snapshot into the Git directory using
git commit.
This cycle then repeats. Each iteration produces a new commit — a permanent, recoverable snapshot of your project at that moment.
Checking Status
Before doing anything, it helps to know the current state of your working directory and staging area. The git status command shows you exactly that:
git status
A clean working directory — nothing modified, nothing staged — looks like this:
On branch main
nothing to commit, working tree clean
When you add a new file, Git immediately flags it as untracked:
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
Git does not track files automatically. Every file starts untracked. You must explicitly tell Git to start tracking it.
Staging Changes — git add
git add is the command that moves changes from the working directory into the staging area. It is more accurately described as "add this content to the next commit" rather than "add this file to the project."
git add README.md # Stage a specific file
git add src/ # Stage all files in a directory
git add . # Stage all changes in the current directory
After running git add, the file moves from untracked (or modified) to staged:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README.md
An Important Subtlety
git add stages the file as it exists at the moment you run the command. If you stage a file and then edit it again before committing, the new edits are not staged — only the version from when you ran git add will go into the commit. You would need to run git add again to stage the latest version.
This is not a bug — it is intentional design. The staging area gives you precise control over exactly what snapshot gets committed.
Tracked vs. Untracked Files
Every file in your working directory is in one of two states:
| State | What It Means |
|---|---|
| Tracked | Git knows about this file. It was in the last snapshot. It can be unmodified, modified, or staged. |
| Untracked | Git sees the file but has never been told to include it. It won't appear in commits until you git add it. |
When you first clone a repository, all files are tracked and unmodified. As you edit them, they become modified. As you stage them, they become staged. As you commit, they become committed and unmodified again — and the cycle starts over.
Committing — git commit
Once your staging area contains exactly what you want to record, you commit it:
git commit -m "Add README with project description"
The -m flag lets you write the commit message inline. Without it, Git opens your configured text editor for you to write the message there.
A commit records:
- The snapshot of everything currently in the staging area.
- The author name and email from your
git config. - The timestamp.
- The commit message.
- A SHA-1 hash that uniquely identifies this commit.
After committing, Git reports a summary:
[main 463dc4f] Add README with project description
1 file changed, 3 insertions(+)
create mode 100644 README.md
Anything that was not staged stays in the working directory as modified — it is not lost, just not included in this commit. You can commit it separately.
Writing Good Commit Messages
A commit message is a record for your future self and your collaborators. The widely accepted convention is:
- Subject line: 50 characters or fewer, written in the imperative mood — "Fix bug" not "Fixed bug" or "Fixes bug."
- Body (optional): A blank line followed by a detailed explanation of why the change was made, not just what changed. The diff already shows what changed.
Add user authentication to login route
Previously, the login route had no authentication check, allowing
any request to proceed. This adds JWT verification middleware to
ensure only valid tokens can access the endpoint.
Good commit messages make git log readable and make debugging much easier when you need to understand why a decision was made months later.
Skipping the Staging Area
For simple, routine commits where you want to stage and commit all tracked changes in one step, Git provides a shortcut:
git commit -a -m "Update project dependencies"
The -a flag automatically stages every tracked file that has been modified before committing. Note that it only works on files Git already tracks — untracked files are still ignored and must be git add-ed first.
Use this shortcut carefully. The staging area exists precisely so you can be deliberate about what goes into each commit. Bypassing it entirely can lead to commits that include unintended changes.
The .gitignore File
Not every file in your project should be tracked by Git. Build artifacts, log files, dependency folders, and environment files containing secrets should never be committed. The .gitignore file tells Git which files and patterns to completely ignore.
Create a .gitignore file in the root of your repository:
# Dependencies
node_modules/
# Build output
dist/
build/
# Environment variables
.env
.env.local
# Logs
*.log
# OS files
.DS_Store
.gitignore Rules
| Pattern | What It Ignores |
|---|---|
*.log | All files ending in .log |
build/ | The entire build directory |
/TODO | Only the TODO file in the root, not in subdirectories |
!lib.a | Track lib.a even if other .a files are ignored |
doc/**/*.pdf | All .pdf files inside doc/ and any subdirectory within it |
Setting up .gitignore before your first commit is good practice. If you accidentally track a file that should be ignored, removing it from tracking requires extra steps.
GitHub maintains a comprehensive collection of .gitignore templates for most languages and frameworks at https://github.com/github/gitignore.
Viewing Differences — git diff
git status tells you which files have changed. git diff tells you what changed — the exact lines added and removed.
git diff # Changes in working directory not yet staged
git diff --staged # Changes staged and ready to commit
The output uses a standard diff format:
- Lines prefixed with
+are additions. - Lines prefixed with
-are removals. - Unchanged lines provide context.
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.
git diff is particularly useful before staging to review exactly what you are about to add to a commit.
A Complete Workflow Example
Here is the full cycle from start to a committed change:
# 1. Check the current state
git status
# 2. Make changes to your files (in your editor)
# 3. Review what changed
git diff
# 4. Stage the changes you want
git add src/app.js
git add README.md
# 5. Confirm what is staged
git status
# 6. Commit with a descriptive message
git commit -m "Add homepage route and update documentation"
# 7. Back to a clean state — the cycle repeats
git status
# nothing to commit, working tree clean
Summary
| Command | What It Does |
|---|---|
git status | Shows the current state of the working directory and staging area |
git add <file> | Stages a file for the next commit |
git add . | Stages all changes in the current directory |
git diff | Shows unstaged changes line by line |
git diff --staged | Shows staged changes that are about to be committed |
git commit -m "msg" | Creates a commit with the staged snapshot |
git commit -a -m "msg" | Stages all tracked changes and commits in one step |