Skip to content
Merged
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
87 changes: 6 additions & 81 deletions bin/wt-add
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ else
fi
wt_require_valid_config || exit 1

# Source wt-adopt library for adoption treatment
wt_source wt-adopt

usage() {
echo "Usage: $(basename "$0") [git worktree add arguments...]"
echo
Expand Down Expand Up @@ -140,81 +143,6 @@ while [[ $i -lt ${#ARGS[@]} ]]; do
i=$((i + 1))
done

# Helper: install project metadata for a created worktree
install_metadata_for_worktree() {
local worktree_path="$1"

# Normalize to absolute (worktree now exists)
local worktree_path_abs
worktree_path_abs="$(cd "$worktree_path" && pwd)"

echo "Installing project metadata into worktree: $worktree_path_abs"

# Find wt-metadata-import: try script directory first, then PATH
local metadata_import=""
if [[ -f "$SCRIPT_DIR/wt-metadata-import" ]]; then
metadata_import="$SCRIPT_DIR/wt-metadata-import"
elif command -v wt-metadata-import >/dev/null 2>&1; then
metadata_import="wt-metadata-import"
else
error "Cannot find wt-metadata-import"
return 1
fi

"$metadata_import" -y "$WT_IDEA_FILES_BASE" "$worktree_path_abs"
}

# Helper: install Bazel output symlinks (bazel-out, bazel-bin, etc.) for a created worktree
#
# Bazel stores build outputs in a central cache directory (typically under /private/var/tmp).
# The symlinks (bazel-out, bazel-bin, etc.) in the repo root point to this shared cache.
# By copying these symlinks to new worktrees, we enable:
# 1. Faster IntelliJ/IDE sync (no rebuild required)
# 2. Shared build cache across worktrees
# 3. Immediate access to compiled artifacts
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
}

########################################
# Fast path: NOT creating a new branch #
########################################
Expand Down Expand Up @@ -266,8 +194,7 @@ if [[ $creating_new_branch -eq 0 ]]; then
echo "Creating worktree for existing branch '$local_branch' at: $worktree_path"
git worktree add -- "$worktree_path" "$local_branch"

install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
wt_adopt_worktree --force "$(cd "$worktree_path" && pwd -P)"
success "git worktree add completed successfully."
exit 0
fi
Expand All @@ -288,8 +215,7 @@ if [[ $creating_new_branch -eq 0 ]]; then
info "Not creating a new branch; running: git worktree add ${ARGS[*]}"
git worktree add "${ARGS[@]}"

install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
wt_adopt_worktree --force "$(cd "$worktree_path" && pwd -P)"
success "git worktree add completed successfully."
exit 0
fi
Expand Down Expand Up @@ -475,8 +401,7 @@ else
git worktree add "${FINAL_ARGS[@]}"
fi

install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
wt_adopt_worktree --force "$(cd "$worktree_path" && pwd -P)"

success "git worktree add completed successfully."
exit 0
144 changes: 144 additions & 0 deletions bin/wt-adopt
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#!/usr/bin/env bash
#
# wt-adopt — Adopt an existing worktree into wt management
# ==========================================================
#
# Takes any existing git worktree and gives it the "wt treatment":
# imports metadata from the vault, installs Bazel symlinks, and places
# a marker file so wt commands recognize it as managed.
#
# Usage:
# wt-adopt # Adopt the worktree at CWD
# wt-adopt <worktree-path> # Adopt the specified worktree
# wt-adopt <branch-name> # Adopt the worktree for the given branch
# wt-adopt --redo # Re-run adoption treatment on current worktree
# wt-adopt --force # Skip conflict checks, overwrite without prompting
#
# Notes:
# - The main repository cannot be adopted (only worktrees).
# - Re-adopting an already-adopted worktree requires --redo or --force.
# - If the worktree has existing metadata, interactive sessions prompt
# with overwrite/keep/abort; non-interactive sessions abort.
#

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/
if [[ -f "$LIB_DIR/wt-common" ]]; then
. "$LIB_DIR/wt-common"
elif [[ -f "$HOME/.wt/lib/wt-common" ]]; then
. "$HOME/.wt/lib/wt-common"
else
echo "Error: Cannot find wt-common" >&2
exit 1
fi
wt_require_valid_config || exit 1

# Source wt-adopt library
wt_source wt-adopt

usage() {
cat <<EOF
Usage: $(basename "$0") [options] [worktree-path|branch-name]

Adopt an existing worktree into wt management.

Options:
--force Skip conflict checks (overwrite without prompting)
--redo Re-run adoption treatment on already-adopted worktree
-h, --help Show this help

Modes:
$(basename "$0") # Adopt worktree at current directory
$(basename "$0") <worktree-path|branch> # Adopt specified worktree
$(basename "$0") --redo # Re-adopt current worktree
$(basename "$0") --redo --force # Re-adopt, overwrite without prompting
EOF
}

TARGET_WORKTREE=""
FORCE=false
REDO=false

while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--force)
FORCE=true
shift
;;
--redo)
REDO=true
shift
;;
-*)
error "Unknown option: $1"
usage >&2
exit 1
;;
*)
if [[ -n "$TARGET_WORKTREE" ]]; then
error "Too many arguments"
usage >&2
exit 1
fi
TARGET_WORKTREE="$1"
shift
;;
esac
done

