Skip to content
Ameen Abozaid
PublishedBEG-046-L03

History and diffs

  • Lesson 3 of 8
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

You can now record snapshots. This lesson is about reading them back. Three commands do almost all of the looking: git status (what is the state right now), git log (what has been recorded), and git diff (what exactly changed). Together they answer the questions every developer asks many times a day: "What did I just change?", "What is about to go into my commit?", and "What happened last week?".

These are read-only commands. None of them changes your files or your history, so you can run them as often as you like. That makes them the safest way to build confidence with Git.

You need a repository with a few commits (lesson L02). The examples below use a small Account class that grows over three commits. The commands were run with Git 2.44; hashes, dates, and some wording will differ on your machine.

Learning Objectives

By the end of this lesson you will be able to:

  • Use git status and git status -s to see untracked, modified, and staged files.
  • List history with git log, git log --oneline, and a limit such as -5.
  • Read the output of git diff: the file header, the @@ hunk header, and the - and + lines.
  • Show unstaged changes with git diff and staged changes with git diff --staged, and say why they differ.
  • Check what a particular commit changed with git show.
  • Follow a safe habit: status and diff before every commit.

Written Explanation

Our example history

Imagine the Account class from the Bank Accounts project, built over three commits:

text
aaf0772 Ignore withdraw larger than balance
60242ac Add withdraw method
f605473 Add Account class with deposit

Your hashes will be different. We will look at this history in several ways.

git log: the list of commits

bash
git log
text
commit aaf077208112526fd8eba4b39a969d05fba1cdd9
Author: Sara Student <sara@example.com>
Date:   Wed Nov 4 06:42:30 2026 +0200

    Ignore withdraw larger than balance

commit 60242ac58e4436bb5d1679c878c43930e2a19bca
Author: Sara Student <sara@example.com>
Date:   Wed Nov 4 06:42:30 2026 +0200

    Add withdraw method

commit f6054739a5622a83e0f743a2b31dec68e8f91e7f
Author: Sara Student <sara@example.com>
Date:   Wed Nov 4 06:42:29 2026 +0200

    Add Account class with deposit

Newest first. Each entry is one commit: its full hash (a long identifier Git computes for the commit; it is unique, and you can refer to it by its first few characters), the author, the date, and the message. This is the "who, when, why" from lesson L01. If the output is longer than your screen, Git shows it in a pager: press q to quit.

The full format is thorough but long. Most of the time you want the compact form:

bash
git log --oneline
text
aaf0772 Ignore withdraw larger than balance
60242ac Add withdraw method
f605473 Add Account class with deposit

--oneline prints an abbreviated hash and the first line of the message. This is also where good messages pay off: this list is only useful if each line says something. To limit the number of commits, add -n or just a number:

bash
git log --oneline -2
text
aaf0772 Ignore withdraw larger than balance
60242ac Add withdraw method

git log --oneline -5 shows the last five commits, and git log --oneline -3 -- Account.java shows only commits that touched that file.

git show: one commit in detail

bash
git show --stat --oneline HEAD
text
aaf0772 Ignore withdraw larger than balance
 Account.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

HEAD means "the commit you are currently on" (the latest one, for now). --stat summarises which files changed and how many lines. Without --stat, git show prints the full diff of that commit, which brings us to diffs.

git diff: what changed, line by line

A diff compares two versions of the text and shows the lines that differ. Edit deposit to use a longer form, without staging anything:

