Skip to main content

βͺ Lesson 3: History & Undo

Your commits are a time machine. Learn to see exactly what changed, visit any old version of your site, and undo mistakes safely, without losing a thing.

🎯 Learning Objectives

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

  • See exactly what you changed with git diff and git diff --staged
  • Inspect any past commit with git show and a single file's history with git log --oneline -- <file>
  • Look at an old version of a file without changing anything (git show <hash>:<file> or VS Code's Timeline)
  • Pick the right step on the undo ladder: git restore, git restore --staged, git commit --amend, and git revert
  • Recognize which commands are dangerous, and why we avoid them today

Estimated Time: 40 minutes

Project: Break your own website on purpose, then undo it three different ways

In This Lesson

πŸ•°οΈ Introduction

In the last lesson you made your first commits. Each one is a save point for your whole website. Saving is only half the story, though. The reason save points exist is so you can go back: to check what you changed, to find when a bug crept in, or to undo something you regret.

This lesson is about exactly that. First you'll learn to look (what changed, and when). Then you'll learn to undo, choosing the gentlest tool that fixes the problem. Every command here is safe: nothing you learn today throws away a commit.

Here's the map from Lesson 1 again, now with the "look" and "undo" commands added. Each area has its own way of seeing and undoing changes:

graph LR W["πŸ“ Working folder<br/>your edited files"] -->|"git add"| S["πŸ“¦ Staging area<br/>ready to commit"] S -->|"git commit"| R["πŸ—„οΈ Repository<br/>saved commits"] S -.->|"git restore --staged"| W W -.->|"git restore<br/>throws the edit away"| X["πŸ—‘οΈ Edit discarded<br/>file matches last commit"] style W fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style S fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style R fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style X fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b

🌱 Mistakes Are How You Learn Git

Every developer, including ones with twenty years of experience, commits a typo, stages the wrong file, or breaks a page. The difference isn't that they never make mistakes. They just know which undo to reach for. By the end of this lesson, so will you. So feel free to break things today. That's the assignment!

πŸ” Seeing Changes with git diff

git status tells you which files changed. git diff tells you which lines changed. It's the command to run just before you stage, so you know exactly what's about to go into your next commit.

Unstaged changes: git diff

Open css/style.css in VS Code, change your h1 color, and save. Then, in the integrated terminal:

git diff

You'll see something like this:

diff --git a/css/style.css b/css/style.css
index 136cefd..bbe85b5 100644
--- a/css/style.css
+++ b/css/style.css
@@ -4,5 +4,5 @@ body {
 }

 h1 {
-    color: #1e3a8a;
+    color: #b91c1c;
 }

How to read a diff

  • --- a/ and +++ b/ name the file: a is the old version, b is the new one.
  • @@ -4,5 +4,5 @@ says where in the file this is (around line 4). You can mostly ignore it.
  • Lines starting with - (usually red) were removed.
  • Lines starting with + (usually green) were added.
  • Lines with no sign are unchanged context, shown so you can tell where you are.

Notice that Git doesn't have a "changed" line. A changed line is shown as the old line removed and the new line added.

Staged changes: git diff --staged

Now stage the file and run git diff again:

git add css/style.css
git diff

Nothing prints! That surprises everybody the first time. Plain git diff only compares your working folder with the staging area, and those now match. To see what's staged (what your next commit will contain), add --staged:

git diff --staged

Now the same red/green diff appears again.

Command Shows the difference between… Use it when…
git diff Working folder ↔ staging area "What have I changed that I haven't staged yet?"
git diff --staged Staging area ↔ last commit "What exactly am I about to commit?"

πŸ’‘ Press q to Get Out

When a diff (or a log) is longer than your terminal window, Git shows it one screen at a time in a pager. Use the arrow keys or Space to scroll, and press q to quit back to your prompt. If your terminal ever looks "stuck" with a : or (END) at the bottom, it's just the pager. Press q.

πŸ“œ Exploring Your History

You already know git log --oneline from Lesson 2. It lists your commits, newest first, each with a short hash (its ID) and its message. Here's Ada's history from Lesson 2, with two more small commits on top:

git log --oneline
0d2abf0 (HEAD -> main) Add a note about weekly photos
4c2960c Add photography to about page
3cfc1b8 Add hobbies section to About page
9cc816c Add site images
e56cc25 Add site stylesheet
a9a2faf Add home, about, and contact pages
9b951eb Add .gitignore for system files

Your hashes will be different. Every commit in the world gets its own. You never have to type the full 40-character hash; the short 7-character version works fine. You can also copy and paste it from the terminal instead of typing it.

What did that commit change? git show

Give git show a hash and it prints who made the commit, when, the message, and the diff:

git show 4c2960c
commit 4c2960c697f1acdd6b917765a52c6a03dce574d1
Author: Ada Lovelace <ada@example.com>
Date:   Sat Sep 26 12:42:34 2026 -0700

    Add photography to about page

diff --git a/about.html b/about.html
index c79a249..7fa5da4 100644
--- a/about.html
+++ b/about.html
@@ -5,6 +5,6 @@
 </head>
 <body>
     <h1>About Me</h1>
-    <p>I love hiking and baking bread.</p>
+    <p>I love hiking, baking bread, and photography.</p>
 </body>
 </html>

Run git show with no hash at all and you get the most recent commit.

Just one file's history: git log --oneline -- <file>

When you want to know "when did my About page change?", put the file name after --:

git log --oneline -- about.html
4c2960c Add photography to about page
3cfc1b8 Add hobbies section to About page
a9a2faf Add home, about, and contact pages

Commits that didn't touch about.html (like the stylesheet, the images, and the note on the home page) are left out. The -- means "everything after this is a file name," which avoids confusion if a file is ever named like a branch.

πŸ’‘ This Is Why Good Messages Matter

Look at that history again. Because the messages say what changed ("Add photography to about page"), you can find the right commit in seconds. A history full of "stuff" and "fix" and "asdf" is a time machine with no labels on the dials. Future-you will thank present-you for every clear message.

πŸ”­ Visiting an Old Version

Sometimes you just want to read an old version of a page: "What did my About page say this morning?" You can do that without changing any files at all.

In the terminal: git show <hash>:<file>

Put a colon between the hash and the file's path:

git show a9a2faf:about.html

Git prints the whole file exactly as it was in that commit. Your working folder is untouched. For a file in a subfolder, include the folder, using forward slashes on every operating system:

git show e56cc25:css/style.css

In VS Code: the Timeline view

  1. Open the file in the editor (for example, about.html).
  2. In the Explorer sidebar, look near the bottom for a collapsed section called Timeline and click to expand it.
  3. You'll see a list of that file's commits, with their messages. (It may also show local saves; those are VS Code's own file history, not Git commits.)
  4. Click any commit to open a side-by-side comparison: the old version on the left, the version after that commit on the right.

Both methods are read-only. Look all you like; nothing changes until you decide to change it.

πŸͺœ The Undo Ladder

There isn't one "undo" button in Git. There are several, and the right one depends on how far your mistake has traveled: is it just an edit in a file, is it staged, or is it already committed? Start at the bottom of the ladder and climb only as high as you need.

graph TD Q{"Where is the mistake?"} -->|"Edited, not staged"| A["git restore file<br/>throw the edit away"] Q -->|"Staged, not committed"| B["git restore --staged file<br/>unstage, keep the edit"] Q -->|"In the last commit<br/>not pushed yet"| C["git commit --amend<br/>fix the message or add a file"] Q -->|"In any older commit"| D["git revert hash<br/>new commit that undoes it"] style Q fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style A fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style B fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style C fill:#f5f3ff,stroke:#8b5cf6,stroke-width:2px,color:#1e293b style D fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b

Good news: you don't have to memorize this. git status prints the right undo command as a hint, right under each heading. Read what it tells you!

Rung 1: Throw away an unstaged edit: git restore <file>

You edited a file, saved it, and hate the result. You haven't staged it. Run git status:

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:   css/style.css

no changes added to commit (use "git add" and/or "git commit -a")

See the second hint? That's your undo:

git restore css/style.css

The file snaps back to how it was at your last commit. If VS Code has it open, the editor updates on its own.

⚠️ git restore Really Throws the Edit Away

Uncommitted edits were never saved in Git, so once you restore, they're gone. There's no undo for this undo. Run git diff first if you're not sure what you'd be losing.

Rung 2: Unstage a file: git restore --staged <file>

You ran git add . and staged something you didn't mean to commit yet. git status shows:

On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   css/style.css

Take it back out of the staging area:

git restore --staged css/style.css

This is completely safe. Your edit is still in the file; it's just no longer staged. Run git status and you'll see the file back under "Changes not staged for commit." (And if you now want to throw the edit away too, that's Rung 1.)

Rung 3: Fix the last commit: git commit --amend

You just committed and immediately spotted a problem. Two classic cases:

A typo or unclear commit message. Replace the message on your most recent commit:

git commit --amend -m "Add a note about weekly photos"
[main 0d2abf0] Add a note about weekly photos
 Date: Sat Sep 26 12:42:41 2026 -0700
 1 file changed, 1 insertion(+)

You forgot a file (or a last-second fix). Stage it, then amend with --no-edit to keep the same message:

git add index.html
git commit --amend --no-edit

The missing change is folded into the last commit, as if you'd included it all along.

⚠️ Only Amend What You Haven't Shared

Amending doesn't edit the old commit; it replaces it with a new one (notice the hash changes). That's fine while the commit lives only on your computer. Once you've pushed it to GitHub (Lesson 5), don't amend it. Use git revert or simply make a new commit instead. Today, nothing is pushed yet, so amend freely.

Rung 4: Undo an older commit safely: git revert <hash>

Say you rewrote your About page, committed it, and now you want the old text back. Find the commit:

git log --oneline
e3f2c9b (HEAD -> main) Rewrite about page
0d2abf0 Add a note about weekly photos
4c2960c Add photography to about page
3cfc1b8 Add hobbies section to About page
...

Then revert it:

git revert e3f2c9b

Because you set VS Code as Git's editor in Lesson 1, a tab opens with a ready-made message: Revert "Rewrite about page", followed by This reverts commit e3f2c9b…. That message is fine as-is. Close the tab (save first if you edited it) and Git finishes the job. Now look at the log:

60efe82 (HEAD -> main) Revert "Rewrite about page"
e3f2c9b Rewrite about page
0d2abf0 Add a note about weekly photos
4c2960c Add photography to about page
3cfc1b8 Add hobbies section to About page
...

git revert doesn't erase anything. It adds a new commit that does the exact opposite of the old one. History stays honest ("I tried a rewrite, then undid it"), and it's always safe, even after you've pushed and shared your work. That's why it's the go-to undo for anything already committed.

%%{init: {"themeVariables": {"git0": "#3b82f6", "gitBranchLabel0": "#ffffff", "git1": "#f59e0b", "gitBranchLabel1": "#1e293b", "commitLabelFontSize": "12px"}}}%% gitGraph commit id: "3cfc1b8 About hobbies" commit id: "4c2960c photography" commit id: "0d2abf0 weekly note" commit id: "e3f2c9b rewrite about" type: REVERSE commit id: "60efe82 Revert rewrite" type: HIGHLIGHT

πŸ’‘ Terminal Waiting for You?

If your terminal seems to hang after git revert (or later, git merge), look for a VS Code tab named COMMIT_EDITMSG or MERGE_MSG. Git is waiting for you to close it. If you'd rather skip the editor entirely, add --no-edit: git revert --no-edit e3f2c9b. And if Git says unable to start editor (the code command isn't set up yet; see Lesson 1), the revert is already staged: run git commit --no-edit to finish it.

The ladder at a glance

The situation The command Is anything lost?
Edited a file, want the last committed version back git restore <file> Yes, the uncommitted edit
Staged something by mistake git restore --staged <file> No, the edit stays in the file
Last commit has a bad message or is missing a file (not pushed) git commit --amend No, the commit is replaced by a better one
An earlier commit was a mistake git revert <hash> No, a new "undo" commit is added

πŸ–±οΈ Undo in VS Code

The first two rungs have buttons in VS Code's Source Control panel (the branching icon in the Activity Bar, or Ctrl+Shift+G / Cmd+Shift+G). Hover over a file to reveal its small icons:

  • Under "Changes", the curved-arrow icon is Discard Changes. That's git restore <file>. VS Code asks you to confirm, because the edit will be gone.
  • Under "Staged Changes", the βˆ’ (minus) icon is Unstage Changes. That's git restore --staged <file>.
  • Clicking a changed file opens a side-by-side diff, the visual version of git diff.

For amend, the … (More Actions) menu at the top of the panel has Commit β†’ Commit (Amend). The menu layout moves around between VS Code versions, so if you can't find it, the terminal command always works.

Use whichever you like. The terminal and the buttons do exactly the same thing to the same repository, so you can mix them freely.

🚫 The Command We Won't Use Today

❌ git reset --hard: Powerful, and It Destroys Work

While searching online for "undo git commit," you will find answers that say git reset --hard. It moves your branch back in time and wipes out every uncommitted change in every file, with no confirmation and no undo button. Experienced developers use it carefully; beginners lose an afternoon's work to it.

You don't need it. Everything it's usually recommended for, you can do today with restore, amend, and revert. If a tutorial tells you to run it, stop and ask your instructor first.

❓ Common Questions

I ran git restore by accident. Can I get my edit back?

Not from Git, because the edit was never committed. Try Ctrl+Z / Cmd+Z in the VS Code tab if it's still open, or check that file's Timeline, which also lists VS Code's local saves. This is the best argument for small, frequent commits: anything committed can always be recovered.

Why doesn't git revert just delete the bad commit?

Deleting commits rewrites history, and that causes real trouble once your work is shared (anyone who already has the old commit ends up out of sync). Adding an "opposite" commit is always safe, and your log still tells the true story.

Can I revert a revert?

Yes! If you change your mind again, git revert the revert commit's hash and the change comes back, as yet another new commit.

What if I type the wrong hash?

If it doesn't exist, Git just prints an error and does nothing. If you revert the wrong (real) commit, revert that revert. Nothing you've committed is ever lost with the commands in this lesson.

πŸ› οΈ Hands-on Exercise

πŸ‹οΈ Exercise: Break It, Then Undo It Three Ways

Objective: Practice three rungs of the undo ladder on your own my-website project. Start with a clean slate: git status should say nothing to commit, working tree clean. If it doesn't, commit your work first.

Part A: Undo an unstaged edit (Rung 1)

  1. In VS Code, open index.html and delete your whole <h1> line. Save. Check Live Server: the heading is gone.
  2. Run git status and git diff. Find the red - line.
  3. Undo it with git restore index.html. Confirm the heading is back in the browser and git status is clean.

Part B: Unstage, then discard (Rungs 2 and 1)

  1. In css/style.css, change the body background to something awful (try lime). Save.
  2. Stage it with git add ., then look at it with git diff --staged.
  3. Unstage it with git restore --staged css/style.css. Check git status: it's back under "not staged."
  4. This time use VS Code: in the Source Control panel, hover over style.css and click Discard Changes. Confirm. The lime is gone.

Part C: Commit a mistake, then revert it (Rung 4)

  1. In about.html, change a paragraph to say something silly. Save, then commit it: git commit -am "Rewrite about page intro". (-a stages already-tracked files for you; see Lesson 2.)
  2. Run git log --oneline and copy that commit's hash.
  3. Revert it with git revert <hash>. Close the message tab in VS Code to finish.
  4. Check the browser: your original About text is back. Run git log --oneline: you should see both the mistake and the Revert "…" commit.
  5. Finally, use git show <hash>:about.html with the silly commit's hash to prove the silly version is still safely in history.

Deliverable: a clean git status, your site looking exactly as it did at the start, and a git log --oneline that shows your mistake and its revert.

πŸ’‘ Hint

Stuck? Run git status. It tells you which area your change is in and prints the command to undo it. If the terminal seems frozen after git revert, look for the COMMIT_EDITMSG tab in VS Code and close it. If you see : at the bottom of the terminal, press q.

βœ… Solution
# Part A
git status
git diff
git restore index.html
git status

# Part B
git add .
git diff --staged
git restore --staged css/style.css
git status
# then: Source Control panel β†’ hover style.css β†’ Discard Changes

# Part C
git commit -am "Rewrite about page intro"
git log --oneline
git revert a1b2c3d          # use YOUR hash of the silly commit
git log --oneline
git show a1b2c3d:about.html # the silly version, still in history

Your final log should end with something like this (your hashes will differ):

f9e8d7c (HEAD -> main) Revert "Rewrite about page intro"
a1b2c3d Rewrite about page intro
...

🌟 Want to Go Further?

Try Rung 3: make a small commit with a deliberately bad message like "stuff", then fix it with git commit --amend -m "Add footer text to home page". Run git log --oneline before and after, and notice that the hash changed. Why do you think that is?

Extra credit: git restore --source=<hash> about.html copies an old version of one file back into your working folder (as an ordinary unstaged edit you can commit or discard). Try it with an early commit, look at the result in Live Server, then decide whether to keep it.

🎯 Quick Quiz

Question 1: You edited style.css and ran git add style.css. Now plain git diff prints nothing. Why?

Question 2: You accidentally staged contact.html but want to keep your edits in it. Which command do you run?

Question 3: Three commits ago you changed your site's fonts and now you want the old fonts back, keeping everything you've done since. What's the safest command?

Question 4: What does git show 4c2960c:about.html do?

Question 5: You just committed with the message "updaet homepage" and haven't pushed. How do you fix the typo?

πŸ““ Learning Journal

Take five minutes to add to your learning journal. Write down:

  • Key concepts you learned
  • What clicked β€” the idea or technique that made sense
  • Questions or confusion, so you know what to revisit
  • Ideas to try on your own pages
  • How you feel about your progress

✍️ This lesson's prompt: Think back to a time before today when you lost work or broke a page and couldn't get it back. Which rung of the undo ladder would have saved you? Now that you've broken your site on purpose and fixed it, how does it feel to experiment knowing you can always go back? Which undo command do you still need to look up?

Summary

πŸŽ‰ Key Takeaways

  • git diff shows unstaged line changes; git diff --staged shows what your next commit will contain
  • git show <hash> shows one commit; git log --oneline -- <file> shows one file's history
  • git show <hash>:<file> and VS Code's Timeline let you read old versions without changing anything
  • The undo ladder: restore (discard an edit) β†’ restore --staged (unstage) β†’ commit --amend (fix the last, unpushed commit) β†’ revert (safely undo any commit)
  • git status prints the undo command you need. Read its hints!
  • Avoid git reset --hard: it destroys uncommitted work

πŸ“š Additional Resources

πŸš€ What's Next?

That wraps up Module 1: you can save, look back, and undo. After lunch, you'll learn Git's favorite feature: branches. A branch is a safe copy of your timeline where you can try a bold new color scheme without touching the working site, then merge it in when you like it. You'll even cause a merge conflict on purpose and fix it.