Re: [PATCH v2] completion: zsh: support completion after "git -C <path>"
"D. Ben Knoble" <[email protected]>
| Newsgroups | org.kernel.vger.git |
|---|---|
| Message-ID | <CALnO6CC35iuyJpKZtkEN7fGuGK7zKd_jbebyZdKSQ1pyfOBRZA@mail.gmail.com> |
On Wed, Aug 19, 2026 at 9:07 AM Lutz Lengemann via GitGitGadget <[email protected]> wrote: > > From: Lutz Lengemann <[email protected]> > > 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 <[email protected]> > --- > 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 [email protected] cc: Junio C Hamano [email protected] > > 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 [snip] > > Signed-off-by: Lutz Lengemann <[email protected]> > > @@ 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 Ok, this matches what the message describes about __git_cmd_idx not being able to assume=1; it's different in this version because we are a bit more sophisticated in our parsing. > + 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++ )) At first I thought "should that be i+=2?"; then I saw the unconditional i++ later. Reasonable, though I'm not sure what happens if we walk off the end of the array here: If i=#orig_words, then __git_C_args has (-C) and i becomes #orig_words+2; later, __git_cmd_idx becomes #orig_words+1, which is empty. I'll keep that in mind when looking at how we handle those variables. …Ok, those are handled in the Bash completion. AFAICT, they don't do anything special when the dir is missing either. A bit strange, but not something this patch needs to solve, I suppose. __git_cmd_idx is used many places, as we would imagine, and I didn't look carefully at what happens when it indexes an empty spot (but it looks to mostly be used in comparisons where that would just go falsy, or in arithmetic I haven't really checked at all). (I also haven't thought carefully about the difference between Zsh's 1-based indexing and Bash's 0-based, so I'm not sure if there's an issue lurking there.) > ++ ;; > ++ -c|--git-dir|--work-tree|--namespace) > ++ (( i++ )) > ++ ;; > ++ -*) > ++ ;; Yep, unlike Bash (which requires at least one command in the "list" part between a pattern and the terminator), Zsh accepts empty actions here. > ++ *) > ++ 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' \ At first I wasn't sure about the blank description (space between 2 colons) of the argument to -C, but I see that _directories automatically describes the completed thing as "directory," so that's fine. Overall, if this version works, I think I'm happy with it. Confirming the index math works out between the 2 shells might be a useful exercise, but /shrug. -- D. Ben Knoble