An agent is about to edit your files. Dozens of them, fast, sometimes wrong. The only reason that prospect is exciting instead of terrifying is a program from 2005 that remembers every version of everything and can put any of it back. People who fear git avoid the undo features because they have never used them under calm conditions. So today you will break a repository on purpose, five ways, and fix it five ways, and after that the fear has nothing left to grip.
The map
Git is a save-point system. A commit is a snapshot of your entire project at a moment you chose, with a message saying why. The chain of commits is your project's history, and git can show you any snapshot, the difference between any two, or bring any of them back.
Three zones matter. Your working directory is the files as they are right now. The staging area is the loading dock: git add file puts a file's current state there, meaning "include this in the next snapshot." git commit seals everything staged into a permanent snapshot. This two-step design is a feature, not bureaucracy: you can edit five files and commit two of them as one coherent change.
Rendering diagram...
The commands you will use daily:
git initstarts tracking a directorygit statusshows what changed and what is staged (run it constantly, it is free)git add filestages,git commit -m "why"snapshotsgit log --onelinelists history, newest firstgit diffshows unstaged changes line by linegit branchandgit switchmanage parallel lines of workgit merge other-branchcombines a branch into the current one
A branch is a movable label pointing at a commit. Creating one costs nothing. You work on main normally; when you want to try something without risking it, git switch -c experiment starts a branch, you commit freely there, and main stays untouched until you merge. When both branches changed the same lines, git stops and asks you to decide: that is a merge conflict, and it looks scarier than it is. Git writes both versions into the file between <<<<<<< and >>>>>>> markers; you edit the file to the final content you want, delete the markers, add, and commit. That is the entire procedure.
Remotes are copies of the repository elsewhere, usually GitHub. git push sends your commits up, git pull brings others' commits down. Part 0 covers the GitHub workflow properly; today is about the local safety net. One more file matters now: .gitignore, a list of paths git should never track (dependency folders, secrets, build output). Add node_modules/ and .env to it in every project, day one, and future-you avoids the classic mistakes of committing 200MB of dependencies or an API key.
Tutor first
You are my git tutor. Quiz me one applied question at a time: give me a scenario ("you committed to the wrong branch", "git status shows X") and ask what command I would run and what it will do. After each answer, have me verify the command in a scratch repo in my terminal before moving on. Escalate from status/add/commit through branching, merging, and undoing. Do not let me get away with vague answers like "I would revert it": make me name the exact command. Stop after 10 and list my weak spots.
The lab: break it, then fix it
Lablab-p-3Break and recover
Goal: Cause the five classic git disasters in a scratch repo and reverse each one with the standard recovery tool.
Setup. If git --version fails, install git first (macOS: it comes with the Xcode command line tools, which the terminal offers to install automatically; Linux and WSL: sudo apt install git). Then, if you have never configured git, tell it who you are (this goes on every commit):
git config --global user.name "Your Name"
git config --global user.email "you@example.com"- Build a small history to play with:
cd && mkdir git-lab && cd git-lab
git init -b main
echo "# Recipe Box" > README.md
git add README.md
git commit -m "first commit: readme"
printf 'Chili\n- beans\n- tomatoes\n' > chili.txt
git add chili.txt
git commit -m "add chili recipe"
git log --onelineCheck: log shows two commits, newest first.
- Branch, diverge, and force a conflict:
git switch -c spicy
printf 'Chili\n- beans\n- tomatoes\n- three habaneros\n' > chili.txt
git add chili.txt && git commit -m "spicy version"
git switch main
printf 'Chili\n- beans\n- tomatoes\n- mild green chiles\n' > chili.txt
git add chili.txt && git commit -m "mild version"
git merge spicyCheck: git announces CONFLICT (content): Merge conflict in chili.txt. You made two branches edit the same line, on purpose. Open chili.txt with cat: both versions are there between <<<<<<<, =======, and >>>>>>> markers. Now resolve: edit the file (use nano chili.txt or any editor) so it lists both peppers and contains no marker lines, then:
git add chili.txt
git commit -m "merge: both heats"Check: git log --oneline shows the merge commit on top. You have now survived the thing people fear most in git, and it was a text edit.
- Disaster 1, uncommitted mess. Wreck a file, then discard the wreckage:
echo "GARBAGE" > README.md
git status
git restore README.md
cat README.mdCheck: status showed README.md modified; after restore, the file says # Recipe Box again. git restore means "give me back the committed version." Uncommitted changes are the only thing git cannot resurrect, which is one more argument for committing often.
- Disaster 2, bad commit message or premature commit. Undo the commit, keep the work:
echo "- salt" >> chili.txt
git add chili.txt && git commit -m "oops wrong messgae"
git reset --soft HEAD~1
git status
git commit -m "add salt"Check: after the reset, status shows chili.txt staged and the typo commit is gone from git log --oneline. reset --soft HEAD~1 means "rewind history by one commit but leave my files and staging alone." Nothing was lost; the snapshot was just unsealed.
- Disaster 3, deleted a committed file.
rm chili.txt
git restore chili.txt
lsCheck: the file is back. Committed means recoverable, always.
- Disaster 4, a commit that turned out to be a bad idea. Revert it without rewriting history:
echo "- a cup of mayonnaise" >> chili.txt
git add chili.txt && git commit -m "mayo experiment"
git revert --no-edit HEAD
grep mayonnaise chili.txtCheck: the grep prints nothing, and git log --oneline shows a new commit titled Revert "mayo experiment". Note the difference from reset: revert adds a new commit that undoes an old one, leaving history intact. That is why revert is the safe choice on work you have already shared.
- Disaster 5, "I destroyed my history." The big one. Hard-reset away your recent commits, panic briefly, then discover git kept them anyway:
git log --oneline
git reset --hard HEAD~2
git log --onelineCheck: the last two commits are gone from the log. In most tools this is the unrecoverable moment. Now:
git reflogCheck: the reflog lists every position HEAD has occupied, including the "lost" commits, each with an id like e235407. Find the line for the state just before your reset (it will be the reset line's predecessor, typically HEAD@{1}), then restore it:
git reset --hard HEAD@{1}
git log --onelineCheck: history is back, all commits present. Commit to memory: for roughly 90 days, git remembers even the states you deleted. The reflog is the fire escape behind the fire escape.
Verify
- Your merge commit, revert commit, and restored history are all visible in
git log --oneline. - You can state from memory which tool handles each case: uncommitted mess (restore), unseal last commit (reset --soft), undo a shared commit (revert), find lost commits (reflog).
git statuson your repo prints "nothing to commit, working tree clean."
>Troubleshooting
- The merge did not conflict. You probably edited different lines on the two branches, or committed on the wrong branch. Run
git log --oneline --allto see where your commits landed, delete the folder, and rerun step 2 exactly. - Stuck inside a strange screen after committing. Git opened an editor (often vim) because a
-mwas missing. Type:q!then enter to escape vim, and include-m "message"next time. git restoresays pathspec did not match. The file was never committed, or you are in the wrong directory.git statusandpwdfirst, then retry.
Where it breaks
Git's undo tools differ on one axis that matters: whether history is rewritten. restore touches only your working files. revert adds new history. reset rewrites it. On a repo only you touch, rewriting is fine. The moment commits exist on a shared remote, rewriting history you have pushed creates a mess for everyone who pulled it, so the rule is: private history, reset freely; shared history, revert only. You will not share repos until Part 0, but install the rule before the stakes exist.
And the failure mode agents specifically create: an agent makes forty edits, you like thirty of them, and there is no commit anywhere. Now sorting the keepers from the junk is archaeology. The discipline that prevents this costs one command: commit before you let an agent loose, and commit when it finishes something you like. Checkpoints only help if you place them.
Knowledge check
Knowledge check
Sources
Git's core commands have been stable for many years, including the newer restore and switch (introduced in 2019, standard everywhere now). No volatile claims to cite. Every command sequence and its expected output in this lesson was executed and verified during authoring (July 2026).