Git workflow, branching, deploys, PRs. Load when committing, deploying, or managing branches.
Each chat session works on its own branch.
Create feature branch at session start:
git checkout main && git pull origin main
git checkout -b <descriptive-branch-name>
Check current branch before any work — If on main, create branch first
Cherry-pick to main when complete:
git checkout main && git pull origin main
git cherry-pick <commit-sha>
git push origin main
Do NOT create PRs unless user explicitly asks
Before every deployment:
git fetch origin maingit merge origin/main/pre-deploy/deployGotcha: A worktree cannot check out main if the main repo already has it checked out. When using gh pr merge from a worktree, the post-merge branch cleanup may show an error — this is harmless; the merge itself succeeds. Verify with gh pr view <number> --json state.
gh pr merge --delete-branch + worktrees: If the branch being merged is any worktree's HEAD, the remote delete succeeds but the local delete fails:
✗ failed to delete local branch <name>:
Cannot delete branch '<name>' checked out at '/path/to/worktree'
Workaround: switch the worktree off the branch BEFORE merging, or skip --delete-branch and delete deliberately in a teardown step (git worktree remove … && git branch -d …). See memory/gotcha_gh_pr_merge_delete_branch_worktree.md.
If you used a worktree, clean it up:
# From main repo (not worktree)
git worktree remove --force .claude/worktrees/<name>
git branch -D <worktree-branch>
When developing in a worktree, a running uvicorn server in the main repo will NOT serve worktree code. Either:
pytest rather than live requestsThis caused confusion during debugging when production-like errors could not be reproduced locally because the server was running from the wrong directory.
railway ssh does not handle complex quoting well. For multi-line Python scripts, base64-encode the script and pipe: echo <b64> | base64 -d | python3.asyncpg. Install with pip3 install <package> before running scripts.DATABASE_URL uses an internal hostname (*.railway.internal). Use railway ssh to run DB commands from inside the Railway network.SELECT * may return 0 rows without error if no rows match, masking a missing-column bug.# List tags
git tag -l
# Revert to tag
git checkout pre_alpha_v1
# Create new tag
git tag -a <tag-name> -m "Description"
git push origin <tag-name>
FileNotFoundError, "App directory not found", or ModuleNotFoundError: No module named 'app'. Always cd to repo root (or use absolute path) before ./scripts/quick_ci.sh.--no-verify only for confirmed environment-related failures (e.g. missing local tool, DB not running), never to skip fixable code issues..git is a file pointing at the main repo. Hooks live in the main repo's .git/hooks; the worktree uses those hooks when you commit from the worktree.gh pr merge <n> fails with "merge commit cannot be cleanly created", merge main into the branch locally (git merge origin/main), push, then retry. If GitHub still reports not mergeable, use gh pr merge <n> --squash --auto so the merge runs when checks pass.Learned from: PR 225 (tenant search fix); quick_ci from wrong cwd; CodeRabbit 150-file limit; merge conflict resolution and squash-merge workaround.
When a parent PR is squash-merged into main but your branch has commits from before the squash, a normal rebase fails with many conflicts (because git replays all original commits against the squash). Use --onto to replay only your new commits:
# Only replay the last N commits (your fixes) onto main
git rebase --onto origin/main HEAD~1
When to use: Your branch was based on a feature branch that got squash-merged. git log shows the original feature commits plus your fix commits, but main only has the single squash commit.
/loopUse Claude Code scheduled tasks to poll long-running ops without leaving your session:
# Watch a Railway deploy
/loop 2m check if the Railway deploy finished and report status
# Monitor CI after push
/loop 5m check the GitHub Actions status on PR #<number>
# One-time reminder
remind me in 30 minutes to review the migration
Tasks are session-scoped (gone when you exit). Use for deploy watching, CI babysitting, and time-boxed reminders — not for app-level scheduling.
# Run Python script on Railway
railway run python /path/to/script.py
# Create release (auto-detects version bump from commits)
python scripts/release.py --auto --dry-run
python scripts/release.py --patch # or --minor, --major
# Rollback deployment
python scripts/rollback.py deploy --dry-run
python scripts/rollback.py migration --steps 1 --dry-run
python scripts/rollback.py both --steps 1
# Canary deployment
python scripts/canary_deploy.py \
--production-url https://app.courierflow.com \
--canary-url https://canary.courierflow.com \
--duration 600 --dry-run
# Vulnerability scan
python scripts/vulnerability_scan.py --severity-threshold high --format json