How Do I Set Up a Safe Git Workflow for AI Coding Assistants to Prevent Code Loss?
I lost three hours of work yesterday.
My AI coding assistant decided to run git checkout . on my main branch while I had uncommitted changes. Just like that, my work vanished. No warning. No confirmation. No mercy.
If you’re using AI coding assistants like Claude Code, OpenCode CLI, or GitHub Copilot, you’re probably one wrong instruction away from the same disaster. These tools are powerful but they don’t understand the value of your uncommitted work.
Let me show you how I built a defense system that prevents this from ever happening again.
The Problem: AI Assistants Don’t Respect Your Workflow
AI coding assistants operate with good intentions but dangerous assumptions:
- They assume you want clean, working directories
- They’re happy to run destructive git commands without asking
- They’ll commit directly to your main branch
- They don’t naturally create feature branches
- They skip running tests before committing
I learned this the hard way. After my first disaster, I added more rules. After my second near-miss, I realized I needed a systematic approach.
The Solution: Three Layers of Protection
I built my defense in three layers:
- Global AGENTS.md - Rules that the AI assistant reads before every task
- Git Aliases - Safe workflow shortcuts I can invoke
- Shell Scripts - Automated checks and safeguards
Let me walk you through each layer.
Layer 1: The AGENTS.md Configuration File
The most effective protection I found was creating a global AGENTS.md file. This file lives in my home directory and gets loaded by the AI assistant before every session.
Here’s the exact configuration I use:
# Git Workflow Rules (MANDATORY)
Before starting ANY coding task:1. Check if current branch has uncommitted changes2. If yes, COMMIT or STASH before continuing3. Create a new feature branch before editing: `git checkout -b agent/<short-task-name>`4. NEVER commit directly to main or master
## Commit Message Format- Use conventional commits: feat:, fix:, refactor:, docs:, test:, chore:- Write clear, descriptive messages- Keep commits atomic and focused
## Before Committing- Run tests if they exist- Run linters if configured- Ensure the project builds- Verify no secrets in code
## Branch Naming- Use format: agent/<short-task-name>- Examples: agent/add-auth, agent/fix-login, agent/refactor-cache
## Forbidden Actions- NO `git checkout .` without explicit user confirmation- NO `git reset --hard` without explicit user confirmation- NO `git push --force` to main/master- NO skipping hooks with --no-verify
## Session Export- Use `opencode export` to save session history- Keep records for debugging and traceabilityThis file creates a contract between you and your AI assistant. The assistant reads these rules before starting any task.
How to Set Up Global AGENTS.md
Place this file in your home directory:
# Create the global AGENTS.md filecat > ~/.claude/AGENTS.md << 'EOF'# Git Workflow Rules (MANDATORY)
Before starting ANY coding task:1. Check if current branch has uncommitted changes2. If yes, COMMIT or STASH before continuing3. Create a new feature branch before editing: git checkout -b agent/<short-task-name>4. NEVER commit directly to main or masterEOF
# Verify the file was createdcat ~/.claude/AGENTS.mdFor Claude Code, this file is automatically loaded. For other assistants, you may need to reference it in your project’s .cursorrules or similar configuration.
Layer 2: Git Aliases for Safe Workflows
I created git aliases that enforce safe patterns. These aliases act as guardrails:
# Add these to your ~/.gitconfig
# Safe start: creates feature branch with safeguardsgit config --global alias.safe-start '!f() { # Check for uncommitted changes if ! git diff-index --quiet HEAD --; then echo "ERROR: You have uncommitted changes. Commit or stash first." exit 1 fi
# Check if on main/master branch=$(git rev-parse --abbrev-ref HEAD) if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then if [ -z "$1" ]; then echo "ERROR: Please provide a branch name: git safe-start <branch-name>" exit 1 fi git checkout -b "agent/$1" echo "Created and switched to agent/$1" else echo "Already on branch: $branch" fi}; f'
# Safe commit: runs tests before committinggit config --global alias.safe-commit '!f() { # Run tests if package.json exists if [ -f "package.json" ]; then echo "Running tests..." npm test if [ $? -ne 0 ]; then echo "ERROR: Tests failed. Fix before committing." exit 1 fi fi
# Commit with message if [ -z "$1" ]; then echo "ERROR: Please provide a commit message." exit 1 fi
git commit -m "$1"}; f'
# Check status before destructive operationsgit config --global alias.safe-checkout '!f() { if ! git diff-index --quiet HEAD --; then echo "WARNING: You have uncommitted changes!" echo "Files modified:" git status --short echo "" echo "Use 'git stash' to save changes or 'git commit' to keep them." read -p "Continue with checkout? (y/N): " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then exit 1 fi fi git checkout "$@"}; f'
# Show current workflow statusgit config --global alias.workflow-status '!f() { echo "=== Current Branch ===" git branch --show-current echo "" echo "=== Uncommitted Changes ===" if git diff-index --quiet HEAD --; then echo "No uncommitted changes" else git status --short fi echo "" echo "=== Recent Commits ===" git log --oneline -5}; f'Now instead of running git checkout . (dangerous), I tell my AI assistant: “Run git workflow-status first, then use safe-start to create a branch.”
Using the Aliases
Here’s my typical workflow with an AI assistant:
# 1. Check current stategit workflow-status
# 2. Start new work safelygit safe-start add-user-auth
# 3. Make changes with AI assistant...# (AI makes file edits)
# 4. Commit safely with testsgit safe-commit "feat: add user authentication"
# 5. Push to remotegit push -u origin agent/add-user-authLayer 3: Shell Script for Complete Safety
For maximum protection, I wrapped everything into a single script:
#!/bin/bash
# AI-Safe Git Workflow Script# Prevents AI coding assistants from destroying work
set -e
COMMAND=${1:-"status"}BRANCH_NAME=${2:-""}
# Colors for outputRED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}[INFO]${NC} $1"}
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"}
log_error() { echo -e "${RED}[ERROR]${NC} $1"}
check_uncommitted() { if ! git diff-index --quiet HEAD --; then return 0 # Has uncommitted changes fi return 1 # Clean}
get_current_branch() { git rev-parse --abbrev-ref HEAD}
is_protected_branch() { local branch=$(get_current_branch) [ "$branch" = "main" ] || [ "$branch" = "master" ]}
cmd_status() { log_info "Current branch: $(get_current_branch)" echo ""
if check_uncommitted; then log_warn "You have uncommitted changes:" git status --short echo "" log_warn "Run 'commit' or 'stash' before starting new work." else log_info "Working directory is clean" fi
echo "" log_info "Recent commits:" git log --oneline -5}
cmd_start() { if [ -z "$BRANCH_NAME" ]; then log_error "Please provide a branch name: ./ai-safe-workflow.sh start <branch-name>" exit 1 fi
if check_uncommitted; then log_error "You have uncommitted changes!" echo "" git status --short echo "" log_error "Commit or stash before starting new work." exit 1 fi
if is_protected_branch; then git checkout -b "agent/$BRANCH_NAME" log_info "Created and switched to agent/$BRANCH_NAME" else log_warn "Already on branch: $(get_current_branch)" read -p "Create new branch anyway? (y/N): " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then git checkout -b "agent/$BRANCH_NAME" fi fi}
cmd_commit() { local message="$BRANCH_NAME"
if [ -z "$message" ]; then log_error "Please provide a commit message: ./ai-safe-workflow.sh commit \"your message\"" exit 1 fi
# Check for tests if [ -f "package.json" ] && grep -q '"test"' package.json; then log_info "Running tests..." if npm test; then log_info "Tests passed" else log_error "Tests failed. Fix before committing." exit 1 fi fi
# Check for linter if [ -f "package.json" ] && grep -q '"lint"' package.json; then log_info "Running linter..." if npm run lint; then log_info "Linting passed" else log_warn "Linting issues found. Review before committing." read -p "Continue with commit? (y/N): " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then exit 1 fi fi fi
git commit -m "$message" log_info "Committed: $message"}
cmd_stash() { if check_uncommitted; then git stash push -m "ai-safe-backup-$(date +%Y%m%d-%H%M%S)" log_info "Stashed uncommitted changes" else log_info "Nothing to stash" fi}
cmd_unstash() { git stash pop log_info "Restored stashed changes"}
cmd_export-session() { if command -v opencode &> /dev/null; then opencode export log_info "Session exported" else log_warn "opencode CLI not found. Install it for session export." fi}
# Main command routercase $COMMAND in status) cmd_status ;; start) cmd_start ;; commit) cmd_commit ;; stash) cmd_stash ;; unstash) cmd_unstash ;; export) cmd_export-session ;; *) echo "Usage: $0 {status|start|commit|stash|unstash|export}" echo "" echo "Commands:" echo " status - Show current workflow status" echo " start - Create new feature branch safely" echo " commit - Commit with tests and lint checks" echo " stash - Stash uncommitted changes" echo " unstash - Restore stashed changes" echo " export - Export session history (requires opencode)" exit 1 ;;esacMake it executable and available:
chmod +x ai-safe-workflow.shsudo mv ai-safe-workflow.sh /usr/local/bin/ai-safe
# Now you can use it anywhereai-safe statusai-safe start fix-login-bugai-safe commit "fix: resolve login redirect issue"Why This Matters
After implementing this workflow, I’ve had zero code loss incidents. Here’s why each layer matters:
AGENTS.md catches problems before they happen. The AI reads the rules and adjusts its behavior. It’s like having a senior developer watching over the AI’s shoulder.
Git aliases create muscle memory. Even when I’m working without AI, I use git safe-start because it’s safer than raw git commands.
Shell scripts provide a single source of truth. When I’m stressed or rushing, I don’t have to remember the safe sequence. I just run ai-safe status and follow the prompts.
Common Mistakes to Avoid
I’ve made these mistakes so you don’t have to:
Mistake 1: Relying on Single-Layer Protection
At first, I only used the AGENTS.md file. But sometimes the AI assistant would skip reading it, or I’d forget to reference it. The triple-layer approach gives you defense in depth.
Mistake 2: Not Running Tests Before Committing
I once had an AI assistant commit code that broke the build. The tests existed but weren’t run. Now my safe-commit alias always runs tests.
Mistake 3: Working Directly on Main Branch
This is how my first disaster happened. I was making “small fixes” directly on main. The AI ran git checkout . and my changes were gone. Now I always use feature branches, even for tiny changes.
Mistake 4: Not Stashing Before Destructive Operations
Sometimes you need to switch contexts quickly. I used to just checkout another branch, losing uncommitted work. Now I always stash first with ai-safe stash.
Mistake 5: Skipping Conventional Commits
Without conventional commit format, my git history was a mess. AI assistants would write vague messages like “updated code.” The AGENTS.md rules enforce better messages.
How I Use This Daily
Here’s my typical workflow when starting a new task with an AI assistant:
- Check status:
ai-safe status - Start clean: If dirty,
ai-safe stashor commit first - Create branch:
ai-safe start my-feature - Work with AI: Let the assistant make changes
- Review: Check what files changed
- Commit safely:
ai-safe commit "feat: my feature" - Push:
git push -u origin agent/my-feature - Create PR: Use GitHub CLI or web interface
This workflow has become second nature. The AI assistant respects it, I respect it, and my code stays safe.
Related Knowledge
Branch Protection Rules: Beyond local safeguards, enable branch protection on your remote repository. This prevents force pushes to main/master even if someone (or some AI) tries to bypass local rules.
Git Hooks: Consider using pre-commit hooks for additional safety. Tools like Husky can run linting, formatting, and tests automatically before each commit.
Session Export: If you’re using OpenCode CLI, the opencode export command saves your session history. This is invaluable for debugging what went wrong and for compliance purposes.
Conventional Commits: The feat/fix/refactor prefix system enables automatic changelog generation. Tools like standard-version can create release notes from your commit history.
Final Thoughts
AI coding assistants are transformative productivity tools, but they need guardrails. The three-layer protection system I’ve shown you prevents the most common disasters:
- Global AGENTS.md sets the rules
- Git aliases create safe shortcuts
- Shell scripts enforce the workflow
Start with the AGENTS.md file. It’s the highest-impact, lowest-effort improvement you can make today. Add the aliases and scripts as you get comfortable with the workflow.
Your future self will thank you when you realize you just prevented a catastrophic code loss. Trust me.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments