Skip to content

Instantly share code, notes, and snippets.

@ben-vargas
Created August 20, 2025 14:44
Show Gist options
  • Select an option

  • Save ben-vargas/e6064aadb5310afe069d1cbd172a322b to your computer and use it in GitHub Desktop.

Select an option

Save ben-vargas/e6064aadb5310afe069d1cbd172a322b to your computer and use it in GitHub Desktop.

Git Bare Repository Worktree Workflow for Claude Code

🚨 CRITICAL: Understanding My Project Structure

I use a bare Git repository approach with Git worktrees extensively. Every subdirectory in my projects represents a different git branch as a worktree.

When you see a project like /home/code/projects/my-app/:

  • my-app/ = project container (NOT a working tree)
  • my-app/.bare/ = actual Git repository database
  • my-app/.git = pointer file directing Git commands to .bare/
  • All other subdirectories = worktrees representing different branches

Typical Directory Structure

project-name/                # Project container (NOT a working tree)
├── .bare/                   # 🗄️  Bare git repository (the actual Git database)
├── .git                     # 🔗 Pointer file directing Git commands to .bare
├── main/                    # ✅ Worktree (branch: main)
├── feature-auth/            # ✅ Worktree (branch: feature-auth)
└── hotfix-security/         # ✅ Worktree (branch: hotfix-security)

⚠️ Critical: Upstream Tracking & Remote Configuration

1. Verify Remote Configuration

Before setting up worktrees, ensure your remotes are properly configured:

git remote -v  # Check existing remotes

# If no origin exists (rare but possible):
git remote add origin <your-repo-url>

# For forked repositories, also add upstream:
git remote add upstream <original-repo-url>

2. Every Worktree Needs Upstream Tracking

EVERY worktree needs upstream tracking configured! Without this, git pull and git push will fail with "no tracking information" errors.

  • When creating new branches: Use git push -u origin branch-name
  • When adding worktrees for existing remote branches: Run git branch --set-upstream-to=origin/branch-name branch-name
  • This applies to ALL worktrees, not just main!

Initial Setup (New Projects)

# Clone and setup bare repository
git clone --bare <repository-url> .bare
echo "gitdir: ./.bare" > .git
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"

# For forked repositories, add upstream remote
git remote add upstream <original-repo-url>  # Only if this is a fork

# Fetch and create main worktree
git fetch origin
git worktree add main main
cd main && git branch --set-upstream-to=origin/main main && cd ..

Daily Workflow

  1. Navigate to branch: cd main/ or cd feature-auth/
  2. Work normally within each worktree directory
  3. Create worktrees from project root: git worktree add new-feature -b new-feature
  4. Switch branches by changing directories (never use git checkout)

Essential Commands

# Worktree management (from project root)

# For NEW branches (not on remote yet):
git worktree add branch-name -b branch-name main  # Create new
cd branch-name && git push -u origin branch-name && cd ..  # Push and set upstream

# For EXISTING remote branches:
git worktree add branch-name branch-name  # Create worktree
cd branch-name && git branch --set-upstream-to=origin/branch-name branch-name && cd ..

# Other commands:
git worktree remove branch-name                   # Remove
git worktree list                                 # List all
git worktree prune                               # Clean stale refs

# Verification
pwd && git status                                # Where am I?
git worktree list                                # Show all worktrees

# Custom scripts (available globally)
wt feature-name [base-branch]                    # Quick create + navigate
wt-status                                        # Status of all worktrees

# For forked repositories - sync with upstream
git fetch upstream                               # Get latest from original repo
cd main && git merge upstream/main && cd ..      # Update main from upstream

Important Guidelines for Claude Code

✅ DO:

  • Run git worktree list from project root to see all worktrees
  • Navigate to correct worktree directory before making changes
  • Use branch names directly as directory names
  • Run worktree management (add/remove/list) from project root

❌ DON'T:

  • Don't run git commands from project root (except worktree management)
  • Don't use git checkout - change directories instead
  • Don't assume any directory is special - all worktrees are equal

Migration from Old Worktree Setup

To convert from old "main/ manages worktrees" approach:

# 1. Backup and identify structure
cp -r my-project my-project-backup
cd my-project && ls -la

# 2. Remove existing worktrees (from main/)
cd main/
git worktree list  # See what exists
git worktree remove ../feature-branch  # Remove all except main/

# 3. Create bare structure
cd .. && git clone --bare main/.git .bare
rm -rf main/.git && echo "gitdir: ./.bare" > .git