bash
git diff
text
diff --git a/Account.java b/Account.java
index 077584d..34f2619 100644
--- a/Account.java
+++ b/Account.java
@@ -2,7 +2,7 @@ public class Account {
     private double balance;
 
     public void deposit(double amount) {
-        balance += amount;
+        balance = balance + amount;
     }
 
     public void withdraw(double amount) {

How to read it:

  • --- a/Account.java and +++ b/Account.java: the old version (a) and the new version (b).
  • @@ -2,7 +2,7 @@: a hunk header. It means "starting at line 2 of the old file, 7 lines are shown; starting at line 2 of the new file, 7 lines are shown".
  • Lines starting with a space are unchanged context.
  • A line starting with - was removed. A line starting with + was added. A changed line appears as one - and one +.

The index line (with the hashes) is Git's internal bookkeeping; you can ignore it for now. If your terminal supports colour, removed lines are usually red and added lines green.

Unstaged versus staged: two different diffs

Because of the three areas (lesson L02), there are two natural comparisons:

CommandComparesAnswers
git diffworking tree against staging areaWhat have I changed that is not yet staged?
git diff --stagedstaging area against last commitWhat will go into my next commit?

Continue the example: stage the change.

bash
git add Account.java
git diff

This prints nothing, because there is no difference between the working tree and the staging area any more. Now:

bash
git diff --staged

prints the same diff as before. The change did not vanish; it moved to the staged side. This is the single most common point of confusion for beginners: "git diff shows nothing, did my change disappear?" Run git diff --staged before you worry. (--cached is a synonym.)

You can also compare two commits: git diff HEAD~2 HEAD --stat compares the commit two steps back (HEAD~2) with the latest one.

git status: the summary

git status -s after editing the file but before staging:

text
 M Account.java

The left column is the staging area, the right column is the working tree. A space on the left and M on the right means "modified, not staged". After git add, it becomes M (modified on the left, staged). git status without -s says the same in sentences and suggests commands.

The safe habit

Before each commit:

bash
git status        # which files are involved?
git diff          # what exactly is unstaged?
git diff --staged # what exactly will be committed?

It takes ten seconds and catches the commit that includes a debugging line, a half-finished edit, or a file that should not be there. Reading the diff of your own work is also the cheapest code review you will ever get.

Examples

Example 1: What did I just do? (simple)

You come back to a project after a break and do not remember where you stopped.

bash
git status -s
git log --oneline -3

status shows if anything is half-done; log shows the last three steps. Two commands, thirty seconds, and you know where you are.

Example 2: Checking before commit (practical)

You changed Account.java. Before committing:

bash
git diff

You notice a line System.out.println("DEBUG " + balance); that you added while testing. You delete it, run git diff again to confirm it is gone, and then:

bash
git add Account.java
git diff --staged
git commit -m "Ignore withdraw larger than balance"

Without the diff, the debugging line would be in your project history permanently.

Example 3: Finding when something changed (realistic)

A teammate reports that withdrawals larger than the balance are now silently ignored. Is that intended? Search the history of the file:

bash
git log --oneline -3 -- Account.java
text
aaf0772 Ignore withdraw larger than balance
60242ac Add withdraw method
f605473 Add Account class with deposit

The newest message says it was a deliberate choice. To see exactly what that commit did:

bash
git show aaf0772

You read the diff and see the if (amount <= balance) check was added. You now know when, who, why, and how. Whether that behaviour is a good design (silently ignoring is debatable) is a separate discussion, but at least it is not a mystery.

Backend Insight

In backend work, a bug report often arrives as "it worked last week". Commands from this lesson turn that into a search: git log --oneline to list candidates, git show to read what each did, and git diff between two commits to see everything that changed over a period. This is how teams find the change that caused an incident, and why precise messages matter: they let the log be skimmed at speed.

The same habit protects you at the other end. Backend repositories can contain configuration with database addresses and other sensitive values. Running git status and git diff --staged before every commit is how you notice them before they enter a history that is hard to clean.

Common Mistakes

Mistake 1: "git diff shows nothing, so nothing changed"

  • Incorrect: "The diff is empty, so my edit was lost."
  • Correct: "git diff shows only unstaged changes. I staged it, so I need git diff --staged."

Mistake 2: Committing without looking

What goes wrong: debug lines, stray files, or half-finished edits end up in history. How to avoid it: git status then git diff --staged before every commit.

Mistake 3: Being stuck in the pager

When git log output is long, Git shows a pager and the prompt does not return. Press q to leave it.

Mistake 4: Confusing hash and message

The short hash identifies a commit; the message only describes it. Two commits can have identical messages, so use the hash to refer to a specific one.

Exercises

Level A: Foundation

A1. Which command lists past commits, newest first, one line each?

Show solution A1

A1. git log --oneline.

A2. What do the - and + mean at the start of a line in a diff?

Show solution A2

A2. A line starting with - was removed (it exists only in the old version); a line starting with + was added (it exists only in the new version).

A3. Which command shows changes that are staged but not yet committed?

Show solution A3

A3. git diff --staged (or git diff --cached).

Level B: Application

B1. In a repository with at least three commits, show only the last two commits in compact form. Write the command and what the output looks like.

Show solution B1

B1. git log --oneline -2. Output is two lines, each an abbreviated hash and a message, newest first.

B2. Edit one line of a tracked file. Run git diff, then git add that file, then run git diff again and git diff --staged. Describe the three outputs and explain the difference.

Show solution B2

B2. The first git diff shows the change (working tree differs from staging area). After git add, git diff shows nothing, because the working tree and staging area now match. git diff --staged shows the same change as the first diff, because staging now differs from the last commit.

B3. Read this hunk header: @@ -10,6 +10,8 @@. What does it tell you about the size of the change?

Show solution B3

B3. The old version shows 6 lines from line 10, the new version shows 8 lines from line 10, so two more lines are present in the new version (for example, two lines were added).

Level C: Challenge

C1. You want to know every commit that touched Bank.java, and then exactly what the most recent one changed. Write the commands.

Show solution C1

C1.

bash
git log --oneline -- Bank.java
git show <hash-of-the-newest-one>

Replace the placeholder with the first hash from the list. git log --oneline -1 -- Bank.java shows only that newest commit.

C2. A teammate runs git diff, sees nothing, and says "Git lost my edit". Their git status -s shows M Bank.java. Explain what happened and how they can see the change.

Show solution C2

C2. M in the left column means the change is staged. git diff compares the working tree with the staging area, which now match, so it prints nothing. git diff --staged shows the change; the edit is not lost.

Mini Challenge

In a repository of yours with at least three commits, produce a short "report" using only Git commands (copy the real output into a text file):

  1. The one-line history of the whole project.
  2. The full details of the middle commit.
  3. A diff of an edit you make but have not staged, and then the same edit staged.

Add one sentence explaining which command you would use to answer: "What will my next commit contain?"

Check your work: your report has three real outputs, and the sentence names git diff --staged.

Summary & Next Lesson

Key takeaways

  • git log --oneline lists history compactly; -n limits the count; -- file limits to a file.
  • git show displays one commit.
  • A diff shows removed lines with - and added lines with +; the @@ header tells where.
  • git diff is unstaged; git diff --staged is what the next commit will contain.
  • Run git status and git diff before every commit.

Next lesson

Next is "Branches": you will create a separate line of commits so you can work on a feature without touching the working version.

Self-check

  1. Which command lists the commits?
  2. Which command shows staged changes?
  3. After git add, git diff prints nothing. Why?
  4. What does HEAD mean?
  5. How do you leave the pager in git log?
Show answers
  1. git log (or git log --oneline for the compact form).
  2. git diff --staged.
  3. It compares the working tree with the staging area, and they are now the same.
  4. The commit you are currently on, usually the latest one.
  5. Press q.

References

  • Pro Git book, "Recording Changes to the Repository". It supports the definitions of git diff (unstaged) and git diff --staged (staged against last commit), and the two-column git status -s format.
  • Git documentation, git log. It supports --oneline (abbreviated hash and title) and -n <number> limiting the commits shown. I read only the first part of this long page.
  • Not verified from an official page: the pager behaviour (q to quit) and the detailed meaning of the @@ hunk header; both are from running the commands and general Git knowledge. All outputs above were produced with Git 2.44 in a scratch repository.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.