Claude Code can change forty files in ninety seconds. Command+Z in your editor undoes changes in one file, in one window, until you close it.

That gap is why version control matters more to you than to a developer typing every line by hand. You are approving work you did not write, at a speed you cannot proofread.

The idea

Git takes a snapshot of your entire project whenever you ask, attaches a message and a timestamp, and keeps it. A snapshot is a , and the folder holding your project plus all of its snapshots is a . Returning to any snapshot is one command and about a second.

text
  main    A ----- B ----- C ----- D   <- history, oldest to newest. D is where you are.
                          |
                          \-- E ----- F    branch: risky-refactor
                                             same history up to C, then separate
                                             git merge brings E and F back into main

  Working tree  ->  git add  ->  Staging  ->  git commit  ->  History
  (your files)                   (chosen                       (saved)
                                  changes)

How it works

  • The working tree is your files as they are right now. Git watches them and records nothing until you tell it to. Your line of commits has a name, and by default that name is main.
  • Staging is the holding area you put changes into before saving them. It exists so you can save two unrelated fixes as two commits, and until you need that, git add -A before each commit stages everything.
  • A commit writes the staged changes into history with your message: git commit -m "add waitlist form". The message is for the you of next month, so write what changed, not "update".
  • git log --oneline lists your commits with a short id each. git checkout 4f2a1c puts the project back to how it looked at that one, and git checkout main returns you to the present. Git calls the state in between a "detached HEAD", which is not an error. Look, do not edit: a commit made there disappears when you leave.
  • A lets you try something without touching the version that works. git checkout -b risky-refactor creates one and moves you onto it. When the experiment is good, git checkout main then git merge risky-refactor copies the work back.
  • git status is the one you run constantly. It names the files that changed, which are staged, and which branch you are on.

What to do

  1. Deal with .gitignore first, before anything is recorded. It lists what git must never save, one per line, and most starter projects write one for you. Open it, or create it, and check for these:

    text
    node_modules/
    .env
    .env.local
    .next/
    .DS_Store
  2. Now put the project under git, before the first AI-written line. In the project root: git init, git add -A, then git status. Read that list. If a .env file or node_modules is in it, add it to .gitignore, then git reset and git add -A again, because an ignore rule does not reach a file git has already been handed. Once the list is clean: git commit -m "starting point". On a new machine git refuses that last one until you say who you are, and prints the two commands that fix it.

  3. Commit before you hand over a task, every time. A clean git status at the start means one command puts everything back if the session goes badly. Asking Claude Code to make that commit for you is fine. Asking it whether the work was any good is not.

  4. Undo with the smallest tool that works. git restore src/app/page.tsx throws away the unstaged changes to one file, and git restore :/ does it for every tracked file in the project, wherever you are standing. Neither touches staged changes or new files. To put everything back to the last commit: git reset --hard, then deal with the new files in two steps. git clean -nd lists what would go, and only then git clean -fd deletes it. That is a real deletion off your disk, not a stash, so read the list. If the assistant already committed, name the commit you want: git log --oneline, then git reset --hard 4f2a1c. All of these are irreversible, so read git status first.

  5. Work on a branch for anything you expect to go wrong: git checkout -b try-new-auth. If it fails, git checkout main leaves the experiment behind.

Where it breaks

Git only protects what you committed. An assistant that rewrites a file you never snapshotted has destroyed the previous version, and no git command brings it back. Two things help beforehand. git stash -u parks uncommitted work where git stash pop retrieves it, and the -u matters: plain git stash skips files git has never seen. VS Code and Cursor also keep a per-file Local History in the Timeline panel.

The history also keeps what you wish it would not. An API key committed once is still in there after you delete the line, which is what the .gitignore step is for. What to do when one has already gone in is a section of its own in section 5.