# Show context banner if contexts are configured
wt_show_context_banner

# Resolve target worktree
if [[ -z "$TARGET_WORKTREE" ]]; then
# Resolve to worktree root, not CWD (user may be in a subdirectory)
TARGET_WORKTREE="$(git rev-parse --show-toplevel 2>/dev/null)" || {
error "Not inside a git repository"
exit 1
}
else
TARGET_WORKTREE="$(wt_resolve_and_validate "$TARGET_WORKTREE")" || exit 1
fi

# Validate it's a git worktree or repo
if ! git -C "$TARGET_WORKTREE" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
error "Not a git repository or worktree: $TARGET_WORKTREE"
exit 1
fi

# Block adoption of the main repo
if wt_is_main_repo "$TARGET_WORKTREE"; then
error "Cannot adopt the main repository. Only worktrees can be adopted."
exit 1
fi

# Build args for wt_adopt_worktree
adopt_args=()
if [[ "$FORCE" == true ]]; then
adopt_args+=("--force")
fi
adopt_args+=("$TARGET_WORKTREE")

# Check if already adopted
if wt_is_adopted "$TARGET_WORKTREE"; then
if [[ "$REDO" == true || "$FORCE" == true ]]; then
info "Re-running adoption treatment: $TARGET_WORKTREE"
wt_adopt_worktree "${adopt_args[@]}"
else
info "Worktree is already adopted: $TARGET_WORKTREE"
info "Use --redo to re-run adoption treatment, or --force to force."
fi
exit 0
fi

# Run adoption
wt_adopt_worktree "${adopt_args[@]}"
11 changes: 10 additions & 1 deletion completion/wt.bash
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ _wt_metadata_import_complete() {

# --- Wire up standalone wt-* completions (only if commands exist on PATH) ---
type wt-add >/dev/null 2>&1 && complete -F _wt_add_complete wt-add
type wt-adopt >/dev/null 2>&1 && complete -F _wt_switch_complete wt-adopt
type wt-switch >/dev/null 2>&1 && complete -F _wt_switch_complete wt-switch
type wt-remove >/dev/null 2>&1 && complete -F _wt_remove_complete wt-remove
type wt-cd >/dev/null 2>&1 && complete -F _wt_cd_complete wt-cd
Expand All @@ -318,7 +319,7 @@ _wt_completion_bash() {
cur="${COMP_WORDS[COMP_CWORD]}"

if [[ ${COMP_CWORD} -eq 1 ]]; then
local commands="add switch remove list cd context metadata-export metadata-import ijwb-export ijwb-import help"
local commands="add adopt switch remove list cd context metadata-export metadata-import ijwb-export ijwb-import help"
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
else
case "${COMP_WORDS[1]}" in
Expand All @@ -327,6 +328,14 @@ _wt_completion_bash() {
branches=$(git branch -a 2>/dev/null | sed 's/^[* ]*//' | sed 's|remotes/origin/||')
COMPREPLY=($(compgen -W "$branches" -- "$cur"))
;;
adopt)
local branches
branches="$(wt_worktree_branch_list)"
if [[ -n "$branches" ]]; then
local IFS=$'\n'
COMPREPLY+=($(compgen -W "$branches" -- "$cur"))
fi
;;
switch|cd)
local branches
branches="$(wt_worktree_branch_list)"
Expand Down
3 changes: 3 additions & 0 deletions completion/wt.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ fi
# Register standalone wt-* completions
# ═══════════════════════════════════════════════════════════════════════════════

compdef _wt_switch wt-adopt
compdef _wt_switch wt-switch
compdef _wt_remove wt-remove
compdef _wt_cd wt-cd
Expand All @@ -312,6 +313,7 @@ _wt_completion() {
local -a commands
commands=(
'add:Create a new worktree for a branch'
'adopt:Adopt an existing worktree into wt management'
'switch:Switch the active worktree symlink'
'remove:Remove a worktree'
'list:List all worktrees with status'
Expand Down Expand Up @@ -343,6 +345,7 @@ _wt_completion() {
branches=("${(f)$(git branch -a 2>/dev/null | sed 's/^[* ]*//' | sed 's|remotes/origin/||')}")
(( ${#branches[@]} > 0 )) && _describe 'branch' branches
;;
adopt) _wt_switch ;;
switch|cd) _wt_switch ;;
remove) _wt_remove ;;
context) _wt_context ;;
Expand Down
Loading