Understanding git status, git log, and git diff
Why These Three Commands Matter
In Part 2, you learned the core cycle of Git: edit, stage, commit. That cycle is how you write your project's history. But writing is only half the story. You also need to read — to understand what's happening in your repository at any given moment.
That's where git status, git log, and git diff come in. Think of them as your three lenses for looking at your project:
- git status — Shows you the present. What's changed right now? What's staged? What's not?
- git log — Shows you the past. What commits have been made? Who made them? When?
- git diff — Shows you the details. Exactly which lines were added, removed, or modified?
Master these three, and you'll never feel lost in a Git repository again.
Setting Up a Practice Project
To follow along, let's create a small project we can experiment with. Open your terminal and run:
mkdir git-reading-practice
cd git-reading-practice
git init
Now let's create a couple of files and make an initial commit:
echo "# Shopping List App" > README.md
echo "milk" > list.txt
git add .
git commit -m "Initial commit: add README and shopping list"
We now have a repository with one commit and two files. Perfect. Let's start reading.
git status — Your Real-Time Dashboard
If you only memorize one Git command, make it this one. git status tells you exactly what's going on in your working directory and staging area right now. It answers the question: "What has changed since my last commit, and what's Git expecting me to do next?"
When Nothing Has Changed
Right after a commit, if you run:
git status
You'll see:
On branch main
nothing to commit, working tree clean
This is the "all clear" message. It means your working directory matches your last commit exactly. No edits, no new files, nothing staged. Everything is in sync.
When You Edit a Tracked File
Let's make a change. Add an item to the shopping list:
echo "eggs" >> list.txt
Now check the status:
git status
Output:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: list.txt
no changes added to commit (use "git add" to track)
Git is telling you several things here:
- list.txt has been modified — you changed it since the last commit.
- The change is not staged — it's in your working directory but hasn't been added to the staging area yet.
- Git helpfully suggests two options: git add to stage the change, or git restore to throw the change away and go back to the last committed version.
When You Create a New File
Let's create a new file:
echo "function addItem() {}" > app.js
Check the status:
git status
Output:
On branch main
Changes not staged for commit:
modified: list.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
app.js
Notice the difference. list.txt shows as modified because Git was already tracking it. app.js shows as untracked because it's brand new — Git has never seen it before. Git won't do anything with untracked files until you explicitly add them.
When You Stage Changes
Let's stage just the modified file:
git add list.txt
Now check status again:
git status
Output:
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: list.txt
Untracked files:
app.js
Now you can see two sections: list.txt has moved to "Changes to be committed" (it's in the staging area, ready for the next commit), while app.js is still untracked and will not be included in the commit.
This is the power of git status. At a glance, you can see exactly what will go into your next commit and what won't.
The Short Format
Once you're comfortable reading the full output, you might prefer the compact version:
git status -s
Output:
M list.txt
?? app.js
Each file gets a two-character code:
- M (left column) — Modified and staged
- M (right column) — Modified but not staged
- A — New file that's been staged
- ?? — Untracked file
- D — Deleted file
The left column represents the staging area, the right column represents the working directory. So M list.txt (M in the left, space in the right) means the file is staged and has no additional unstaged changes. If you saw MM list.txt, it would mean some changes are staged and some aren't — you modified the file again after staging it.
When to Use git status
The short answer: constantly. Run it before staging, after staging, before committing, and after committing. Run it when you're confused. Run it when you're not confused. It's the single fastest way to orient yourself in any Git repository.
git log — Your Project's Complete History
While git status shows the present, git log shows the past. It displays every commit that's been made in your repository, starting with the most recent.
First, let's commit what we have and make a few more commits to build up some history:
git add .
git commit -m "Add eggs to shopping list and create app.js"
echo "bread" >> list.txt
git add .
git commit -m "Add bread to shopping list"
echo "function removeItem() {}" >> app.js
git add .
git commit -m "Add removeItem function to app.js"
Now we have four commits. Let's explore them.
The Default View
git log
Output:
commit d4e5f6a (HEAD -> main)
Author: Your Name <your.email@example.com>
Date: Fri Feb 14 11:00:00 2026
Add removeItem function to app.js
commit c3d4e5f
Author: Your Name <your.email@example.com>
Date: Fri Feb 14 10:45:00 2026
Add bread to shopping list
commit b2c3d4e
Author: Your Name <your.email@example.com>
Date: Fri Feb 14 10:30:00 2026
Add eggs to shopping list and create app.js
commit a1b2c3d
Author: Your Name <your.email@example.com>
Date: Fri Feb 14 10:15:00 2026
Initial commit: add README and shopping list
Each commit entry shows four things: the commit's unique ID (SHA hash), the author, the date, and the commit message. The HEAD -> main label on the latest commit tells you that you're currently viewing the tip of the main branch.
If your history is long, Git will open it in a pager (usually less). Press q to quit, Space to scroll down, and b to scroll back up.
One-Line View
For a quick overview, use:
git log --oneline
Output:
d4e5f6a (HEAD -> main) Add removeItem function to app.js
c3d4e5f Add bread to shopping list
b2c3d4e Add eggs to shopping list and create app.js
a1b2c3d Initial commit: add README and shopping list
This is the view you'll use 90% of the time. It's compact, scannable, and gives you just enough information — the short commit ID and the message.
Limiting the Output
If you only want to see the last few commits, add a number:
git log --oneline -3
This shows only the three most recent commits. Useful when you have hundreds of commits and only care about what happened recently.
Filtering by File
Want to see only the commits that affected a specific file? Add the filename at the end:
git log --oneline -- list.txt
Output:
c3d4e5f Add bread to shopping list
b2c3d4e Add eggs to shopping list and create app.js
a1b2c3d Initial commit: add README and shopping list
This filters the history to show only commits that touched list.txt. Notice that the commit about app.js doesn't appear because it didn't change list.txt. The double dash -- before the filename tells Git that what follows is a file path, not a branch name or option.
Filtering by Author
On a team with multiple developers, you can filter commits by who made them:
git log --oneline --author="Your Name"
This shows only commits by the specified author. Useful for tracking your own work or reviewing a teammate's contributions.
Filtering by Date
You can filter commits by when they were made:
git log --oneline --since="2026-02-01" --until="2026-02-14"
Or use relative dates:
git log --oneline --since="2 weeks ago"
This is helpful for generating changelogs or reviewing what happened during a specific time period.
Filtering by Keyword
Search commit messages for a specific word or phrase:
git log --oneline --grep="shopping"
Output:
c3d4e5f Add bread to shopping list
b2c3d4e Add eggs to shopping list and create app.js
a1b2c3d Initial commit: add README and shopping list
This searches through commit messages and returns only the ones containing the word "shopping." It's case-sensitive by default. Add -i to make it case-insensitive:
git log --oneline --grep="Shopping" -i
Visual Branch Graph
When you start working with branches (covered later in the series), this command becomes incredibly useful:
git log --oneline --graph --all
It draws an ASCII art graph showing how branches diverge and merge. Even with just one branch, it's good practice to know this command exists — it'll become essential later.
git diff — The Microscope
If git status tells you which files changed, git diff tells you exactly what changed inside them. It shows you the precise lines that were added, removed, or modified. This is how you review your work before committing and catch mistakes before they become permanent.
Seeing Unstaged Changes
Let's make a change to our shopping list:
echo "butter" >> list.txt
Now run:
git diff
Output:
diff --git a/list.txt b/list.txt
index 8a7b3c1..f2e4d6a 100644
--- a/list.txt
+++ b/list.txt
@@ -1,3 +1,4 @@
milk
eggs
bread
+butter
This looks intimidating at first, but it's straightforward once you know how to read it. Let's break it down piece by piece:
--- a/list.txt— The old version of the file (before your change).+++ b/list.txt— The new version of the file (after your change).@@ -1,3 +1,4 @@— The location marker. It says: in the old file, starting at line 1, there were 3 lines. In the new file, starting at line 1, there are 4 lines.- Lines starting with a space are unchanged — they're shown for context so you can see where the change happened.
- Lines starting with
+(in green on most terminals) are additions — new lines you added. - Lines starting with
-(in red on most terminals) are deletions — lines you removed.
In this case, the three existing items (milk, eggs, bread) are unchanged, and one new line (+butter) was added. Simple.
A More Complex Diff
Let's make a more interesting change. Open app.js and replace its contents with something new:
printf "function addItem(item) {\n console.log('Added: ' + item);\n}\n\nfunction removeItem(item) {\n console.log('Removed: ' + item);\n}\n" > app.js
Now run git diff:
git diff app.js
You'll see both red lines (the old, simple functions that were removed) and green lines (the new, more detailed functions that replaced them). This is exactly what a code review looks like — you can see at a glance what was changed and evaluate whether the changes are correct.
Notice we added app.js after the command. You can pass a filename to git diff to see changes for just that one file, instead of all changed files at once.
Seeing Staged Changes
Here's a subtlety that trips up many beginners. Once you stage a file with git add, running plain git diff won't show that file's changes anymore. Why? Because git diff compares the working directory to the staging area. Once a change is staged, it's no longer a "difference" between those two locations.
To see what you've staged (what will go into your next commit), use:
git add list.txt
git diff --staged
Output:
diff --git a/list.txt b/list.txt
index 8a7b3c1..f2e4d6a 100644
--- a/list.txt
+++ b/list.txt
@@ -1,3 +1,4 @@
milk
eggs
bread
+butter
The --staged flag (also written as --cached — they're identical) compares the staging area to the last commit. This shows you exactly what your next commit will contain.
The Three Comparisons
This is the key concept to understand with git diff. There are three comparisons you can make:
| Command | What It Compares | When to Use It |
|---|---|---|
git diff |
Working directory vs. staging area | See changes you haven't staged yet |
git diff --staged |
Staging area vs. last commit | Review what you're about to commit |
git diff HEAD |
Working directory vs. last commit | See all changes since your last commit (staged and unstaged combined) |
HEAD is Git's name for "the latest commit on your current branch." So git diff HEAD shows you everything that's different between your current working state and the last time you committed — regardless of whether you've staged anything or not.
Comparing Specific Commits
You can also compare any two commits directly. First, grab the commit IDs from your log:
git log --oneline
Then compare two of them:
git diff a1b2c3d c3d4e5f
This shows every change that happened between those two commits. It's like saying "show me everything that changed between Monday's version and Wednesday's version." Extremely useful when you're trying to understand how a piece of code evolved over time.
You don't need to type the full commit ID — the first 7 characters are almost always enough for Git to identify the commit uniquely.
Comparing with a Previous Commit
A handy shortcut: to compare with the commit before the last one, use the tilde notation:
git diff HEAD~1
This compares your current working directory against the second-to-last commit. HEAD~2 goes back two commits, HEAD~3 goes back three, and so on.
Word-Level Diff
By default, git diff shows changes at the line level. If you changed just one word in a long line, the entire line shows up as removed and re-added, which can make it hard to spot the actual change. For a more granular view:
git diff --word-diff
This highlights the exact words that changed within each line, using [-removed-] and {+added+} markers. It's especially helpful for prose, documentation, or any file where changes happen within long lines rather than across whole lines.
Statistics Only
Sometimes you don't need to see every line — you just want a high-level summary. Use:
git diff --stat
Output:
app.js | 8 +++++---
list.txt | 1 +
2 files changed, 6 insertions(+), 3 deletions(-)
This tells you which files changed, how many lines were added (+) and removed (-), and a visual bar showing the proportion. It's the same summary you see on pull requests on GitHub.
Putting It All Together: A Real Workflow
Here's how these three commands fit into a typical development session. This is the rhythm experienced developers fall into naturally:
- Start working. Edit some files — fix a bug, add a feature, update documentation.
- Check status. Run git status to see which files you've touched.
- Review changes. Run git diff to inspect exactly what you changed. Catch typos, debug leftover console.log statements, or realize you accidentally edited the wrong file.
- Stage selectively. Use git add to stage only the changes related to your current task.
- Review staged changes. Run git diff --staged to double-check what you're about to commit. This is your last chance to catch mistakes.
- Commit. Write a clear, descriptive message and commit.
- Verify. Run git status to confirm everything is clean, and git log --oneline to see your new commit at the top of the history.
This cycle — status, diff, add, diff staged, commit, log — becomes second nature after a few days of practice. It's the rhythm of working with Git.
Quick Reference
| Command | What It Does |
|---|---|
git status |
Shows modified, staged, and untracked files |
git status -s |
Compact status with two-character codes |
git log |
Full commit history with details |
git log --oneline |
Compact one-line-per-commit history |
git log --oneline -5 |
Last 5 commits only |
git log --oneline -- file.txt |
Commits that affected a specific file |
git log --oneline --author="Name" |
Commits by a specific author |
git log --oneline --grep="keyword" |
Commits with a keyword in the message |
git log --oneline --graph --all |
Visual branch graph |
git diff |
Unstaged changes (working directory vs. staging area) |
git diff --staged |
Staged changes (staging area vs. last commit) |
git diff HEAD |
All changes since last commit |
git diff HEAD~1 |
Changes compared to the previous commit |
git diff --word-diff |
Highlights exact words changed within lines |
git diff --stat |
Summary showing files changed and lines added/removed |