diff --git a/bin/wt-add b/bin/wt-add index 7dac3f5..1e18fd0 100755 --- a/bin/wt-add +++ b/bin/wt-add @@ -78,6 +78,9 @@ else exit 1 fi +# Source wt-bazel-common for shared Bazel utilities +wt_source wt-bazel-common + usage() { echo "Usage: $(basename "$0") [git worktree add arguments...]" echo @@ -161,49 +164,13 @@ install_ijwb_for_worktree() { } # Helper: install Bazel output symlinks (bazel-out, bazel-bin, etc.) for a created worktree -# These symlinks point to Bazel's cached build artifacts in a temp directory. -# Sharing them across worktrees significantly speeds up IntelliJ project sync. +# Note: Uses wt_install_bazel_symlinks from lib/wt-bazel-common install_bazel_symlinks_for_worktree() { local worktree_path="$1" - - # Normalize to absolute (worktree now exists) - local worktree_path_abs - worktree_path_abs="$(cd "$worktree_path" && pwd)" - - # List of Bazel symlinks to copy from main repo - # These are the most important ones for IntelliJ sync performance - local bazel_symlinks=("bazel-out" "bazel-bin" "bazel-testlogs" "bazel-genfiles") - local main_repo_abs main_repo_abs="$(cd "$WT_MAIN_REPO_ROOT" && pwd)" - - echo "Installing Bazel symlinks into worktree: $worktree_path_abs" - - for symlink_name in "${bazel_symlinks[@]}"; do - local src_link="$main_repo_abs/$symlink_name" - local dst_link="$worktree_path_abs/$symlink_name" - - # Check if source symlink exists in main repo - if [[ -L "$src_link" ]]; then - # Get the target of the symlink - local target - target="$(readlink "$src_link")" - - # If the target is relative, make it absolute based on main repo location - if [[ "$target" != /* ]]; then - target="$main_repo_abs/$target" - fi - - # Remove existing symlink/file in worktree if present - if [[ -L "$dst_link" || -e "$dst_link" ]]; then - rm -rf "$dst_link" - fi - - # Create symlink pointing to the same target - ln -s "$target" "$dst_link" - echo " ✓ $symlink_name -> $target" - fi - done + + wt_install_bazel_symlinks "$main_repo_abs" "$worktree_path" || true } ######################################## diff --git a/bin/wt-bazel-symlinks b/bin/wt-bazel-symlinks new file mode 100755 index 0000000..962b4a2 --- /dev/null +++ b/bin/wt-bazel-symlinks @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# +# wt-bazel-symlinks — Install Bazel Output Symlinks Into a Worktree +# ================================================================== +# +# This script copies Bazel output symlinks (bazel-out, bazel-bin, etc.) from +# the main repository into a worktree, ensuring they point to the same +# underlying Bazel cache directories. +# +# This allows all worktrees to share build artifacts, avoiding duplicate builds +# and speeding up IntelliJ project sync. +# +# Behavior: +# --------- +# 1. Reads Bazel symlinks from the main repository +# 2. Creates matching symlinks in the target worktree pointing to the same targets +# 3. Handles both absolute and relative symlink targets +# +# Usage: +# ------ +# wt-bazel-symlinks [OPTIONS] [worktree-path] +# +# Options: +# --main-repo Path to main repository (overrides $WT_MAIN_REPO_ROOT) +# -h, --help Show this help message +# +# Arguments: +# worktree-path Target worktree directory (default: current directory) +# +# Examples: +# --------- +# # Use current directory as worktree, $WT_MAIN_REPO_ROOT from environment +# wt-bazel-symlinks +# +# # Explicit worktree path, $WT_MAIN_REPO_ROOT from environment +# wt-bazel-symlinks ~/Development/java-worktrees/feature-x +# +# # Explicit main repo, current directory as worktree +# wt-bazel-symlinks --main-repo ~/Development/java-master +# +# # Both paths explicit (no environment needed) +# wt-bazel-symlinks --main-repo ~/Development/java-master ~/Development/java-worktrees/feature-x +# +# Environment Variables: +# ---------------------- +# WT_MAIN_REPO_ROOT Main repository root (used if --main-repo not provided) +# +# Notes: +# ------ +# - The main repository must have Bazel symlinks already (created by running bazel commands) +# - If symlinks don't exist in the main repo, they are skipped (not an error) +# - Safe to run multiple times; existing symlinks are replaced +# + +set -euo pipefail + +# Resolve script and lib directories +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="$SCRIPT_DIR/../lib" + +# Bootstrap: source wt-common from lib/ (optional - for defaults and helpers) +if [[ -f "$LIB_DIR/wt-common" ]]; then + . "$LIB_DIR/wt-common" +elif [[ -f "$HOME/.config/wt/lib/wt-common" ]]; then + . "$HOME/.config/wt/lib/wt-common" +fi + +# Source wt-bazel-common for shared Bazel utilities +wt_source wt-bazel-common + +usage() { + cat < Path to main repository (overrides \$WT_MAIN_REPO_ROOT) + -h, --help Show this help message + +Arguments: + worktree-path Target worktree directory (default: current directory) + +Examples: + $(basename "$0") + $(basename "$0") ~/Development/java-worktrees/feature-x + $(basename "$0") --main-repo ~/Development/java-master + $(basename "$0") --main-repo ~/Development/java-master ~/Development/java-worktrees/feature-x + +Environment Variables: + WT_MAIN_REPO_ROOT Main repository root (current: ${WT_MAIN_REPO_ROOT:-not set}) + +The main repository must have Bazel symlinks (bazel-out, bazel-bin, etc.) +created by running bazel commands. These symlinks will be copied to the worktree +to share the Bazel build cache. +EOF +} + +# Note: install_bazel_symlinks_for_worktree is now wt_install_bazel_symlinks +# defined in lib/wt-bazel-common + +# Parse arguments +MAIN_REPO="" +WORKTREE_PATH="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + --main-repo) + if [[ -z "${2:-}" ]]; then + echo "Error: --main-repo requires a path argument" >&2 + usage + exit 1 + fi + MAIN_REPO="$2" + shift 2 + ;; + -*) + echo "Error: Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + if [[ -n "$WORKTREE_PATH" ]]; then + echo "Error: Too many arguments" >&2 + usage + exit 1 + fi + WORKTREE_PATH="$1" + shift + ;; + esac +done + +# Determine main repo path +if [[ -z "$MAIN_REPO" ]]; then + # Use environment variable + if [[ -z "${WT_MAIN_REPO_ROOT:-}" ]]; then + echo "Error: Main repository not specified." >&2 + echo "Either set WT_MAIN_REPO_ROOT environment variable or use --main-repo option." >&2 + echo >&2 + usage + exit 1 + fi + MAIN_REPO="$WT_MAIN_REPO_ROOT" +fi + +# Determine worktree path +if [[ -z "$WORKTREE_PATH" ]]; then + WORKTREE_PATH="$(pwd)" +fi + +# Execute +wt_install_bazel_symlinks "$MAIN_REPO" "$WORKTREE_PATH" diff --git a/bin/wt-print-post-checkout-hook b/bin/wt-print-post-checkout-hook new file mode 100755 index 0000000..fa63224 --- /dev/null +++ b/bin/wt-print-post-checkout-hook @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# wt-print-post-checkout-hook — Print post-checkout hook template to stdout +# =========================================================================== +# +# This script prints a post-checkout Git hook that automatically installs +# Bazel output symlinks when checking out branches in worktrees. +# +# Usage: +# wt-print-post-checkout-hook > /path/to/hooks/post-checkout-bazel-symlinks +# wt-print-post-checkout-hook > .hooks/post-checkout-bazel-symlinks +# wt-print-post-checkout-hook > .git/hooks/post-checkout +# +# After redirecting to a file, remember to make it executable: +# chmod +x /path/to/hooks/post-checkout-bazel-symlinks +# + +cat <<'EOF' +#!/bin/bash +# +# post-checkout-bazel-symlinks — Install Bazel symlinks in worktrees +# =================================================================== +# +# This hook automatically installs Bazel output symlinks (bazel-out, bazel-bin, etc.) +# whenever checking out a branch in a Git worktree. +# +# Arguments (provided by Git via post-checkout): +# $1 = ref of previous HEAD +# $2 = ref of new HEAD +# $3 = flag (1 = branch checkout, 0 = file checkout) +# + +PREV_HEAD=$1 +NEW_HEAD=$2 +BRANCH_CHECKOUT=$3 + +# Only run for branch checkouts +if [ "$BRANCH_CHECKOUT" != "1" ]; then + exit 0 +fi + +# Detect if we're in a worktree +GIT_DIR="$(git rev-parse --git-dir 2>/dev/null)" || exit 0 + +# Check if this is a worktree (path contains .git/worktrees/) +if [[ "$GIT_DIR" != */.git/worktrees/* ]]; then + # Not a worktree, skip + exit 0 +fi + +# Find the main repository root +# GIT_DIR looks like: /path/to/repo/.git/worktrees/worktree-name +MAIN_REPO_GIT_DIR="${GIT_DIR%%/worktrees/*}" +MAIN_REPO_ROOT="$(dirname "$MAIN_REPO_GIT_DIR")" +WORKTREE_PATH="$(pwd)" + +# Try to find wt-bazel-symlinks +if command -v wt >/dev/null 2>&1; then + echo "Installing Bazel symlinks..." >&2 + wt bazel-symlinks --main-repo "$MAIN_REPO_ROOT" "$WORKTREE_PATH" 2>&1 || true +else + echo "Warning: wt-bazel-symlinks not found in PATH, skipping Bazel symlink installation" >&2 +fi + +exit 0 +EOF diff --git a/lib/wt-bazel-common b/lib/wt-bazel-common new file mode 100644 index 0000000..d5a29c6 --- /dev/null +++ b/lib/wt-bazel-common @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# +# wt-bazel-common — Shared Bazel utilities for wt-* scripts +# =========================================================== +# +# Provides common functions for managing Bazel artifacts in worktrees. +# + +# Install Bazel output symlinks for a worktree +# Args: $1 = main repo path, $2 = worktree path +# Returns: 0 on success, 1 on error +wt_install_bazel_symlinks() { + local main_repo="$1" + local worktree_path="$2" + + # Normalize to absolute paths + local main_repo_abs + local worktree_path_abs + + if [[ ! -d "$main_repo" ]]; then + error "Main repository does not exist: $main_repo" + return 1 + fi + + if [[ ! -d "$worktree_path" ]]; then + error "Worktree directory does not exist: $worktree_path" + return 1 + fi + + main_repo_abs="$(cd "$main_repo" && pwd)" + worktree_path_abs="$(cd "$worktree_path" && pwd)" + + # List of Bazel symlinks to copy from main repo + local bazel_symlinks=("bazel-out" "bazel-bin" "bazel-testlogs" "bazel-genfiles") + + echo "Installing Bazel symlinks into worktree: $worktree_path_abs" + echo "Reading symlinks from main repo: $main_repo_abs" + echo + + local installed_count=0 + + for symlink_name in "${bazel_symlinks[@]}"; do + local src_link="$main_repo_abs/$symlink_name" + local dst_link="$worktree_path_abs/$symlink_name" + + # Check if source symlink exists in main repo + if [[ -L "$src_link" ]]; then + # Get the target of the symlink + local target + target="$(readlink "$src_link")" + + # If the target is relative, make it absolute based on main repo location + if [[ "$target" != /* ]]; then + target="$main_repo_abs/$target" + fi + + # Remove existing symlink/file in worktree if present + if [[ -L "$dst_link" || -e "$dst_link" ]]; then + rm -rf "$dst_link" + fi + + # Create symlink pointing to the same target + ln -s "$target" "$dst_link" + echo " ✓ $symlink_name -> $target" + installed_count=$((installed_count + 1)) + else + echo " ⊘ $symlink_name (not found in main repo, skipping)" + fi + done + + echo + if [[ $installed_count -eq 0 ]]; then + warn "No Bazel symlinks found in main repository." + echo "Run a bazel command in the main repo first to create them." + return 1 + else + success "Installed $installed_count Bazel symlink(s)." + fi + + return 0 +} diff --git a/lib/wt-help b/lib/wt-help index 689e3e4..6e1aef8 100644 --- a/lib/wt-help +++ b/lib/wt-help @@ -34,6 +34,15 @@ IntelliJ Metadata Commands: - src: defaults to $WT_IDEA_FILES_BASE - dst: interactive worktree selection if omitted +Bazel Commands: + bazel-symlinks [OPTIONS] [worktree] + Install Bazel output symlinks into a worktree + - --main-repo : specify main repository path + - worktree: defaults to current directory + + print-post-checkout-hook Print post-checkout hook template to stdout + - Redirect to file and chmod +x to install + Other: help Show this help message @@ -51,6 +60,13 @@ Examples: wt ijwb-import ~/worktree # Import into specific worktree wt ijwb-import ~/vault ~/wt # Import from specific vault into worktree + wt bazel-symlinks # Install Bazel symlinks in current directory + wt bazel-symlinks ~/worktree # Install into specific worktree + wt bazel-symlinks --main-repo ~/repo ~/worktree # Specify both paths + + wt print-post-checkout-hook > .hooks/post-checkout-bazel-symlinks + chmod +x .hooks/post-checkout-bazel-symlinks + EOF cat <