Skip to content

Commit daa8631

Browse files
ericapisaniclaude
andcommitted
feat(scripts): Add git worktree management tools
Add helper scripts and make targets to simplify creating and managing git worktrees. The worktree-create script automates branch creation and virtual environment setup (preferring uv if available). The worktree-delete script removes worktrees and offers to clean up associated branches. Worktrees are particularly useful when working on multiple features independently of each other without needing to switch branches. This also enables parallelizing Claude Code sessions if desired, reducing friction when managing multiple development contexts. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f08ab01 commit daa8631

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ pip-wheel-metadata
3434

3535
# for running AWS Lambda tests using AWS SAM
3636
sam.template.yaml
37+
38+
.worktrees/

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ help:
77
@echo
88
@echo "make apidocs: Build the API documentation"
99
@echo "make aws-lambda-layer: Build AWS Lambda layer directory for serverless integration"
10+
@echo "make worktree-create NAME=<name>: Create a worktree with a new feature branch and virtual environment"
11+
@echo "make worktree-delete NAME=<name>: Remove a worktree (prompts to delete branch)"
12+
@echo "make worktree-list: List all active worktrees"
1013
@echo
1114
@echo "Also make sure to read ./CONTRIBUTING.md"
1215
@echo
@@ -33,3 +36,19 @@ aws-lambda-layer: dist
3336
$(VENV_PATH)/bin/pip install -r requirements-aws-lambda-layer.txt
3437
$(VENV_PATH)/bin/python -m scripts.build_aws_lambda_layer
3538
.PHONY: aws-lambda-layer
39+
40+
worktree-create:
41+
@test -n "$(NAME)" || (echo "Error: NAME is required. Usage: make worktree-create NAME=<name>" && false)
42+
@echo "$(NAME)" | grep -qE '^[a-zA-Z0-9_/-]+$$' || (echo "Error: NAME contains invalid characters" && false)
43+
./scripts/worktree-create.sh "$(NAME)"
44+
.PHONY: worktree-create
45+
46+
worktree-delete:
47+
@test -n "$(NAME)" || (echo "Error: NAME is required. Usage: make worktree-delete NAME=<name>" && false)
48+
@echo "$(NAME)" | grep -qE '^[a-zA-Z0-9_/-]+$$' || (echo "Error: NAME contains invalid characters" && false)
49+
./scripts/worktree-delete.sh "$(NAME)"
50+
.PHONY: worktree-delete
51+
52+
worktree-list:
53+
@git worktree list
54+
.PHONY: worktree-list

scripts/worktree-create.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ $# -lt 1 ]]; then
5+
echo "Usage: $0 <name>" >&2
6+
exit 1
7+
fi
8+
9+
POSITIONAL_ARGS=("$@")
10+
11+
# For simplicity, we use the same value for both the worktree name and branch name.
12+
WORKTREE_NAME="${POSITIONAL_ARGS[0]}"
13+
BRANCH_NAME="${POSITIONAL_ARGS[0]}"
14+
15+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
17+
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"
18+
19+
if [[ -d "$WORKTREE_DIR" ]]; then
20+
echo "Error: worktree directory already exists: $WORKTREE_DIR" >&2
21+
exit 1
22+
fi
23+
24+
if git -C "$REPO_ROOT" branch --list "$BRANCH_NAME" | grep -q .; then
25+
echo "Error: branch '$BRANCH_NAME' already exists. Delete it first or choose a different name." >&2
26+
exit 1
27+
fi
28+
29+
echo "Creating worktree '$WORKTREE_NAME' on branch '$BRANCH_NAME'..."
30+
git -C "$REPO_ROOT" worktree add "$WORKTREE_DIR" -b "$BRANCH_NAME"
31+
32+
if command -v uv &>/dev/null; then
33+
echo "Setting up virtual environment with uv..."
34+
uv venv "$WORKTREE_DIR/.venv"
35+
else
36+
echo "uv not found — falling back to python -m venv..."
37+
python -m venv "$WORKTREE_DIR/.venv"
38+
fi
39+
40+
echo ""
41+
echo "Worktree ready!"
42+
echo " Path: $WORKTREE_DIR"
43+
echo " Branch: $BRANCH_NAME"
44+
echo ""
45+
echo "To start working:"
46+
echo " cd $WORKTREE_DIR"
47+
echo " source .venv/bin/activate"

scripts/worktree-delete.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ $# -lt 1 ]]; then
5+
echo "Usage: $0 <name>" >&2
6+
exit 1
7+
fi
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11+
12+
for WORKTREE_NAME in "$@"; do
13+
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"
14+
15+
if [[ ! -d "$WORKTREE_DIR" ]]; then
16+
echo "Warning: worktree directory not found: $WORKTREE_DIR — skipping" >&2
17+
continue
18+
fi
19+
20+
# Capture branch name before removal
21+
BRANCH_NAME="$(git -C "$WORKTREE_DIR" branch --show-current 2>/dev/null || true)"
22+
23+
echo "Removing worktree '$WORKTREE_NAME'..."
24+
git -C "$REPO_ROOT" worktree remove "$WORKTREE_DIR"
25+
echo " Removed: $WORKTREE_DIR"
26+
27+
if [[ -n "$BRANCH_NAME" ]]; then
28+
if [[ -t 0 ]]; then
29+
read -r -p " Delete branch '$BRANCH_NAME'? [y/N] " REPLY
30+
echo
31+
else
32+
REPLY="n"
33+
fi
34+
if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then
35+
git -C "$REPO_ROOT" branch -d "$BRANCH_NAME"
36+
echo " Deleted branch: $BRANCH_NAME"
37+
fi
38+
fi
39+
40+
echo " Done."
41+
done

0 commit comments

Comments
 (0)