From: "Lutz Lengemann via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Lutz Lengemann <lutz@lengemann.net>,
"D. Ben Knoble" <ben.knoble@gmail.com>,
Lutz Lengemann <lutz@lengemann.net>,
Lutz Lengemann <lutz@lengemann.net>
Subject: [PATCH v2] completion: zsh: support completion after "git -C <path>"
Date: Wed, 19 Aug 2026 13:07:51 +0000 [thread overview]
Message-ID: <pull.2155.v2.git.1787144872870.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2155.git.1781710256081.gitgitgadget@gmail.com>
From: Lutz Lengemann <lutz@lengemann.net>
The zsh completion wrapper does not handle the global -C option, so
git -C <path> <command> <TAB>
offers nothing. -C is not part of the _arguments specification, and the
wrapper hard-codes __git_cmd_idx=1, i.e. it assumes that the command is
the first argument, so the bash helpers look at the wrong word. The
latter is not specific to -C; the assumption breaks after any global
option, e.g. "git -p checkout <TAB>" does not complete branch names.
Add -C to the specification, and find the command by skipping over the
global options and, where they take one, their arguments, as __git_main
in git-completion.bash does. The index is one less than zsh's, as the
helpers count the words from zero. Collect the paths given to -C into
__git_C_args, or else the helpers run git in the current directory and
fail to resolve the aliases and refs of the repository the command runs
in.
The argument of a -C is still completed without regard for the -C
options before it, i.e. "git -C dir -C <TAB>" offers the directories in
".", not the ones in "dir".
Signed-off-by: Lutz Lengemann <lutz@lengemann.net>
---
completion: zsh: support completion after "git -C "
* The command is now located by walking the global options in front of
it, mirroring the loop at the beginning of __git_main in
git-completion.bash, instead of skipping only leading -C options.
This also fixes argument completion after other global options, e.g.
git -p checkout <TAB>.
* The log message uses the present tense for the pre-image and notes
that the argument of a -C is completed without regard for the -C
options before it.
cc: Ben Knoble ben.knoble@gmail.com cc: Junio C Hamano gitster@pobox.com
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2155%2Fmobilutz%2Fzsh-complete-global-C-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2155/mobilutz/zsh-complete-global-C-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2155
Range-diff vs v1:
1: 9739cde6fc ! 1: 9984228f1f completion: zsh: support completion after "git -C <path>"
@@ Metadata
## Commit message ##
completion: zsh: support completion after "git -C <path>"
- 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.
+ The zsh completion wrapper does not handle the global -C option, so
- Three things are needed to make it work, all scoped to -C:
+ git -C <path> <command> <TAB>
- - Add -C to the _arguments specification, so completion no longer stops
- at it.
+ offers nothing. -C is not part of the _arguments specification, and the
+ wrapper hard-codes __git_cmd_idx=1, i.e. it assumes that the command is
+ the first argument, so the bash helpers look at the wrong word. The
+ latter is not specific to -C; the assumption breaks after any global
+ option, e.g. "git -p checkout <TAB>" does not complete branch names.
- - 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.
+ Add -C to the specification, and find the command by skipping over the
+ global options and, where they take one, their arguments, as __git_main
+ in git-completion.bash does. The index is one less than zsh's, as the
+ helpers count the words from zero. Collect the paths given to -C into
+ __git_C_args, or else the helpers run git in the current directory and
+ fail to resolve the aliases and refs of the repository the command runs
+ in.
- - 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.
+ The argument of a -C is still completed without regard for the -C
+ options before it, i.e. "git -C dir -C <TAB>" offers the directories in
+ ".", not the ones in "dir".
Signed-off-by: Lutz Lengemann <lutz@lengemann.net>
@@ contrib/completion/git-completion.zsh: __git_zsh_main ()
'(- :)--version[prints the git suite version]' \
'--exec-path=-[path to where your core git programs are installed]:: :_directories' \
@@ contrib/completion/git-completion.zsh: __git_zsh_main ()
+ done
;;
(arg)
- local command="${words[1]}" __git_dir __git_cmd_idx=1
+- local command="${words[1]}" __git_dir __git_cmd_idx=1
++ local command="${words[1]}" __git_dir __git_cmd_idx
+ 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 ))
++ while (( i <= $#orig_words )); do
++ case ${orig_words[i]} in
++ -C)
++ __git_C_args+=(-C ${orig_words[i+1]})
++ (( i++ ))
++ ;;
++ -c|--git-dir|--work-tree|--namespace)
++ (( i++ ))
++ ;;
++ -*)
++ ;;
++ *)
++ break
++ ;;
++ esac
++ (( i++ ))
+ done
++
++ __git_cmd_idx=$(( i - 1 ))
if (( $+opt_args[--bare] )); then
__git_dir='.'
contrib/completion/git-completion.zsh | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index c32186a977..d5c526665b 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' \
@@ -251,7 +252,29 @@ __git_zsh_main ()
done
;;
(arg)
- local command="${words[1]}" __git_dir __git_cmd_idx=1
+ local command="${words[1]}" __git_dir __git_cmd_idx
+ local -a __git_C_args
+ local -i i=2
+
+ while (( i <= $#orig_words )); do
+ case ${orig_words[i]} in
+ -C)
+ __git_C_args+=(-C ${orig_words[i+1]})
+ (( i++ ))
+ ;;
+ -c|--git-dir|--work-tree|--namespace)
+ (( i++ ))
+ ;;
+ -*)
+ ;;
+ *)
+ break
+ ;;
+ esac
+ (( i++ ))
+ done
+
+ __git_cmd_idx=$(( i - 1 ))
if (( $+opt_args[--bare] )); then
__git_dir='.'
base-commit: 0fae78c9d55efe705877ea537fe42c59164ccd94
--
gitgitgadget
next prev parent reply other threads:[~2026-08-19 13:07 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
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
2026-06-18 17:43 ` D. Ben Knoble
2026-07-14 22:34 ` D. Ben Knoble
2026-08-17 19:29 ` Lutz Lengemann
2026-08-18 12:13 ` D. Ben Knoble
2026-08-18 12:40 ` Lutz Lengemann
2026-08-18 16:35 ` D. Ben Knoble
2026-08-19 13:07 ` Lutz Lengemann via GitGitGadget [this message]
2026-08-20 12:28 ` [PATCH v2] " D. Ben Knoble
2026-08-20 21:39 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.2155.v2.git.1787144872870.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=lutz@lengemann.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.