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
102 changes: 83 additions & 19 deletions bin/gtr
Original file line number Diff line number Diff line change
Expand Up @@ -1131,17 +1131,26 @@ cmd_adapter() {

# Config command
cmd_config() {
local scope="local"
local scope="auto"
local action="" key="" value=""
local extra_args=""

# Parse args flexibly: action, key, value, and --global anywhere
# Parse args flexibly: action, key, value, and --global/--local anywhere
while [ $# -gt 0 ]; do
case "$1" in
--global|global)
scope="global"
shift
;;
get|set|unset|add)
--local|local)
scope="local"
shift
;;
--system|system)
scope="system"
shift
;;
get|set|unset|add|list)
action="$1"
shift
;;
Expand All @@ -1153,51 +1162,101 @@ cmd_config() {
value="$1"
shift
else
# Unknown extra token
# Track extra tokens for validation (add space only if not first)
extra_args="${extra_args:+$extra_args }$1"
shift
fi
;;
esac
done

# Default action is get
action="${action:-get}"
# Default action: list if no action and no key, otherwise get
if [ -z "$action" ]; then
if [ -z "$key" ]; then
action="list"
else
action="get"
fi
fi

# Resolve "auto" scope to "local" for set/add/unset operations (they need explicit scope)
# This ensures log messages show the actual scope being used
local resolved_scope="$scope"
if [ "$scope" = "auto" ] && [ "$action" != "list" ] && [ "$action" != "get" ]; then
resolved_scope="local"
fi

# Reject --system for write operations (requires root, not commonly useful)
if [ "$scope" = "system" ]; then
case "$action" in
set|add|unset)
log_error "--system is not supported for write operations (requires root privileges)"
log_error "Use --local or --global instead"
exit 1
;;
esac
fi

case "$action" in
get)
if [ -z "$key" ]; then
log_error "Usage: git gtr config get <key> [--global]"
log_error "Usage: git gtr config get <key> [--local|--global|--system]"
exit 1
fi
# Warn on unexpected extra arguments
if [ -n "$extra_args" ]; then
log_warn "get action: ignoring extra arguments: $extra_args"
fi
cfg_get_all "$key" "" "$scope"
;;
set)
if [ -z "$key" ] || [ -z "$value" ]; then
log_error "Usage: git gtr config set <key> <value> [--global]"
log_error "Usage: git gtr config set <key> <value> [--local|--global]"
exit 1
fi
cfg_set "$key" "$value" "$scope"
log_info "Config set: $key = $value ($scope)"
# Warn on unexpected extra arguments
if [ -n "$extra_args" ]; then
log_warn "set action: ignoring extra arguments: $extra_args"
fi
cfg_set "$key" "$value" "$resolved_scope"
log_info "Config set: $key = $value ($resolved_scope)"
;;
add)
if [ -z "$key" ] || [ -z "$value" ]; then
log_error "Usage: git gtr config add <key> <value> [--global]"
log_error "Usage: git gtr config add <key> <value> [--local|--global]"
exit 1
fi
cfg_add "$key" "$value" "$scope"
log_info "Config added: $key = $value ($scope)"
# Warn on unexpected extra arguments
if [ -n "$extra_args" ]; then
log_warn "add action: ignoring extra arguments: $extra_args"
fi
cfg_add "$key" "$value" "$resolved_scope"
log_info "Config added: $key = $value ($resolved_scope)"
;;
unset)
if [ -z "$key" ]; then
log_error "Usage: git gtr config unset <key> [--global]"
log_error "Usage: git gtr config unset <key> [--local|--global]"
exit 1
fi
cfg_unset "$key" "$scope"
log_info "Config unset: $key ($scope)"
# Warn on unexpected extra arguments (including value which unset doesn't use)
if [ -n "$value" ] || [ -n "$extra_args" ]; then
log_warn "unset action: ignoring extra arguments: ${value}${value:+ }${extra_args}"
fi
cfg_unset "$key" "$resolved_scope"
log_info "Config unset: $key ($resolved_scope)"
;;
list)
# Warn on unexpected extra arguments
if [ -n "$key" ] || [ -n "$extra_args" ]; then
log_warn "list action doesn't accept additional arguments (ignoring: ${key}${key:+ }${extra_args})"
fi
# Use cfg_list for proper formatting and .gtrconfig support
cfg_list "$scope"
;;
*)
log_error "Unknown config action: $action"
log_error "Usage: git gtr config {get|set|add|unset} <key> [value] [--global]"
log_error "Usage: git gtr config [list] [--local|--global|--system]"
log_error " git gtr config {get|set|add|unset} <key> [value] [--local|--global]"
exit 1
;;
esac
Expand Down Expand Up @@ -1349,12 +1408,17 @@ CORE COMMANDS (daily workflow):

SETUP & MAINTENANCE:

config {get|set|add|unset} <key> [value] [--global]
config [list] [--local|--global|--system]
config get <key> [--local|--global|--system]
config {set|add|unset} <key> [value] [--local|--global]
Manage configuration
- get: read a config value
- list: show all gtr.* config values (default when no args)
- get: read a config value (merged from all sources by default)
- set: set a single value (replaces existing)
- add: add a value (for multi-valued configs like hooks, copy patterns)
- unset: remove a config value
Without scope flag, list/get show merged config from all sources
Use --local/--global to target a specific scope for write operations

doctor
Health check (verify git, editors, AI tools)
Expand Down
46 changes: 38 additions & 8 deletions completions/_git-gtr
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ _git-gtr() {
_describe 'branch names' all_options
;;
config)
_values 'config action' get set add unset
# Complete action or scope flags
_values 'config action' list get set add unset --local --global --system
;;
esac
# Complete subsequent arguments
Expand Down Expand Up @@ -107,13 +108,42 @@ _git-gtr() {
esac
;;
config)
case "$words[4]" in
get|set|add|unset)
_arguments \
'--global[Use global git config]' \
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove)'
;;
esac
# Find action by scanning all config args (handles flexible flag positioning)
# Use offset 3 to start from words[4] (first arg after 'config')
# Zsh uses 0-based offset: ${array[@]:3} = elements starting from 4th position
local config_action=""
local arg
for arg in "${words[@]:3}"; do
case "$arg" in
list|get|set|add|unset)
[[ -z "$config_action" ]] && config_action="$arg"
;;
esac
done

if [[ -z "$config_action" ]]; then
# Still need action or scope
_values 'config action' list get set add unset --local --global --system
else
case "$config_action" in
list|get)
# Read operations support all scopes including --system
_arguments \
'--local[Use local git config]' \
'--global[Use global git config]' \
'--system[Use system git config]' \
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove)'
;;
set|add|unset)
# Write operations only support --local and --global
# (--system may require root or appropriate file permissions)
_arguments \
'--local[Use local git config]' \
'--global[Use global git config]' \
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove)'
;;
esac
fi
;;
esac
fi
Expand Down
31 changes: 30 additions & 1 deletion completions/git-gtr.fish
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,36 @@ complete -c git -n '__fish_git_gtr_using_command copy' -s a -l all -d 'Copy to a
complete -c git -n '__fish_git_gtr_using_command copy' -l from -d 'Source worktree' -r

# Config command
complete -f -c git -n '__fish_git_gtr_using_command config' -a 'get set add unset'
complete -f -c git -n '__fish_git_gtr_using_command config' -a 'list get set add unset'

# Helper to check if config action is a read operation (list or get)
function __fish_git_gtr_config_is_read
set -l cmd (commandline -opc)
for i in $cmd
if test "$i" = "list" -o "$i" = "get"
return 0
end
end
return 1
end

# Helper to check if config action is a write operation (set, add, unset)
function __fish_git_gtr_config_is_write
set -l cmd (commandline -opc)
for i in $cmd
if test "$i" = "set" -o "$i" = "add" -o "$i" = "unset"
return 0
end
end
return 1
end

# Scope flags for config command
# --local and --global available for all operations
complete -f -c git -n '__fish_git_gtr_using_command config' -l local -d 'Use local git config'
complete -f -c git -n '__fish_git_gtr_using_command config' -l global -d 'Use global git config'
# --system only for read operations (list, get) - write requires root
complete -f -c git -n '__fish_git_gtr_using_command config; and __fish_git_gtr_config_is_read' -l system -d 'Use system git config'
complete -f -c git -n '__fish_git_gtr_using_command config' -a "
gtr.worktrees.dir\t'Worktrees base directory'
gtr.worktrees.prefix\t'Worktree folder prefix'
Expand Down
36 changes: 32 additions & 4 deletions completions/gtr.bash
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,38 @@ _git_gtr() {
fi
;;
config)
if [ "$cword" -eq 3 ]; then
COMPREPLY=($(compgen -W "get set add unset" -- "$cur"))
elif [ "$cword" -eq 4 ]; then
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove" -- "$cur"))
# Find action by scanning all config args (handles flexible flag positioning)
local config_action=""
local i
for (( i=3; i < cword; i++ )); do
case "${words[i]}" in
list|get|set|add|unset) config_action="${words[i]}" ;;
esac
done

if [ -z "$config_action" ]; then
# Still need to complete action or scope
COMPREPLY=($(compgen -W "list get set add unset --local --global --system" -- "$cur"))
else
# Have action, complete based on it
case "$config_action" in
list|get)
# Read operations support all scopes including --system
if [[ "$cur" == -* ]]; then
COMPREPLY=($(compgen -W "--local --global --system" -- "$cur"))
else
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove" -- "$cur"))
fi
;;
set|add|unset)
# Write operations only support --local and --global (--system requires root)
if [[ "$cur" == -* ]]; then
COMPREPLY=($(compgen -W "--local --global" -- "$cur"))
else
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove" -- "$cur"))
fi
;;
esac
fi
;;
esac
Expand Down
Loading