Lesson 2 of 3 · Git Time-Travel & GitHub
Branches & Time-Travel
Use commits as saved project states and branches as safe lanes for experiments.
This lesson is locked. You can read ahead, but finish earlier stops to earn progress.
Listen
Watch
Read
Start with an isolated practice repository
Time-travel should begin in a disposable project, not your only copy of a product. Create a git-playground folder outside your studio repository, open it in VS Code and open its terminal. The Git commands below work on Windows and Mac. Use the editor to create notes.md with one sentence, “First working version”. Save it, then run these commands separately:
git init git add notes.md git commit -m "Record the first working version" git branch -M main
If Git asks for an author identity, use the name and commit email setup from Commits & History. Now edit the sentence to “Clearer working version”, save, review git diff, stage notes.md and make a second commit with the message “Clarify the working version”. Run git status and confirm the working tree is clean before moving branches.
Understand HEAD, branches and parents
A branch is a name that moves forward when you commit on it. HEAD normally points to your current branch. A commit points back to its parent, so older snapshots remain reachable when you add new work.
git log --oneline --graph --decorate --all
Your output will resemble this, with different hashes:
* b234567 (HEAD -> main) Clarify the working version * a123456 Record the first working version
Read newest first. The asterisk represents a commit, the hash identifies it, and the decoration shows which branch points there. --all includes other branches; without it, a log normally follows history from the current checkout. A branch does not copy your folder: switching updates the working files to the selected snapshot.
Inspect the past without changing files
Copy the older hash from your own log. In these commands, replace OLD_HASH and NEW_HASH with your actual older and newer hashes:
git show OLD_HASH:notes.md git diff OLD_HASH NEW_HASH -- notes.md
The first reads the file as it existed in that commit without replacing today's file. The second compares the two snapshots: - is content removed from the older version, + is content added in the newer version. Reverse the hashes and the comparison direction reverses. Press q if output opens in a pager.
Predict the old sentence before running the first command. Then confirm your editor still shows the newer sentence. Reading history is different from restoring files.
Experiment on a branch and return
git switch -c try-new-notes
You are now on a new branch at the same commit as main. Edit notes.md, save, inspect the diff, stage it and commit “Try an alternative explanation”. Then:
git switch main git log --oneline --graph --decorate --all
Your editor returns to the main version. The experimental commit still exists on try-new-notes. Run git switch try-new-notes to see it again, then git switch main to return. Switching does not publish or merge a branch. Uncommitted changes may travel with a switch, or Git may block it to avoid overwriting them; a clean working tree makes the exercise predictable. If blocked, stop and inspect status rather than forcing the switch.
To deliberately start a new experiment from the older snapshot, with a clean tree on main, run git switch -c revisit-first OLD_HASH using your own hash. This makes a named branch, so new commits have a clear home. Return with git switch main after saving any work as a commit on that branch. Do not use the example hashes literally.
Detached HEAD is inspection without a branch
Checking out a hash directly can detach HEAD: it points to a commit instead of a branch name. This is not corruption, but new commits will not advance main. For beginners, inspect with git show or create a named branch from the hash as above. If you accidentally made useful work while detached, create a branch there with git switch -c keep-my-experiment before leaving that checkout.
Three different kinds of undo
| Goal | Tool | What it changes |
|---|---|---|
| Remove a file from the next commit, keeping the edit | git restore --staged notes.md | Staging only; requires an existing commit. |
| Discard an unwanted unstaged edit | git restore -- notes.md | Working file becomes the staged version; uncommitted content is lost. |
| Undo a committed change without deleting history | git revert COMMIT_HASH | Creates a new commit reversing that commit's changes. |
Practise unstaging first: on main, edit notes.md, stage it, then run git restore --staged notes.md. Status should show it as modified but not staged, and the edit should remain in the editor. You can stage and commit it when it is ready.
Only in this disposable repository, and only if you want to throw away the edit, run git restore -- notes.md after unstaging. Confirm the sentence returns to the committed version. Ordinary Git commits cannot recover an edit you never committed. Never use restore as a casual response to an error.
Recover a bad commit by adding history
With a clean tree on main, make a deliberately unwanted sentence change and commit it. Run git log --oneline and copy that unwanted commit's hash. Use git revert --no-edit COMMIT_HASH, replacing the placeholder with that hash. The option accepts Git's default revert message without opening an editor. Check the file and log: the sentence is restored and a new revert commit appears; the bad commit remains visible.
This example is a simple, non-merge commit. Reverting a complex change may produce conflicts; stop and inspect the message. Do not guess through conflict markers. git revert --abort cancels an in-progress revert, returning to its starting state. Shared history should normally be corrected with new commits rather than reset, rebase or force-push, which can disrupt collaborators and deployments.
Restore one historical file without rewinding the branch
With a clean tree and all work safely committed in the playground, git restore --source=OLD_HASH -- notes.md replaces just the working copy of notes.md with its old version. Review git diff, then stage and commit it if that restoration is what you want. The branch's earlier commits are not deleted; the restoration becomes a new change. This command overwrites that working file, so never run it over an edit you need to keep.
Check your understanding
Before leaving the playground, explain which branch contains your experiment, what HEAD points to, and how you inspected an older file without changing it. Show the new revert commit and explain why the original bad commit is still in the log. Finally, distinguish unstaging (keeps an edit) from restoring (can discard it). These are practice checks, not new XP or unlock requirements. You can use the same small checkpoints when reviewing an agent's proposed changes to a real product.
Key terms
- Commit
- A snapshot of staged changes in Git history.
- Staging area
- The selection of changes queued for the next commit.
- Branch
- A named line of development for parallel work.
- Restore
- A Git operation that can replace working file contents.
Summary — copy into your notes
- Commits snapshot staged changes, not every edit automatically.
- Use clear messages to explain each snapshot.
- Create branches with git switch -c for safe experiments.
- Inspect status before restoring files or revisiting history.