Preview Git Changes
Learn to preview changes with git diff and git difftool — check modifications, stage diffs, and compare branches before committing.
Focus: preview changes with git diff and git difftool
You've made a few edits, saved the file, and now you're about to commit — but are you sure you're committing exactly what you intended? Rushed commits are a leading source of git headaches: accidental debugging lines, missed whitespace, or staging the wrong file. The git diff and git difftool commands are your safeguard, giving you a precise, staged-forward preview of every change before it becomes part of your history. In this lesson, you'll learn to use these commands to inspect working directory changes, review staged changes, and compare branches — turning guesswork into confident, deliberate commits.
The problem this lesson solves
Picture this: you've been working on a feature for an hour. You type git add ., then git commit -m "feat: update user profile" — done, right? But later you discover the commit included a debug console.log, accidentally removed a blank line that broke a style guide, or even staged a file you meant to leave alone. Without previewing, every commit is a small gamble.
Commits are your project's history — the story you and your team will read later. A commit with unintended changes makes that story confusing and occasionally breaks the build. The solution isn't more discipline; it's a better review step. git diff and git difftool let you inspect changes in detail before you commit, so you can catch errors, verify intent, and keep your history clean.
By the end of this lesson, you'll be able to preview changes with git diff and git difftool — checking what's modified, what's staged, and how branches differ — and you'll have a mental model that makes these commands second nature.
Core concept / mental model
Think of your working copy, the index (staging area), and your last commit as three layers of a stack. git diff is a lens that shows you exactly what differs between two layers. It doesn't change anything — it only reports.
git diff(no arguments): shows unstaged changes — differences between your working directory and the index (staged snapshot). This is your "what have I changed but not staged yet" view.git diff --staged(or--cached): shows staged changes — differences between the index and the HEAD (last commit). This is your "what will go into the next commit" view.git diff <branch1> <branch2>: shows differences between two branches — a way to compare entire lines of history.git difftool: launches an external visual diff tool (like VS Code, Meld, or KDiff3) for a side-by-side or word-level comparison, which can be easier for large or complex changes.
The key idea: always preview before you commit. A quick git diff takes seconds but saves minutes of cleanup and avoids polluting history.
How it works step by step
1. Check the status first
Always start with git status to see which files are modified, staged, or untracked. This gives you a high-level map, and then you know which diff commands to run.
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: app.js
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
2. Preview unstaged changes
Run git diff to see exactly what's different in your working directory compared to the index. This shows added lines (prefixed with +) and removed lines (prefixed with -) in a unified diff format.
$ git diff
diff --git a/app.js b/app.js
index 2f2c1d1..9bf3a7e 100644
--- a/app.js
+++ b/app.js
@@ -10,7 +10,8 @@ function greet(name) {
console.log(`Hello, ${name}!`);
}
-// old comment
+// new comment about greeting
+// Added debug log
console.log(greeting);
3. Preview staged changes
After you run git add, the file moves to the index. To see what's staged (what will actually be committed), use git diff --staged.
$ git add app.js
$ git diff --staged
Now the output shows only the staged changes — the ones that became part of the next commit. Unstaged changes are not shown.
4. Compare differences against a branch or commit
You can also diff against a specific branch, tag, or commit hash to see how your current state differs from another point in history. This is useful before merging or when investigating what a colleague changed.
$ git diff main feature/login
$ git diff HEAD~2 HEAD # changes from two commits ago to the last commit
$ git diff v1.0.0..v1.1.0 # compare two tags
5. Launch a visual tool with git difftool
If the text diff is too dense, run git difftool to open your configured visual diff tool. First, set your preferred tool (this example uses VS Code):
$ git config --global diff.tool vscode
$ git config --global difftool.vscode.cmd 'code --wait --diff "$LOCAL" "$REMOTE"'
Then just run:
$ git difftool
Git will open your tool for each changed file, and you can review changes side-by-side, with syntax highlighting and easy navigation.
Hands-on walkthrough
Let's practice in a fresh repository so you can see the commands in action.
Setup a test repo
mkdir demo-diff && cd demo-diff
git init
echo "def add(a, b):" > calc.py
echo " return a + b" >> calc.py
git add calc.py
git commit -m "initial commit"
Modify the file
# Make some edits
echo "def add(a, b):" > calc.py
echo " # debug: print calculation" >> calc.py
echo " return a + b" >> calc.py
echo " # extra comment" >> calc.py
git diff
Expected output (abbreviated):
diff --git a/calc.py b/calc.py
index 1f3f4c3..d0f6b2a 100644
--- a/calc.py
+++ b/calc.py
@@ -1,3 +1,4 @@
def add(a, b):
+ # debug: print calculation
return a + b
+ # extra comment
The + lines show what you added. Notice the debug comment — you probably don't want that in production!
Stage and review staged diff
# Remove the debug line and stage the file
git checkout -- calc.py # revert to HEAD (safe here, but careful — use git restore in real work)
# Add a legitimate feature: a sub function
echo "def add(a, b):" > calc.py
echo " return a + b" >> calc.py
echo "" >> calc.py
echo "def multiply(a, b):" >> calc.py
echo " return a * b" >> calc.py
git add calc.py
git diff --staged
Expected output:
diff --git a/calc.py b/calc.py
index 1f3f4c3..b3e4c5d 100644
--- a/calc.py
+++ b/calc.py
@@ -1,3 +1,6 @@
def add(a, b):
return a + b
+
+def multiply(a, b):
+ return a * b
Now you see exactly what will be committed. If you had a stray debug line, you'd catch it here — just edit the file again and re-stage.
Use difftool (optional)
If you have a visual tool configured, try git difftool --staged to see the same changes with a GUI. Otherwise, the plain git diff is always reliable.
Compare options / when to choose what
| Command / Option | Shows | Best For |
|---|---|---|
git diff |
Unstaged changes (working vs index) | Quick check before git add |
git diff --staged |
Staged changes (index vs HEAD) | Review before git commit |
git diff HEAD |
Both staged and unstaged changes combined | Full picture of uncommitted work |
git diff <branch> |
Differences between current branch and another | Preparing for merge |
git difftool |
Same as above but in a visual tool | Large/complex diffs, side-by-side review |
git diff --word-diff |
Inline word-level changes | Spotting small edits in long lines |
git diff --stat |
Summary of files and line counts | Quick overview |
Pro tip: Use
git diff --statfirst to see a compact summary. If the file list matches your intent, drill intogit difffor details. This saves you from drowning in large diffs when you only need a high-level check.
Troubleshooting & edge cases
git diffshows nothing but you know you changed a file — If the file is modified butgit diffis empty, it's because the changes are already staged (you rangit add). Usegit diff --stagedto see them. Alternatively,git diff HEADshows all changes, staged or not.git diffshows changes you didn't expect — This often happens with line-ending differences (CRLF vs LF). Add a.gitattributesfile or configurecore.autocrlfto handle cross-platform consistency.- Binary files show as "Binary files differ" — For images, PDFs, or compiled files, a text diff isn't useful. Use
git diff --statto see that they changed, and consider usinggit difftool --tool=...to open them in an appropriate viewer. git difftoolprompts for each file and can interrupt flow — You can disable the prompt withgit config --global difftool.prompt false. The--no-promptflag also works.- You accidentally ran
git checkout -- <file>and lost changes — This is a common mistake. If you haven't committed, there's no built-in undo. Usegit stashor commit early to avoid data loss.git restoreis the modern, more explicit alternative. - Large diffs are overwhelming — Use
git diff -U0to suppress context lines, orgit diff --color-wordsto highlight only the changed words, which makes review faster.
What you learned & what's next
You now have the ability to preview changes with git diff and git difftool — you can inspect unstaged and staged changes, compare branches, and use a visual tool for complex diffs. You've also practiced the discipline of reviewing before committing, which is the foundation of clean history. This directly supports the next lesson where you'll likely merge branches or rewrite history with git rebase — with a clear view of what's changing, merge conflicts and rebase amends become much easier to handle.
Remember:
git diffis your safety net. Run it, review, then commit. Your future self and your teammates will thank you.
Practice recap
Create a new repository, make a few edits, and practice the full preview workflow: git status, git diff, git add, then git diff --staged. Try comparing two branches with git diff <branch1> <branch2>. Bonus: configure git difftool with your favorite editor and run git difftool --staged to see the visual difference.
Common mistakes
- Running
git addfirst, thengit diff— and seeing nothing because changes are already staged. Usegit diff --stagedto review staged changes, orgit diff HEADto see everything. - Assuming
git diffshows all uncommitted changes — but untracked files are invisible. Rungit statusto see them andgit add+git diff --stagedto preview them. - Using
git checkout -- <file>to discard changes without having a backup — you can lose work permanently. Prefergit stash(with a message) or commit first if there's any doubt. - Ignoring line-ending changes (CRLF vs LF) that clutter diffs — configure
core.autocrlfor add a.gitattributesfile to keep diffs clean. - Forgetting that
git difftoolonly works if you've configured a tool — rungit difftool --tool-helpto see available options and set one up.
Variations
- Use
git diff --cachedas an alias for--staged— both work, so pick one and stay consistent. - Use
git difftool --dir-diffto open all changed files in a directory-level comparison, which is great for batch review. - Integrate a modern IDE's built-in diff view (VS Code, PyCharm, etc.) — often more comfortable than a terminal diff, but you can still use the same underlying
git diffcommands.
Real-world use cases
- Reviewing a pull request: run
git diff main...featureto see exactly what will be merged, catching unintended changes before approval. - Debugging a regression: use
git diff HEAD~5 HEAD -- app.jsto inspect recent changes to a specific file and find the culprit commit. - Auditing before release: run
git diff --staged(orgit diff v1.0.0..v1.1.0) to verify the exact set of files and changes in a release commit, ensuring only intended changes ship.
Key takeaways
git diffshows unstaged changes;git diff --stagedshows what will be committed — always preview both before committing.git diff HEADcombines staged and unstaged changes — use it for a full picture of uncommitted work.git diff <branch>or<commit1> <commit2>lets you compare different points in history, useful for merges and audits.git difftoolopens a visual diff tool for complex changes; configure it once to speed up reviews.- Always run
git statusfirst — it tells you which diff command to use and reveals untracked files that won't appear in diffs.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.