Chapter 4 of 10 · Git and GitHub for Beginners — Complete Free Tutorial
Viewing History and Undoing Changes
Now that you can create commits, this fourth chapter of the Git and GitHub for Beginners series teaches you to read them and — more importantly — to undo things safely. Undoing is not one command but a small decision tree: what you are undoing and where it currently lives decides which command fits.
Reading history with git log
git log prints commits newest first, but its format is completely adjustable. The default verbose view is great for exploring; once you know what you are looking for, compact formats are faster to scan.
| Form | Shows | Best for |
|---|---|---|
git log --oneline | One short line per commit | Scanning recent history quickly |
git log --graph --oneline --decorate | Branch and merge lines drawn beside commits | Seeing how branches joined together |
git log --stat | Files changed and line counts per commit | Finding which commit touched a file |
git log -p | The full line-by-line diff of each commit | Studying exactly what a commit did |
You can also filter: git log --since="2 weeks ago", git log --grep="menu", or git log -- index.html to list only commits that touched one path.
Zooming in with show and diff
Where log summarises history, show opens one commit: its message, metadata and full diff. Between snapshots, plain git diff answers a different question — what have I changed since I last staged? The output is a unified diff: context lines prefixed with a space, removed lines with -, added lines with +. Learn to read it and you can review any change on any machine.
Undoing uncommitted changes
The scariest-sounding undo is actually the most common and the simplest, because nothing is committed yet:
- Discard edits in a file —
git restore <file>copies the last committed version back over your working copy. - Unstage a file but keep the edits —
git restore --staged <file>moves it out of the next commit without touching your work. - Not sure? Run
git statusandgit difffirst. Nothing here affects commits that already exist.
Undoing commits you have already made
Once a commit exists the options widen, and the right one depends on whether anyone else has seen it. Three tools cover almost every case:
| Situation | Command | Effect |
|---|---|---|
| Last commit was too early, nothing pushed | git commit --amend | Replaces it with a corrected commit |
| Committed the wrong changes, not pushed | git reset --soft HEAD~1 | Removes the commit but keeps the changes staged |
| Bad commit is already pushed | git revert <hash> | Adds a new commit that undoes the old one |
The golden rule behind that table: never rewrite history that other people have already pulled. Revert is always safe; reset and amend belong to commits only you have. You will practise all three in Correcting History: Reset, Revert and Rebase.
Recovering from a mistake: the reflog
If you reset too far or check out an old state and panic, take a breath. Git keeps a safety net called the reflog (reference log) that records where your branch pointer has been for roughly 90 days. git reflog lists those positions with hashes; finding the commit you lost and running git switch -c rescue <hash> brings it back on a new branch. As long as the commit was ever made, it is almost never truly gone.
git log --oneline -5
Expected output:
a1b2c3d Add homepage and update the menu prices
9f8e7d6 Save work in progress on the contact form
4c3b2a1 Fix the broken link in the footer
7e6d5c4 Style the navigation bar
b2a1f0e Initial commit
git diff -- bakery-menu.txt
Expected output:
diff --git a/bakery-menu.txt b/bakery-menu.txt
index 6c1a2f3..9ab4de1 100644
--- a/bakery-menu.txt
+++ b/bakery-menu.txt
@@ -1,3 +1,3 @@
Sourdough loaf
-Croissant: Rs. 40
+Croissant: Rs. 45
Chocolate cake
git restore --staged bakery-menu.txt
git status --short
Expected output:
M bakery-menu.txt
A contact.html
Common mistakes
- Running
git restorewithout--stagedwhen they meant to unstage — without the flag it discards the file's edits. Check status first. - Using
git reset --hardas a first reaction — it throws work away silently; try--softor status/diff before hard resets. - Amending a commit that is already pushed — teammates' clones reject the rewritten history and the branch diverges.
- Reading the default
logoutput and giving up — start with--onelineand add detail only when you need it. - Assuming an old commit is deleted when it leaves the branch — it usually stays reachable through the reflog until it eventually expires.
Practice checklist
- Run
git log --onelinein your practice repo and identify the hash, message and order of each commit. - Change a committed file without staging it and read
git diffuntil the plus and minus lines make sense. - Stage that change, then undo the staging with
git restore --stagedand confirm the edit is still in your file. - Commit a small change, then practise
git commit --amendto improve its message before pushing anywhere. - Run
git reflogand find the hash of your most recent commit listed there.
What's next?
Single-track history is about to become a choose-your-own-adventure. Continue to Branching and Merging to learn how Git copies your entire project with one command.
Frequently Asked Questions
Git is a distributed version control system: it records snapshots of your project over time, lets you jump back to any point, branch off to try ideas safely, and merge other people's work. It is the industry standard — nearly every developer job assumes basic Git fluency — and it is the best insurance you can have against lost or broken code.
Git is the tool that runs on your machine and stores history locally; it works fully offline. GitHub is one of many websites (GitLab, Bitbucket) that hosts Git repositories in the cloud and adds collaboration features: pull requests, code review, issues and CI. You can use Git forever without GitHub — GitHub needs Git to be useful.
A repository (repo) is your project folder plus a hidden .git directory containing the entire history: every commit, branch and tag. It can be local (only on your computer) or remote (cloned on GitHub). Deleting .git loses the history but keeps your files; deleting the files while keeping .git leaves history with nothing to show.
git add moves changed files into the staging area — your draft of the next snapshot — so you can review exactly what will be recorded. git commit writes the staged snapshot permanently with a message describing why the change happened. Two steps by design: stage deliberately, commit intentionally.
A branch is a movable pointer to a commit — creating one costs almost nothing. Use a branch for every unit of work (feature, fix, experiment): it keeps unfinished code out of main, lets you review changes in a pull request, and makes abandoning a bad idea as simple as deleting the branch.
It depends on where the change lives. Unstaged edits: git restore file. Staged but uncommitted: git restore --staged file. Committed but not pushed: git reset --soft to keep changes staged. Already pushed: git revert commit — it writes a new commit that undoes the old one instead of rewriting shared history. The undoing chapter covers each case with commands.
A conflict happens when two branches changed the same lines and Git cannot choose automatically. Your file gets marked up with <<<<<<<, ======= and >>>>>>> sections; you edit it down to the correct result, remove the markers, then git add and git commit. Conflicts are normal, small and entirely fixable — they are not an error state.
A pull request (PR) is GitHub's proposal to merge one branch into another. It shows the diff, runs checks, and lets teammates comment line by line before the code lands. It is the centre of modern collaboration: work on a branch, open a PR, review, approve, merge.
Write the subject in imperative mood, under about 50 characters: "Add email validation to signup". Explain why, not what, in the body when it is not obvious. Many teams use Conventional Commits (feat:, fix:, docs:) so tools can generate changelogs — the workflows chapter shows the format in practice.
Secrets (API keys, .env files), dependencies (node_modules), build output (dist), and operating-system junk (.DS_Store, Thumbs.db). A .gitignore file excludes them from the start; the common mistakes chapter shows what to do if something already slipped into history.