* [PATCH] completion: zsh: support completion after "git -C <path>"
@ 2026-06-17 15:30 Lutz Lengemann via GitGitGadget
2026-06-17 17:17 ` Ben Knoble
2026-06-17 17:21 ` Junio C Hamano
0 siblings, 2 replies; 3+ messages in thread
From: Lutz Lengemann via GitGitGadget @ 2026-06-17 15:30 UTC (permalink / raw)
To: git; +Cc: Lutz Lengemann, Lutz Lengemann
From: Lutz Lengemann <lutz@lengemann.net>
The zsh completion wrapper (__git_zsh_main) did not handle the global -C
option, so "git -C <path> <command> <TAB>" offered nothing and could not
complete a command's arguments.
Three things are needed to make it work, all scoped to -C:
- Add -C to the _arguments specification, so completion no longer stops
at it.
- Advance __git_cmd_idx past any leading "-C <path>" options. The index
is hard-coded to 1, i.e. the command is assumed to be the first
argument; with -C present the command sits two words later for each
-C, so the bash helpers otherwise look at the wrong word and produce
nothing.
- Collect the -C paths into __git_C_args, as __git_main does. The bash
helpers run git to resolve aliases and list refs; without the -C
paths they run in the current directory, so completion fails whenever
the cwd is not the target repository or the command is an alias.
With these, "git -C <path> <command> <TAB>" completes the command, its
options and its arguments, including outside the repository, through
aliases, and with repeated -C options.
Signed-off-by: Lutz Lengemann <lutz@lengemann.net>
---
completion: zsh: support completion after "git -C "
This patch is intentionally scoped to -C, but the underlying problem is
more general. The zsh wrapper hard-codes __git_cmd_idx=1, i.e. it
assumes the command is always the first argument. That assumption breaks
argument completion after any global option that precedes the command,
not just -C — e.g. --git-dir, --work-tree, --namespace, -c, and
-p/--paginate. After those, git <opt> <command> <TAB> currently
completes the command name but not its arguments.
The same approach generalizes cleanly: instead of skipping only leading
-C options, walk all leading global options and their arguments to
locate the command and its true index (mirroring the option scan in
__git_main in git-completion.bash), while collecting -C into
__git_C_args and --git-dir into __git_dir as today.
I kept this revision narrow for reviewability and because git -C is the
case where I miss the completion, but I'm happy to extend it to cover
the other global options in a follow-up (or fold it into this patch) if
that's preferred.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2155%2Fmobilutz%2Fzsh-complete-global-C-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2155/mobilutz/zsh-complete-global-C-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2155
contrib/completion/git-completion.zsh | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index c32186a977..323049be8b 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -227,6 +227,7 @@ __git_zsh_main ()
'(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
'(-p --paginate)--no-pager[do not pipe git output into a pager]' \
'--git-dir=-[set the path to the repository]: :_directories' \
+ '*-C[run as if git was started in <path>]: :_directories' \
'--bare[treat the repository as a bare repository]' \
'(- :)--version[prints the git suite version]' \
'--exec-path=-[path to where your core git programs are installed]:: :_directories' \
@@ -252,6 +253,14 @@ __git_zsh_main ()
;;
(arg)
local command="${words[1]}" __git_dir __git_cmd_idx=1
+ local -a __git_C_args
+ local -i i=2
+
+ while [[ ${orig_words[i]} == -C ]]; do
+ __git_C_args+=(-C ${orig_words[i+1]})
+ (( __git_cmd_idx += 2 ))
+ (( i += 2 ))
+ done
if (( $+opt_args[--bare] )); then
__git_dir='.'
base-commit: 0fae78c9d55efe705877ea537fe42c59164ccd94
--
gitgitgadget
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] completion: zsh: support completion after "git -C <path>"
2026-06-17 15:30 [PATCH] completion: zsh: support completion after "git -C <path>" Lutz Lengemann via GitGitGadget
@ 2026-06-17 17:17 ` Ben Knoble
2026-06-17 17:21 ` Junio C Hamano
1 sibling, 0 replies; 3+ messages in thread
From: Ben Knoble @ 2026-06-17 17:17 UTC (permalink / raw)
To: Lutz Lengemann via GitGitGadget; +Cc: git, Lutz Lengemann
I’d like to take a deeper look at this, but I’m not sure when I can.
> Le 17 juin 2026 à 11:37, Lutz Lengemann via GitGitGadget <gitgitgadget@gmail.com> a écrit :
>
> From: Lutz Lengemann <lutz@lengemann.net>
>
> The zsh completion wrapper (__git_zsh_main) did not handle the global -C
> option, so "git -C <path> <command> <TAB>" offered nothing and could not
> complete a command's arguments.
One easy note, though: our commit style prefers describing the code base before the patch in question in the present tense (« does not handle », « offers nothing »).
The below imperative mood looks appropriate to me.
>
> Three things are needed to make it work, all scoped to -C:
>
> - Add -C to the _arguments specification, so completion no longer stops
> at it.
>
> - Advance __git_cmd_idx past any leading "-C <path>" options. The index
> is hard-coded to 1, i.e. the command is assumed to be the first
> argument; with -C present the command sits two words later for each
> -C, so the bash helpers otherwise look at the wrong word and produce
> nothing.
>
> - Collect the -C paths into __git_C_args, as __git_main does. The bash
> helpers run git to resolve aliases and list refs; without the -C
> paths they run in the current directory, so completion fails whenever
> the cwd is not the target repository or the command is an alias.
>
> With these, "git -C <path> <command> <TAB>" completes the command, its
> options and its arguments, including outside the repository, through
> aliases, and with repeated -C options.
>
> Signed-off-by: Lutz Lengemann <lutz@lengemann.net>
> ---
> completion: zsh: support completion after "git -C "
>
> This patch is intentionally scoped to -C, but the underlying problem is
> more general. The zsh wrapper hard-codes __git_cmd_idx=1, i.e. it
> assumes the command is always the first argument. That assumption breaks
> argument completion after any global option that precedes the command,
> not just -C — e.g. --git-dir, --work-tree, --namespace, -c, and
> -p/--paginate. After those, git <opt> <command> <TAB> currently
> completes the command name but not its arguments.
>
> The same approach generalizes cleanly: instead of skipping only leading
> -C options, walk all leading global options and their arguments to
> locate the command and its true index (mirroring the option scan in
> __git_main in git-completion.bash), while collecting -C into
> __git_C_args and --git-dir into __git_dir as today.
>
> I kept this revision narrow for reviewability and because git -C is the
> case where I miss the completion, but I'm happy to extend it to cover
> the other global options in a follow-up (or fold it into this patch) if
> that's preferred.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2155%2Fmobilutz%2Fzsh-complete-global-C-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2155/mobilutz/zsh-complete-global-C-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/2155
>
> contrib/completion/git-completion.zsh | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
> index c32186a977..323049be8b 100644
> --- a/contrib/completion/git-completion.zsh
> +++ b/contrib/completion/git-completion.zsh
> @@ -227,6 +227,7 @@ __git_zsh_main ()
> '(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
> '(-p --paginate)--no-pager[do not pipe git output into a pager]' \
> '--git-dir=-[set the path to the repository]: :_directories' \
> + '*-C[run as if git was started in <path>]: :_directories' \
> '--bare[treat the repository as a bare repository]' \
> '(- :)--version[prints the git suite version]' \
> '--exec-path=-[path to where your core git programs are installed]:: :_directories' \
> @@ -252,6 +253,14 @@ __git_zsh_main ()
> ;;
> (arg)
> local command="${words[1]}" __git_dir __git_cmd_idx=1
> + local -a __git_C_args
> + local -i i=2
> +
> + while [[ ${orig_words[i]} == -C ]]; do
> + __git_C_args+=(-C ${orig_words[i+1]})
> + (( __git_cmd_idx += 2 ))
> + (( i += 2 ))
> + done
>
> if (( $+opt_args[--bare] )); then
> __git_dir='.'
>
> base-commit: 0fae78c9d55efe705877ea537fe42c59164ccd94
> --
> gitgitgadget
>
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] completion: zsh: support completion after "git -C <path>"
2026-06-17 15:30 [PATCH] completion: zsh: support completion after "git -C <path>" Lutz Lengemann via GitGitGadget
2026-06-17 17:17 ` Ben Knoble
@ 2026-06-17 17:21 ` Junio C Hamano
1 sibling, 0 replies; 3+ messages in thread
From: Junio C Hamano @ 2026-06-17 17:21 UTC (permalink / raw)
To: Lutz Lengemann via GitGitGadget; +Cc: git, Lutz Lengemann
"Lutz Lengemann via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Lutz Lengemann <lutz@lengemann.net>
>
> The zsh completion wrapper (__git_zsh_main) did not handle the global -C
> option, so "git -C <path> <command> <TAB>" offered nothing and could not
> complete a command's arguments.
I do not write, use, or customize zsh, so please take my comments
with huge grains of salt, or just ignore them completely (your
choice) ;-), but one thng I noticed was that ...
> diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
> index c32186a977..323049be8b 100644
> --- a/contrib/completion/git-completion.zsh
> +++ b/contrib/completion/git-completion.zsh
> @@ -227,6 +227,7 @@ __git_zsh_main ()
> '(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
> '(-p --paginate)--no-pager[do not pipe git output into a pager]' \
> '--git-dir=-[set the path to the repository]: :_directories' \
> + '*-C[run as if git was started in <path>]: :_directories' \
> '--bare[treat the repository as a bare repository]' \
> '(- :)--version[prints the git suite version]' \
> '--exec-path=-[path to where your core git programs are installed]:: :_directories' \
... this part talks about not just "-C<dir>" but knows about
all the other options that the "git" potty itself takes, while ...
> @@ -252,6 +253,14 @@ __git_zsh_main ()
> ;;
> (arg)
> local command="${words[1]}" __git_dir __git_cmd_idx=1
> + local -a __git_C_args
> + local -i i=2
> +
> + while [[ ${orig_words[i]} == -C ]]; do
> + __git_C_args+=(-C ${orig_words[i+1]})
> + (( __git_cmd_idx += 2 ))
> + (( i += 2 ))
> + done
... this only knows about "-C<dir>" and nothing else.
Doesn't it want to do something similar to what __git_main in
git-completion.bash does at the beginning, namely, this part?
__git_main ()
{
local i c=1 command __git_dir __git_repo_path
local __git_C_args C_args_count=0
local __git_cmd_idx
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
--git-dir=*)
__git_dir="${i#--git-dir=}"
;;
--git-dir)
((c++))
__git_dir="${words[c]}"
;;
--bare)
__git_dir="."
;;
--help)
command="help"
break
;;
-c|--work-tree|--namespace)
((c++))
;;
-C)
__git_C_args[C_args_count++]=-C
((c++))
__git_C_args[C_args_count++]="${words[c]}"
;;
-*)
;;
*)
command="$i"
__git_cmd_idx="$c"
break
;;
esac
((c++))
done
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-17 17:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17 15:30 [PATCH] completion: zsh: support completion after "git -C <path>" Lutz Lengemann via GitGitGadget
2026-06-17 17:17 ` Ben Knoble
2026-06-17 17:21 ` Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox