Skip to content
Open
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
81 changes: 61 additions & 20 deletions bin/wt-list
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
#
# Indicators:
# -----------
# * = Currently linked worktree (symlink target)
# [main] = The main repository root
# [linked] = This worktree is currently linked at WT_ACTIVE_WORKTREE
# [dirty] = Has uncommitted changes (only with -v)
# [↑N] = N commits ahead of upstream (only with -v)
# [↓N] = N commits behind upstream (only with -v)
# * = Currently linked worktree (symlink target)
# [main] = The main repository root
# [linked] = This worktree is currently linked at WT_ACTIVE_WORKTREE
# [unadopted] = Worktree not adopted by wt (yellow; red if also linked)
# [dirty] = Has uncommitted changes (only with -v)
# [↑N] = N commits ahead of upstream (only with -v)
# [↓N] = N commits behind upstream (only with -v)
#
# Usage:
# wt-list # Fast: shows path, branch, [main], [linked]
Expand All @@ -44,26 +45,35 @@ else
fi
wt_require_valid_config || exit 1

# Source wt-adopt for adoption status checks
wt_source wt-adopt

usage() {
cat <<EOF
Usage: $(basename "$0") [-v|--verbose]
Usage: $(basename "$0") [-v|--verbose] [--porcelain]

List all git worktrees with status indicators.

Options:
-v, --verbose Show dirty/ahead/behind status (slower)
--porcelain Machine-readable output (augmented git worktree list --porcelain)
-h, --help Show this help message
EOF
}

# Parse arguments
VERBOSE=false
PORCELAIN=false
while [[ $# -gt 0 ]]; do
case "$1" in
-v|--verbose)
VERBOSE=true
shift
;;
--porcelain)
PORCELAIN=true
shift
;;
-h|--help)
usage
exit 0
Expand All @@ -76,15 +86,31 @@ while [[ $# -gt 0 ]]; do
esac
done

# Show context banner if contexts are configured
wt_show_context_banner

# Validate main repo exists
# Validate main repo exists (shared by both modes so porcelain doesn't
# silently return an empty listing when the config points at a non-repo)
if [[ ! -d "$WT_MAIN_REPO_ROOT" ]]; then
error "WT_MAIN_REPO_ROOT does not exist: $WT_MAIN_REPO_ROOT"
exit 1
fi

if ! git -C "$WT_MAIN_REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
error "$WT_MAIN_REPO_ROOT is not a git repository or worktree."
exit 1
fi

# Porcelain mode: output augmented porcelain directly
if [[ "$PORCELAIN" == true ]]; then
if [[ "$VERBOSE" == true ]]; then
wt_list_porcelain --verbose
else
wt_list_porcelain
fi
exit $?
Comment thread
bezhermoso marked this conversation as resolved.
fi

# Show context banner if contexts are configured
wt_show_context_banner

# Get absolute path of main repo (use pwd -P to resolve symlinks for consistent comparison)
# Note: git worktree list stores physical paths, so we need to match that behavior
MAIN_REPO_ABS="$(cd "$WT_MAIN_REPO_ROOT" && pwd -P)"
Expand All @@ -96,11 +122,6 @@ LINKED_WORKTREE="$(wt_get_linked_worktree)"
(
cd "$WT_MAIN_REPO_ROOT" || exit 1

if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
error "$WT_MAIN_REPO_ROOT is not a git repository or worktree."
exit 1
fi

echo

while IFS= read -r line; do
Expand All @@ -109,14 +130,34 @@ LINKED_WORKTREE="$(wt_get_linked_worktree)"
wt="${line#worktree }"
wt_abs="$(cd "$wt" 2>/dev/null && pwd -P)" || continue

# Determine prefix (linked indicator)
prefix=" "
# Check adoption status (skip for main repo — adoption doesn't apply)
is_linked=false
is_unadopted=false
if [[ -n "$LINKED_WORKTREE" && "$wt_abs" == "$LINKED_WORKTREE" ]]; then
is_linked=true
fi
if [[ "$wt_abs" != "$MAIN_REPO_ABS" ]] && ! wt_is_adopted "$wt_abs" 2>/dev/null; then
is_unadopted=true
fi

# Determine prefix: * for linked, space otherwise
prefix=" "
if [[ "$is_linked" == true ]]; then
prefix="${GREEN}*${NC} "
fi

# Print with shared formatting
printf "%s%s\n" "$prefix" "$(wt_format_worktree "$wt_abs" "$MAIN_REPO_ABS" "$LINKED_WORKTREE" "$VERBOSE")"
# Build unadopted badge (red if also linked, yellow otherwise)
unadopted_badge=""
if [[ "$is_unadopted" == true ]]; then
if [[ "$is_linked" == true ]]; then
unadopted_badge="${RED}[unadopted]${NC} "
else
unadopted_badge="${YELLOW}[unadopted]${NC} "
fi
fi

# Print with shared formatting + unadopted badge
printf "%s%s%s\n" "$prefix" "$(wt_format_worktree "$wt_abs" "$MAIN_REPO_ABS" "$LINKED_WORKTREE" "$VERBOSE")" "$unadopted_badge"
;;
esac
done < <(git worktree list --porcelain)
Expand Down
12 changes: 12 additions & 0 deletions completion/wt.bash
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ _wt_cd_complete() {
fi
}

_wt_list_complete() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"

COMPREPLY+=( $(compgen -W "-v --verbose --porcelain -h --help" -- "$cur") )
}

# --- Helper: get context list ---
_wt_context_list() {
local repos_dir="$HOME/.wt/repos"
Expand Down Expand Up @@ -303,6 +311,7 @@ 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
type wt-list >/dev/null 2>&1 && complete -F _wt_list_complete wt-list
type wt-context >/dev/null 2>&1 && complete -F _wt_context_complete wt-context
type wt-metadata-export >/dev/null 2>&1 && complete -F _wt_metadata_export_complete wt-metadata-export
type wt-metadata-import >/dev/null 2>&1 && complete -F _wt_metadata_import_complete wt-metadata-import
Expand Down Expand Up @@ -352,6 +361,9 @@ _wt_completion_bash() {
COMPREPLY+=($(compgen -W "$branches" -- "$cur"))
fi
;;
list)
COMPREPLY=($(compgen -W "-v --verbose --porcelain -h --help" -- "$cur"))
;;
context)
local contexts
contexts="$(_wt_context_list)"
Expand Down
10 changes: 10 additions & 0 deletions completion/wt.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ _wt_cd() {
esac
}

# Completion for wt-list / wt list: flags
_wt_list() {
_arguments \
'(-v --verbose)'{-v,--verbose}'[Show dirty/ahead/behind status]' \
'--porcelain[Machine-readable output]' \
'(-h --help)'{-h,--help}'[Show help]'
}

# Completion for wt-context / wt context
_wt_context() {
local context state
Expand Down Expand Up @@ -298,6 +306,7 @@ compdef _wt_switch wt-adopt
compdef _wt_switch wt-switch
compdef _wt_remove wt-remove
compdef _wt_cd wt-cd
compdef _wt_list wt-list
compdef _wt_context wt-context
compdef _wt_metadata_export wt-metadata-export
compdef _wt_metadata_import wt-metadata-import
Expand Down Expand Up @@ -348,6 +357,7 @@ _wt_completion() {
adopt) _wt_switch ;;
switch|cd) _wt_switch ;;
remove) _wt_remove ;;
list) _wt_list ;;
context) _wt_context ;;
metadata-export|ijwb-export) _wt_metadata_export ;;
metadata-import|ijwb-import) _wt_metadata_import ;;
Expand Down
35 changes: 29 additions & 6 deletions lib/wt-choose
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ else
exit 1
fi

# Source wt-adopt for adoption status checks
wt_source wt-adopt

Comment thread
bezhermoso marked this conversation as resolved.
# Interactively pick a worktree from WT_MAIN_REPO_ROOT
# Runs in a subshell to avoid changing the caller's working directory
# Args: $1 = "exclude_main" to exclude the main repository from selection
Expand All @@ -79,9 +82,10 @@ select_git_worktree() {
exit 1
fi

# Get absolute path of main repo
# Get absolute path of main repo (use pwd -P so symlinks in the path
# resolve to the physical form matching worktree entries below)
local main_repo_abs
main_repo_abs="$(cd "$WT_MAIN_REPO_ROOT" && pwd)"
main_repo_abs="$(cd "$WT_MAIN_REPO_ROOT" && pwd -P)"

# Get currently linked worktree for display
local linked_worktree
Expand All @@ -94,7 +98,7 @@ select_git_worktree() {
case "$line" in
worktree\ *)
wt="${line#worktree }"
wt_abs="$(cd "$wt" && pwd)"
wt_abs="$(cd "$wt" 2>/dev/null && pwd -P)" || continue
# Skip main repo if exclude_main is set
if [[ "$exclude_main" != "exclude_main" || "$wt_abs" != "$main_repo_abs" ]]; then
WORKTREES[${#WORKTREES[@]}]="$wt_abs"
Expand All @@ -116,13 +120,32 @@ select_git_worktree() {

local i=1
for wt in "${WORKTREES[@]}"; do
# Determine prefix (* for currently linked worktree)
local prefix=" "
local is_linked=false is_unadopted=false
if [[ -n "$linked_worktree" && "$wt" == "$linked_worktree" ]]; then
is_linked=true
fi
if [[ "$wt" != "$main_repo_abs" ]] && ! wt_is_adopted "$wt" 2>/dev/null; then
is_unadopted=true
fi

# Determine prefix: * for linked, space otherwise
local prefix=" "
if [[ "$is_linked" == true ]]; then
prefix="${GREEN}*${NC} "
fi

# Build unadopted badge (red if also linked, yellow otherwise)
local unadopted_badge=""
if [[ "$is_unadopted" == true ]]; then
if [[ "$is_linked" == true ]]; then
unadopted_badge="${RED}[unadopted]${NC} "
else
unadopted_badge="${YELLOW}[unadopted]${NC} "
fi
fi

# Use shared formatting with number prefix (fast mode - no status checks)
printf "%s%2d) %s\n" "$prefix" "$i" "$(wt_format_worktree "$wt" "$main_repo_abs" "$linked_worktree" "false")" >&2
printf "%s%2d) %s%s\n" "$prefix" "$i" "$(wt_format_worktree "$wt" "$main_repo_abs" "$linked_worktree" "false")" "$unadopted_badge" >&2
i=$((i + 1))
done

Expand Down
Loading