# 4. Configure and recreate worktrees
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin
mv main main-old && git worktree add main main
cd main && git branch --set-upstream-to=origin/main main && cd ..
# Copy uncommitted changes from main-old to main/
rm -rf main-old

# 5. Recreate other worktrees
git worktree add feature-branch feature-branch
cd feature-branch && git branch --set-upstream-to=origin/feature-branch feature-branch && cd ..

# 6. Verify
git worktree list && wt-status

Summary

Every subdirectory is a worktree representing a different branch. Project root contains bare repository for worktree management only. Navigate between branches by changing directories. All git operations happen within worktree directories.

#!/bin/bash
# wt-status - Show status of all worktrees in a project
# Usage: wt-status
# Shows git status for each worktree in the current project
set -e
# Find the project root (directory containing .bare and .git)
project_root="$(pwd)"
while [ "$project_root" != "/" ]; do
if [ -d "$project_root/.bare" ] && [ -f "$project_root/.git" ]; then
break
fi
project_root="$(dirname "$project_root")"
done
if [ "$project_root" = "/" ] || [ ! -d "$project_root/.bare" ]; then
echo "Error: Not in a bare repository worktree project"
echo "Expected to find .bare/ directory and .git pointer file"
exit 1
fi
# Change to project root for worktree operations
cd "$project_root"
echo "=== Worktree Status Report ==="
echo "Project: $(basename "$project_root")"
echo ""
# Get list of worktrees and process each one
git worktree list --porcelain | while IFS= read -r line; do
if [[ $line == worktree* ]]; then
worktree_path="${line#worktree }"
worktree_name="$(basename "$worktree_path")"
elif [[ $line == branch* ]]; then
branch_name="${line#branch refs/heads/}"
echo "📁 $worktree_name [$branch_name]"
if [ -d "$worktree_path" ]; then
cd "$worktree_path"
# Check for uncommitted changes
if ! git diff-index --quiet HEAD 2>/dev/null; then
echo " ⚠️ Uncommitted changes:"
git status --porcelain | sed 's/^/ /'
else
echo " ✅ Clean working tree"
fi
# Check for unpushed commits
if git rev-parse --verify "@{upstream}" >/dev/null 2>&1; then
ahead_behind=$(git rev-list --left-right --count @{upstream}...HEAD 2>/dev/null)
behind=${ahead_behind% *}
ahead=${ahead_behind#* }
if [ "$ahead" -gt 0 ] && [ "$behind" -gt 0 ]; then
echo " 🔄 $ahead ahead, $behind behind upstream"
elif [ "$ahead" -gt 0 ]; then
echo " ⬆️ $ahead commits ahead of upstream"
elif [ "$behind" -gt 0 ]; then
echo " ⬇️ $behind commits behind upstream"
else
echo " 🟰 Up to date with upstream"
fi
else
echo " 📡 No upstream branch set"
fi
cd "$project_root"
else
echo " ❌ Worktree directory not found: $worktree_path"
fi
echo ""
fi
done
#!/bin/bash
# wt - Quick worktree creation and navigation
# Usage: wt <branch-name> [base-branch]
# Creates a new worktree and navigates to it
set -e
if [ $# -eq 0 ]; then
echo "Usage: wt <branch-name> [base-branch]"
echo "Creates a new worktree and navigates to it"
echo ""
echo "Examples:"
echo " wt feature-auth # Create from current HEAD"
echo " wt hotfix-bug main # Create from main branch"
echo " wt experiment-ui develop # Create from develop branch"
exit 1
fi
branch_name="$1"
base_branch="${2:-HEAD}"
# Find the project root (directory containing .bare and .git)
project_root="$(pwd)"
while [ "$project_root" != "/" ]; do
if [ -d "$project_root/.bare" ] && [ -f "$project_root/.git" ]; then
break
fi
project_root="$(dirname "$project_root")"
done
if [ "$project_root" = "/" ] || [ ! -d "$project_root/.bare" ]; then
echo "Error: Not in a bare repository worktree project"
echo "Expected to find .bare/ directory and .git pointer file"
exit 1
fi
# Change to project root for worktree operations
cd "$project_root"
# Check if worktree already exists
if [ -d "$branch_name" ]; then
echo "Error: Directory '$branch_name' already exists"
exit 1
fi
# Create the worktree
echo "Creating worktree '$branch_name' from '$base_branch'..."
git worktree add "$branch_name" -b "$branch_name" "$base_branch"
# Navigate to the new worktree
cd "$branch_name"
echo "Switched to worktree: $branch_name"
echo "Run 'pwd' to see current location"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment