Understanding file changes with git diff
Understanding file changes with git diff
git status tells us which files have changed. git diff tells us how they’ve changed. That’s the difference between knowing a file was modified and seeing exactly which lines were added, deleted, or replaced.
It’s a command you’ll use constantly right before making a commit, to make sure that what you’re about to save is exactly what you think it is.
Reading the git diff output
Before looking at the different variants of the command, we need to understand how to interpret its output. Let’s say we have index.html and we modify it:
git diff
The output looks something like this:
diff --git a/index.html b/index.html
index 8a5b2c1..f3e7d4a 100644
--- a/index.html
+++ b/index.html
@@ -1,7 +1,8 @@
<!DOCTYPE html>
<html>
<head>
- <title>My project</title>
+ <title>My Git project</title>
</head>
<body>
<h1>Hello world</h1>
+ <p>Learning Git from scratch</p>
</body>
</html>
Let’s break down what we see:
--- a/index.htmland+++ b/index.html: the previous version (a) and the new version (b) of the file.@@ -1,7 +1,8 @@: the hunk header.-1,7means the block shown starts at line 1 of the original file and has 7 lines.+1,8means in the new version it also starts at line 1 but now has 8 lines.- Lines in red with
-: removed. - Lines in green with
+: added. - Lines with no prefix: context, they haven’t changed.
With practice you’ll read a diff as fast as you read regular code.
The four main variants
git diff — working directory vs staging area
Without arguments, git diff compares what you have in the working directory with what’s in the staging area (or with the last commit if nothing is staged). In other words: it shows the changes you haven’t staged yet.
git diff
If you’ve run git add on all your changes, this command will show nothing, because the working directory and the staging area are in sync.
git diff —staged — staging area vs last commit
Also called --cached (they’re equivalent). Shows the changes you have staged and that will be included in the next commit:
git diff --staged
This is the one you should run right before git commit to review exactly what you’re about to save. It’s a very good habit: it prevents you from committing things you didn’t mean to include.
git diff HEAD — working directory vs last commit
Compares all your current work (both staged and unstaged) against the last commit. It’s like the sum of the two previous ones:
git diff HEAD
It gives you a complete picture of everything that has changed since the last commit, regardless of whether you’ve staged it or not.
Comparing specific commits
You can pass two commit identifiers to see what changed between them:
git diff f7c5eba e02c63c
You can also use relative references. HEAD~1 is the commit before the current one, HEAD~2 the one before that, and so on:
git diff HEAD~1 HEAD
That shows exactly what changed in the last commit, which is something you’ll do often to review your own work.
Comparing branches
One of the most useful variants of git diff is comparing two branches. Imagine you have a feature branch and you want to see what changes it introduces compared to master:
git diff master..feature
The two dots (..) compare the tip of master with the tip of feature. You’ll see all the changes in feature that aren’t in master.
There’s also the three-dot syntax (...):
git diff master...feature
This variant shows the changes in feature since the point where it diverged from master, ignoring any changes that may have been made in master since then. It’s usually the most useful when working with feature branches and you only want to see what you’ve added.
Filtering by file
If your repository has many modified files and you’re only interested in the changes in one specific file, you can specify it:
git diff index.html
git diff --staged src/styles/main.css
You can also filter by folder:
git diff src/
Seeing only the names of changed files
If you don’t need to see the diff content but just want to know which files changed, use --name-only:
git diff --name-only
git diff --name-only HEAD~1 HEAD
And if you also want to know the type of change (modified, added, deleted), use --name-status:
git diff --name-status master..feature
The output would look like this:
M index.html
A about.html
D old-page.html
Ignoring whitespace changes
Sometimes a diff is full of noise because someone changed the indentation of a file or converted tabs to spaces. To ignore those changes:
git diff -w
The -w flag (or --ignore-all-space) makes Git completely ignore whitespace changes, showing only real content changes.
git difftool: using a graphical tool
The text output of git diff is perfectly functional but it’s not always the most comfortable way to review changes. That’s what git difftool is for — it opens diffs in an external graphical tool.
To see what tools Git has configured on your system:
git difftool --tool-help
To use a specific tool, for example vimdiff:
git difftool --tool=vimdiff
Or to configure it as the default tool:
git config --global diff.tool vimdiff
From that point on, git difftool will open vimdiff directly. Popular editors like VS Code, IntelliJ, or Sublime Merge can also be configured as a difftool, and they offer a very comfortable side-by-side view for reviewing large changes.
With git status, git log, and git diff you have the complete inspection triangle: you know which files have changed, what the project’s history looks like, and exactly what each change consists of. These are the three commands you’ll use most in your day-to-day work with Git and by now you should be comfortable with all of them.
In the next lesson we revisit the topic of recovering files, which we briefly covered at the beginning of the course, but now with much more context and adding the modern alternatives that Git 2.23+ offers.
Never stop coding!