Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 6 additions & 39 deletions bin/wt-add
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

########################################
Expand Down
157 changes: 157 additions & 0 deletions bin/wt-bazel-symlinks
Original file line number Diff line number Diff line change
@@ -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> 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 <<EOF
Usage: $(basename "$0") [OPTIONS] [worktree-path]

Install Bazel output symlinks from main repository into a worktree.

Options:
--main-repo <path> 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"
66 changes: 66 additions & 0 deletions bin/wt-print-post-checkout-hook
Original file line number Diff line number Diff line change
@@ -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
81 changes: 81 additions & 0 deletions lib/wt-bazel-common
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions lib/wt-help
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>: 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

Expand All @@ -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 <<EOF
Environment Variables (configured in lib/wt-common):
Expand Down
Loading