Build the mental model
An AI coding agent can produce in thirty seconds what would take a careful developer an hour: a new feature across five files, a refactor touching every controller, a dependency bump plus the code changes it requires. That speed is genuinely useful, and it is exactly why Git matters more here, not less.
A human writing code slowly tends to self-review as they go, sentence by sentence. An agent does not pause between files, and it has no stake in whether the change is actually correct -- it optimizes for producing a plausible diff, not for the judgment of whether that diff should exist.
- A known-good checkpoint commit before the agent starts.
- A full `git diff` review before anything gets trusted.
- A clean way back (restore, reset, or revert) if the result is wrong.
Git is what turns "the agent changed a lot of files" from a leap of faith into something you can actually inspect and, if needed, undo. None of this is exotic -- it is the same commands you already use, applied with one extra rule: never skip the review step just because the agent wrote the code quickly.
Never let an agent run destructive Git commands unsupervised
An AI agent should not be given unrestricted, unreviewed access to destructive Git operations -- force-push, hard reset, history rewriting -- by default. Treat every agent-generated diff the way you'd treat a stranger's pull request: read it before you trust it, and keep the checkpoint commit as your unconditional way back.
THE AGENT-EDIT REVIEW LOOP
--------------------------
STEP 1 commit a clean checkpoint (safe point to return to)
|
v
STEP 2 AI agent edits code (often touches several files fast)
|
v
STEP 3 git diff -- read every changed line before anything else
|
v
STEP 4 run lint + tests
|
v
STEP 5 extra scrutiny: auth, secrets, permissions, dependencies
|
+-------------------------------+
| |
change is GOOD change is BAD
| |
v v
git add + commit git restore / reset / revert
(describe what changed) (back to the checkpoint)
| |
+---------------+----------------+
|
v
repeat for the next agent taskConnect it to a real scenario
You will work through two rounds of a simulated agent task inside a real Git repository. A checkpoint commit locks in a known-good state -- a small `calc.py` module with a passing test -- before any agent touches it. The recommended workflow, in order:
Commit a checkpoint
Before giving the agent a task, commit a clean, known-good state. This is the point you can always return to.
Let the agent edit
Hand off the task. The agent may touch several files in a single pass.
Review the diff
Run `git diff` and read every changed line before doing anything else. Never assume agent-written code is correct just because it looks plausible.
Run lint and tests
Execute the project's lint and test suite against the change, exactly as you would for a human-written pull request.
Scrutinize security-sensitive files
Give extra attention to any file touching auth, secrets, permissions, or dependencies -- exactly where a subtle, dangerous change can hide.
Commit or roll back
If the change is good, `git add` and `git commit` it with a real description. If it's not, `git restore`, `git reset`, or `git revert` back to the checkpoint rather than hand-editing around the problem.
In round one, the simulated agent edit adds a real helper function and also hardcodes an API key into a config file -- the diff review catches it, and the whole change is rejected with `git restore`. In round two, a well-scoped function-and-test change is reviewed, tested, and committed for real. Same commands both times; what changes is whether you looked before deciding.
Never let an agent run destructive Git commands unsupervised
Agent-generated code should never be blindly committed, and an AI agent should never be given unrestricted, unreviewed access to destructive Git operations -- force-push, hard reset, history rewriting -- by default. The checkpoint commit and the diff review are not optional extras; they are what makes using an agent safe to do quickly.
Try the working example
# One-time setup (once per machine)
git config --global init.defaultBranch main
mkdir project && cd project
git init
# calc.py -- the module the agent will be asked to extend
cat > calc.py <<'EOF'
def add(a, b):
return a + b
EOF
# test_calc.py -- a passing test for the current code
cat > test_calc.py <<'EOF'
from calc import add
def test_add():
assert add(2, 3) == 5
EOF
# config.py -- a normal, non-secret config module
cat > config.py <<'EOF'
import os
DATABASE_URL = os.environ.get("DATABASE_URL", "postgres://localhost/myapp_dev")
DEBUG = True
EOF
export GIT_AUTHOR_NAME="Thuta Learner"
export GIT_AUTHOR_EMAIL="learner@example.com"
export GIT_COMMITTER_NAME="Thuta Learner"
export GIT_COMMITTER_EMAIL="learner@example.com"
export GIT_AUTHOR_DATE="2026-02-01T09:00:00"
export GIT_COMMITTER_DATE="2026-02-01T09:00:00"
git add calc.py test_calc.py config.py
git commit -m "Checkpoint: add() function, tests, and config pass before starting agent task"
# ======================================================================
# ROUND 1 -- a BAD agent edit: a real helper PLUS a hardcoded secret
# ======================================================================
# What the agent's round-1 edit actually did to the two files:
printf '\ndef multiply(a, b):\n return a * b\n' >> calc.py
printf '\nSTRIPE_API_KEY = "sk_live_51H8x2example_do_not_commit"\n' >> config.py
git status
git diff
# Review catches the hardcoded key in config.py -- reject the whole change
git restore calc.py config.py
git status # back to a clean checkpoint, nothing lost
# ======================================================================
# ROUND 2 -- a GOOD agent edit: a well-scoped function + test
# ======================================================================
# What the agent's round-2 edit actually did to the two files:
printf '\ndef subtract(a, b):\n return a - b\n' >> calc.py
printf '\ndef test_subtract():\n assert subtract(5, 3) == 2\n' >> test_calc.py
sed -i 's/from calc import add$/from calc import add, subtract/' test_calc.py
git diff
# Run the test suite before trusting the change
python -c "import test_calc; test_calc.test_add(); test_calc.test_subtract(); print('2 passed')"
export GIT_AUTHOR_DATE="2026-02-01T09:20:00"
export GIT_COMMITTER_DATE="2026-02-01T09:20:00"
git add calc.py test_calc.py
git commit -m "Add subtract() function with test (reviewed AI agent edit)"
git log --onelineRound 1's `git diff` shows exactly what the simulated agent touched:
diff --git a/calc.py b/calc.py
index 4693ad3..3581473 100644
--- a/calc.py
+++ b/calc.py
@@ -1,2 +1,6 @@
def add(a, b):
return a + b
+
+
+def multiply(a, b):
+ return a * b
diff --git a/config.py b/config.py
index 9b58e1e..5452405 100644
--- a/config.py
+++ b/config.py
@@ -2,3 +2,4 @@ import os
DATABASE_URL = os.environ.get("DATABASE_URL", "postgres://localhost/myapp_dev")
DEBUG = True
+STRIPE_API_KEY = "sk_live_51Hc9example1234567890abcdef"
The `multiply()` addition is harmless, but a hardcoded key in `config.py` is a real problem -- so the whole change is rejected. `git restore calc.py config.py` returns `git status` to `nothing to commit, working tree clean`, and the checkpoint commit is untouched.
Round 2's `git diff` only touches calc.py and test_calc.py -- no config or dependency file in sight:
diff --git a/calc.py b/calc.py
index 4693ad3..3b474e9 100644
--- a/calc.py
+++ b/calc.py
@@ -1,2 +1,6 @@
def add(a, b):
return a + b
+
+
+def subtract(a, b):
+ return a - b
diff --git a/test_calc.py b/test_calc.py
index d509f32..38c0994 100644
--- a/test_calc.py
+++ b/test_calc.py
@@ -1,5 +1,9 @@
-from calc import add
+from calc import add, subtract
def test_add():
assert add(2, 3) == 5
+
+
+def test_subtract():
+ assert subtract(5, 3) == 2
Running the tests prints `2 passed`. This time the change is accepted:
[main de6c5a8] Add subtract() function with test (reviewed AI agent edit)
2 files changed, 9 insertions(+), 1 deletion(-)5-minute try-it
You give an AI coding agent a small task and it comes back having touched two files at once. `git diff` shows a new `multiply()` function in `calc.py` -- clean, tested-looking, unremarkable -- and, in the same pass, a new line in `config.py`: `STRIPE_API_KEY = "sk_live_51Hc9example1234567890abcdef"`. Nothing crashed. Nothing looks obviously broken. The function alone would be a fine change to accept. Before you run `git add` on everything the agent touched, what should stop you, and which specific file in this diff deserves closer attention than the rest?
One important caution
Committing an agent's change immediately without reading the diff defeats the entire safety net -- reviewing before trusting is the whole point.
Letting an agent run `git push --force`, `git reset --hard`, or rewrite history on its own removes your ability to recover if something goes wrong.
Pro Git -- Git Basics: Undoing Things — Git & GitHub
Git & GitHub Glossary — Common Terms
| Term | Meaning |
|---|---|
| Version Control | A system that records changes to files over time so you can review or restore any earlier version. |
| Git | The distributed version-control tool that tracks a project's history locally on your machine. |
| GitHub | A web platform built on top of Git that hosts repositories and adds collaboration features like Pull Requests, Issues, and Actions. |
| Repository | The folder Git tracks, containing your files plus the full history of changes to them. |
| Local Repository | The copy of a repository that lives on your own machine, where you actually commit changes. |
| Remote Repository | A copy of a repository hosted elsewhere, such as on GitHub, that your local repository pushes to and pulls from. |
| Working Directory | The actual files on disk that you're currently editing, before anything is staged or committed. |
| Staging Area | The holding area (also called the index) where changes wait after `git add` and before `git commit`. |
| Commit | A saved snapshot of the staged changes, permanently recorded in the repository's history. |
| Commit Hash | The unique SHA identifier Git assigns to every commit, used to reference it exactly. |
| Branch | A movable pointer to a line of commits, letting you develop separate lines of work in parallel. |
| main | The conventional default branch name for a repository's primary line of history. |
| HEAD | A pointer to the commit your working directory currently reflects -- usually the tip of your current branch. |
| Detached HEAD | A state where HEAD points directly at a commit instead of a branch, so new commits won't belong to any branch unless you create one. |
| Merge | Combining the changes from one branch into another, creating a new commit that joins both histories. |
| Merge Conflict | A situation where Git can't automatically combine two changes to the same lines and asks you to resolve it by hand. |
| Clone | Downloading a full copy of a remote repository, including its entire history, to your own machine. |
| Fork | A personal, independent copy of someone else's GitHub repository that you can freely change without affecting the original. |
| Remote | A named reference to another copy of a repository, most commonly the one hosted on GitHub. |
| origin | The conventional default name Git gives the remote a repository was cloned from. |
| Push | Sending your local commits to a remote repository so others can see them. |
| Pull | Fetching commits from a remote and merging them into your current local branch in one step. |
| Fetch | Downloading a remote's latest commits and branches without merging them into your working branch. |
| Pull Request | A GitHub request to merge one branch's changes into another, opening a space for discussion and review first. |
| Code Review | The practice of having another person examine a proposed change before it's merged, to catch bugs and share knowledge. |
| Issue | A GitHub tracker entry used to report a bug, request a feature, or discuss project work. |
| Tag | A fixed, named pointer to a specific commit, typically used to mark a release point in history. |
| Semantic Versioning | A MAJOR.MINOR.PATCH version-numbering convention that signals whether a release is breaking, additive, or a fix. |
| Release | A packaged, named snapshot of a repository at a tag, often bundled with build artifacts and release notes. |
| Rebase | Replaying a branch's commits onto a new base commit, producing a straighter, linear history. |
| Reset | Moving the current branch pointer to a different commit, optionally changing the staging area and working directory too. |
| Revert | Creating a new commit that undoes the changes from an earlier commit, without rewriting history. |
| Stash | Temporarily shelving uncommitted changes so you can switch context and reapply them later. |
| Cherry-pick | Applying one specific commit from another branch onto your current branch. |
| Bisect | A binary-search tool that finds the exact commit that introduced a bug by testing commits between a known-good and known-bad point. |
| Reflog | Git's local log of every place HEAD and branches have pointed to, useful for recovering commits that seem lost. |
| Git Hook | A script Git runs automatically at a specific point in the workflow, such as before a commit or before a push. |
| CI | Continuous Integration: automatically building and testing every change as soon as it's pushed. |
| CD | Continuous Delivery/Deployment: automatically preparing or shipping a passing build to users after CI succeeds. |
| GitHub Actions | GitHub's built-in automation platform for running CI/CD workflows directly from a repository. |
| Secret | A sensitive value, like an API key or password, stored securely rather than committed into source code. |
| SSH | A secure protocol commonly used to authenticate with GitHub over the network without typing a password each time. |
| Personal Access Token | A password-like credential you generate on GitHub to authenticate scripts, tools, or HTTPS Git operations. |
| Organization | A shared GitHub account that holds multiple repositories and manages team member permissions collectively. |
| Branch Protection | GitHub repository settings that require checks, like reviews or passing CI, before a branch such as `main` can be updated. |
| Open Source | Software whose source code is publicly available for anyone to view, use, modify, and contribute to. |
Git Command Cheat Sheet
| Command | What it does |
|---|---|
| git init | Create a brand-new Git repository in the current folder. |
| git clone <url> | Download a full copy of a remote repository, including its history. |
| git status | Show the working directory and staging area's current state. |
| git log | Show commit history; add --oneline --graph --all for a compact branch map. |
| git diff | Show exactly what changed, line by line, before you stage or commit it. |
| git add <file> | Move a change from the working directory into the staging area. |
| git commit -m "..." | Save the staged changes as a new, permanent snapshot in history. |
| git branch | List, create, or delete branches. |
| git switch <branch> | Move your working directory to point at a different branch. |
| git merge <branch> | Combine another branch's changes into your current branch. |
| git remote -v | List the remotes this repository knows about and their URLs. |
| git fetch | Download a remote's latest commits and branches without merging them. |
| git pull | Fetch and merge a remote's changes into your current branch in one step. |
| git push | Send your local commits to a remote repository. |
| git tag <name> | Mark the current commit with a fixed, named pointer, typically for a release. |
| git restore <file> | Discard uncommitted changes to a file, or unstage it, without touching history. |
| git revert <commit> | Undo a commit's changes safely by creating a new commit, keeping history intact. |
| git reset <commit> | Move the current branch pointer to a different commit, rewriting local history. |
| git rebase <branch> | Replay your branch's commits onto a new base for a straighter history. |
| git cherry-pick <commit> | Apply one specific commit from another branch onto your current branch. |
| git stash | Temporarily shelve uncommitted changes so you can switch context cleanly. |
| git reflog | Show every place HEAD has pointed to recently, useful for recovering 'lost' commits. |
| git bisect | Binary-search through commit history to find exactly which commit introduced a bug. |