The Core Safety Loop: Your "Save Game" for AI Coding
"Commit early, commit often. Every save point is a universe you can return to."
This is the heartbeat of your entire Git workflow. Every time the AI generates code, you'll follow this three-step rhythm: check what changed, approve what you trust, and freeze it in time. Master this loop and you'll never lose work again. We'll close the part with the flip side of saving: the files that should never enter history at all.
The diagram above shows the full cycle you'll repeat every time you work with AI-generated code. This chapter covers the middle of the loop — discarding is Part 4's territory, and pushing comes in Part 3. Let's break it down:
2.1 "What Did the AI Just Do?"
You just prompted the AI to refactor a module, and it went silent for a moment. What exactly did it touch? Before you do anything, you need situational awareness.
git status is your "heads-up display." The output shows three categories:
"Changes not staged" (Red)
Files Git tracks that were modified but not yet approved for the next save.
"Untracked files" (Red)
Brand-new files the AI created that Git has never seen before.
"Changes to be committed" (Green)
Staged files — reviewed, approved, and going into the next commit. Your goal is moving the right files here.
On branch feature/login
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/auth.py
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: src/routes.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/session_store.pyThe good news? You don't actually need to type git status in the terminal. VS Code shows you all of this visually. Just click the Source Control icon in the Activity Bar (the branch icon on the left sidebar). The badge shows how many files changed,
and each file is labeled M (modified), U (untracked), or D (deleted):
You can also spot changes right in the editor without opening the Source Control panel. VS Code adds colored indicators in the gutter (the narrow strip to the left of your code): green for added lines, blue for modified lines, and a red triangle for deleted lines. Click any indicator to preview the change inline:
"What files did you just change? Show me a summary"
"Check git status and explain what each changed file does"
2.2 Reviewing and Staging the AI's Work
Now you know what changed. The next step is the most important one: reviewing the AI's work line by line, and only approving what you trust. This is staging — your quality gate.
git status shows the AI changed five files. Blindly trusting this is how bugs
are introduced. You must review every line and then "stage" the changes you approve.Step Zero: Actually Read the Diff
git status tells you which files changed; git diff shows you what changed inside them — removed lines in red with a -, added lines in green with a +. It's the terminal twin of the VS Code Diff Editor you'll meet below, and
it's the command every AI agent runs before staging (Part 6 makes it a rule).
git diff # Unstaged changes: working directory vs staging area
git diff --staged # Staged changes: what the next commit will containgit diff --staged your ritual final check: it shows exactly — and only —
what's about to be frozen into the commit. If a secret or debug print shows up there, you just
caught it in time.Option 1: Stage All (the "trusting" add)
git add .Option 2: Stage One File (the "surgical" add)
git add src/my_file.pyOption 3: Stage Parts of a File (the "AI-first developer's" add)
git add --patch walks you through every individual block of changes ("hunk") interactively. One blind spot: it
only shows modifications to tracked files — brand-new files the AI created won't
appear at all. Stage those by name with git add <file> after reading them.Here's how to start an interactive staging session:
git add --patch # or git add -pFor each hunk, Git asks: y (stage), n (skip), s (split into smaller pieces), q (quit). In the Try It: The Complete Loop playground (next section), git add -p is simplified to file-by-file staging — type y or n on the next line for each file.
The VS Code Way: Visual Staging
This is where VS Code really shines. Instead of typing cryptic y/n/s/q responses in the terminal, you get a beautiful side-by-side Diff Editor. To stage an entire file,
just hover over it and click the + button:
When you click a file in the Changes list, VS Code opens the Diff Editor -- a side-by-side view showing the old version (left) and new version (right). This makes it easy to review exactly what changed before staging:
For even more precision, click a file to open the Diff Editor, then select specific lines
and right-click → "Stage Selected Ranges". This is the visual equivalent of git add -p, but far easier to use:
"Stage only the files related to the authentication feature"
"Show me the diff of each changed file one at a time so I can decide what to stage"
git add .? Use git restore --staged <file> to unstage specific files before committing. This is especially critical if you accidentally staged .env files with secrets or debug files. You'll practice exactly this in the "Unstage Secrets" playground in section 4.2 (or right now — open the playground
from the header and pick the scenario).2.3 Creating the Save Point
You've reviewed the changes and staged the ones you trust. Now it's time to freeze this moment in time — a commit is your permanent save point that you can always travel back to.
git commit -m "feat: add user authentication endpoint"The command itself is simple, but the message you write matters more than you might think.
| Type | When to Use | Example |
|---|---|---|
feat: | A new feature for the user | feat: add login button to homepage |
fix: | A bug fix for the user | fix: resolve dimension mismatch |
docs: | Documentation only changes | docs: update API reference |
style: | Formatting, missing semicolons, etc. | style: fix indentation in utils |
refactor: | Code change that neither fixes a bug nor adds a feature | refactor: simplify form validation |
test: | Adding or correcting tests | test: add unit tests for auth |
chore: | Build process or tools changes | chore: update dependencies |
perf: | A code change that improves performance | perf: cache database queries |
Where the "Why" Actually Goes: the Body
The subject line above says what changed. The "why" lives in the commit body — a blank line after the subject, then as many lines of context as the
change deserves. Run git commit with no -m and Git opens your editor (VS Code, if you set core.editor in Part 1) for exactly this:
fix(auth): reject expired tokens before the DB lookup
The AI's first fix checked expiry after loading the user, which
let expired tokens trigger a full query. Checking first avoids
the load and matches how the session middleware already behaves.Save and close the editor tab, and the commit completes. (Changed your mind? Close it with an empty message and Git aborts the commit.)
One last habit: prove to yourself the save point exists. Right after committing, run git log --oneline -1 — one line, your hash and message. (That's also the final step Part 6 teaches agents: commit,
then report the hash.)
In VS Code, committing is just as simple: type your message in the input box at the top of the Source Control panel and click the Commit button (or press Cmd+Enter / Ctrl+Enter). Notice the sparkle icon next to the input -- click it to let AI generate a commit message from your staged changes:
Here's the AI commit message feature in action. After staging your changes, click the sparkle icon and Copilot will analyze your diff and write a descriptive message for you:
Try It: The Complete Loop
help in the terminal for the full
command list.Loading playground...
"Commit the staged changes with a good conventional commit message"
"Write a descriptive commit message for these changes and commit them"
2.4 What NOT to Commit (.gitignore)
You asked the AI to scaffold your project, and it delivered: dependencies installed, an .env file holding your API keys, and a working app. Then it helpfully runs git add . — and now your secrets, your node_modules folder, and a stray .DS_Store are all staged for your next commit. You just learned how permanent
a commit is — before you freeze that mistake into history, you need to teach Git what to ignore.
git add . can commit API keys, gigabytes of dependencies, and OS junk into a history
that everyone on your team will clone.A .gitignore file is a plain text file at
the root of your repository that lists patterns of files Git should pretend don't exist.
Ignored files never show up as "untracked" in git status, and git add . silently skips them. The file itself gets committed, so the whole team
— and every AI agent working in the repo — shares the same guardrail.
# Dependencies
node_modules/
.venv/
__pycache__/
*.pyc
# Secrets & local config
.env
.env.*
!.env.example
# Build output
dist/
build/
coverage/
# Logs & caches
*.log
.cache/
# AI tool local state (personal settings stay personal)
.claude/settings.local.json
CLAUDE.local.md
.cursor/
.aider*(Purely personal noise like .DS_Store or .idea/ belongs in your global ignore file instead — see the end of this section. And while a
repo's main .gitignore lives at the root, you can drop extra ones in any subfolder to scope rules
to just that corner of the project.)
Pattern Syntax Essentials
*.log— a glob: matches any file ending in .log, in any directorydist/— a trailing slash matches only directories (and everything inside them)/config.json— a leading slash anchors the pattern to the repository rootdocs/**/*.tmp—**matches any depth of nested directories!.env.example— a leading!negates a pattern, re-including a file an earlier rule excluded (it can't re-include anything inside an excluded directory, though)
The Gotcha: Already-Tracked Files Stay Tracked
.gitignore only affects untracked files. If a file was already committed before you added it to .gitignore, Git keeps tracking it — and keeps committing your changes to it. To stop tracking a file without deleting it from your disk, remove it from the staging area (which Git's own messages call the index — same thing, older name):
git rm --cached .env
# For a whole directory:
git rm -r --cached node_modules
# Then commit the removal
git commit -m "chore: stop tracking ignored files"git filter-repo — specialized surgery beyond this guide, but worth knowing the
name. Rotation comes first; no amount of history surgery un-leaks a key that's already been seen.A Global Ignore File for OS and Editor Junk
Files like .DS_Store (macOS) or Thumbs.db (Windows) are about your machine, not the project. Instead of asking every
repository to ignore them, configure a global ignore file that applies to all your
repositories:
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo "Thumbs.db" >> ~/.gitignore_globalLine Endings: the Cross-Platform Papercut
Windows ends lines with CRLF, macOS and Linux with LF. On a mixed team this produces the classic horror diff: every line of the file "changed" because a teammate's editor rewrote the endings — and AI agents reformatting files make it worse.
The team-level fix is a committed .gitattributes file, which overrides everyone's personal settings:
# Commit LF, let Git normalize what each OS checks out
* text=auto
# Files that must keep exact bytes, no conversion ever
*.png binary
*.webp binary(You may also see core.autocrlf in older guides — that's the per-machine version of the same idea. Prefer .gitattributes: it lives in the repo, so it protects teammates and agents
who never configured anything. If a diff ever claims every line changed, check line endings
before anything else.)
"Write a .gitignore for this project — look at my stack and include editor and OS artifacts"
"Check my repo for tracked files that look like secrets or build artifacts that shouldn't be committed"
Loading challenge...