working tree, staging, commit — the model ai breaks first
Git has three states, and AI confuses them constantly
Cursor just made twelve edits across nine files and ran git commit -am "refactor". Did it commit all of your changes? Half? The wrong half?
You can't answer until you understand git's three-state model. Skip
this and you'll either lose work or ship secrets.
The three states
Every file in a git repo lives in one of three places:
working tree ──> staging area (index) ──> commit (HEAD)
(your edits) (git add) (git commit)
- Working tree. The files on disk as you've edited them. Cursor writes here.
- Staging area (also called the index). A snapshot of changes
you've marked for the next commit.
git add file.pymoves changes from working tree → staging. - The last commit (HEAD). What's actually saved in history.
git commitpackages everything in staging into a new commit.
That arrow is one-way for git add and git commit. It's also
reversible: git restore --staged file un-stages, git restore file
discards working-tree changes.
The four file categories git status shows you
Changes to be committed: # staged - will land in next commit
modified: src/auth.py
Changes not staged for commit: # modified - in working tree only
modified: README.md
Untracked files: # untracked - git has never seen these
scratch.py
A file can be in multiple categories at once. If you git add then
edit again, the same file shows up under both "staged" and "modified".
The staged version is what'll commit; the new edits won't.
What AI does that breaks this
Three patterns Cursor and Claude Code fall into:
git add .thengit commit. Stages every file, including.envfiles, scratch notebooks, and large binaries. Always rungit statusBEFORE adding, or stage specific files by name.git commit -am "...". The-aauto-stages every modified tracked file. It does NOT add untracked files (good), but it also doesn't pause to let you review. Cursor uses this constantly.- Forgetting
.gitignore. AI happily commitsnode_modules/,__pycache__/, and your virtualenv if there's no.gitignore. Check the diff before pushing.
The two CLIs you'll use every day
git— the version-control tool. Local commits, branches, diffs, history. Doesn't know GitHub exists.gh— GitHub's CLI. PRs, issues, releases, repo creation. Wraps the GitHub API so you don't have to leave the terminal.
Real workflow:
git checkout -b feature/auth-fix # new branch
# make edits
git add src/auth.py # stage one specific file
git commit -m "fix: token refresh" # commit
git push -u origin feature/auth-fix # push (first time uses -u)
gh pr create --fill # open the PR from the terminal
gh pr view --web # open it in the browser
Browser note: we can't shell out to
githere, so we'll use Python dicts that match the structured output ofgit statusandgh pr list. The patterns and decisions are identical.
Run the editor. We render a parsed git status for a typical mid-edit
state.