git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC/PATCH] completion: avoid "words" as variable name for zsh portability

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Apr 27, 2011, 06:40 UTC
Message-ID
<20110427064033.GB4226@elie>
In-Reply-To
<7v62q0b8e0.fsf@alter.siamese.dyndns.org>

The "_get_comp_words_by_ref -n := words" command from the bash_completion library reassembles a modified version of COMP_WORDS with ':' and '=' no longer treated as word separators and stores it in the ${words[@]} array. Git's programmable tab completion script uses this to abstract away the difference between bash v3's and bash v4's definitions of COMP_WORDS (bash v3 used shell words, while bash v4 breaks at separator characters); see v1.7.4-rc0~11^2~2 (bash: get --pretty=m<tab> completion to work with bash v4, 2010-12-02).

zsh has (or rather its completion functions have) another idea about what ${words[@]} should contain: the array is prepopulated with the words from the command it is completing. For reasons that are not well understood, when git-completion.bash reserves its own "words" variable with "local words", the variable becomes empty and cannot be changed from then on. So the completion script neglects the arguments it has seen, and words complete like git subcommand names. For example, typing "git log origi<TAB>" gives no completions because there are no "git origi..." commands.

Work around this by using a different variable (comp_words) that is not special to zsh. So now commands that completed correctly before v1.7.4-rc0~11^2~2 on zsh should be able to complete correctly again.

Reported-by: Stefan Haller <lists@haller-berlin.de>
Suggested-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Junio C Hamano wrote:
> I'd love to share the enthusiasm, but find that "as we can see" needs a
> much more clarification.

Sorry, I got carried away (I am happy to see someone has made some headway in investigating this old bug). How about this?

There is still a "for unknown reasons" in the above explanation.
 contrib/completion/git-completion.bash |   68 ++++++++++++++++---------------
 1 files changed, 35 insertions(+), 33 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9150ea6..ce6b3e4 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -447,8 +447,9 @@ _get_comp_words_by_ref ()
 		prev)
 			prev=${words_[$cword_-1]}
 			;;
-		words)
-			words=("${words_[@]}")
+		-w)
+			eval $2'=("${words_[@]}")'
+			shift
 			;;
 		cword)
 			cword=$cword_
@@ -468,8 +469,9 @@ _get_comp_words_by_ref ()
 		prev)
 			prev=${COMP_WORDS[COMP_CWORD-1]}
 			;;
-		words)
-			words=("${COMP_WORDS[@]}")
+		-w)
+			eval $2='("${COMP_WORDS[@]}")'
+			shift
 			;;
 		cword)
 			cword=$COMP_CWORD
@@ -739,12 +741,12 @@ __git_complete_revlist ()
 
 __git_complete_remote_or_refspec ()
 {
-	local cur words cword
-	_get_comp_words_by_ref -n =: cur words cword
-	local cmd="${words[1]}"
+	local cur comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cur cword
+	local cmd="${comp_words[1]}"
 	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
 	while [ $c -lt $cword ]; do
-		i="${words[c]}"
+		i="${comp_words[c]}"
 		case "$i" in
 		--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
 		--all)
@@ -991,10 +993,10 @@ __git_aliased_command ()
 # __git_find_on_cmdline requires 1 argument
 __git_find_on_cmdline ()
 {
-	local word subcommand c=1 words cword
-	_get_comp_words_by_ref -n =: words cword
+	local word subcommand c=1 comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cword
 	while [ $c -lt $cword ]; do
-		word="${words[c]}"
+		word="${comp_words[c]}"
 		for subcommand in $1; do
 			if [ "$subcommand" = "$word" ]; then
 				echo "$subcommand"
@@ -1007,10 +1009,10 @@ __git_find_on_cmdline ()
 
 __git_has_doubledash ()
 {
-	local c=1 words cword
-	_get_comp_words_by_ref -n =: words cword
+	local c=1 comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cword
 	while [ $c -lt $cword ]; do
-		if [ "--" = "${words[c]}" ]; then
+		if [ "--" = "${comp_words[c]}" ]; then
 			return 0
 		fi
 		c=$((++c))
@@ -1135,11 +1137,11 @@ _git_bisect ()
 
 _git_branch ()
 {
-	local i c=1 only_local_ref="n" has_r="n" cur words cword
+	local i c=1 only_local_ref="n" has_r="n" cur comp_words cword
 
-	_get_comp_words_by_ref -n =: cur words cword
+	_get_comp_words_by_ref -n =: -w comp_words cur cword
 	while [ $c -lt $cword ]; do
-		i="${words[c]}"
+		i="${comp_words[c]}"
 		case "$i" in
 		-d|-m)	only_local_ref="y" ;;
 		-r)	has_r="y" ;;
@@ -1167,9 +1169,9 @@ _git_branch ()
 
 _git_bundle ()
 {
-	local words cword
-	_get_comp_words_by_ref -n =: words cword
-	local cmd="${words[2]}"
+	local comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cword
+	local cmd="${comp_words[2]}"
 	case "$cword" in
 	2)
 		__gitcomp "create list-heads verify unbundle"
@@ -1713,15 +1715,15 @@ _git_notes ()
 {
 	local subcommands='add append copy edit list prune remove show'
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
-	local cur words cword
-	_get_comp_words_by_ref -n =: cur words cword
+	local cur comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cur cword
 
 	case "$subcommand,$cur" in
 	,--*)
 		__gitcomp '--ref'
 		;;
 	,*)
-		case "${words[cword-1]}" in
+		case "${comp_words[cword-1]}" in
 		--ref)
 			__gitcomp "$(__git_refs)"
 			;;
@@ -1749,7 +1751,7 @@ _git_notes ()
 	prune,*)
 		;;
 	*)
-		case "${words[cword-1]}" in
+		case "${comp_words[cword-1]}" in
 		-m|-F)
 			;;
 		*)
@@ -1893,11 +1895,11 @@ _git_stage ()
 
 __git_config_get_set_variables ()
 {
-	local words cword
-	_get_comp_words_by_ref -n =: words cword
+	local comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cword
 	local prevword word config_file= c=$cword
 	while [ $c -gt 1 ]; do
-		word="${words[c]}"
+		word="${comp_words[c]}"
 		case "$word" in
 		--global|--system|--file=*)
 			config_file="$word"
@@ -2665,10 +2667,10 @@ _git_svn ()
 _git_tag ()
 {
 	local i c=1 f=0
-	local words cword prev
-	_get_comp_words_by_ref -n =: words cword prev
+	local comp_words cword prev
+	_get_comp_words_by_ref -n =: -w comp_words cword prev
 	while [ $c -lt $cword ]; do
-		i="${words[c]}"
+		i="${comp_words[c]}"
 		case "$i" in
 		-d|-v)
 			__gitcomp "$(__git_tags)"
@@ -2712,10 +2714,10 @@ _git ()
 		setopt KSH_TYPESET
 	fi
 
-	local cur words cword
-	_get_comp_words_by_ref -n =: cur words cword
+	local cur comp_words cword
+	_get_comp_words_by_ref -n =: -w comp_words cur cword
 	while [ $c -lt $cword ]; do
-		i="${words[c]}"
+		i="${comp_words[c]}"
 		case "$i" in
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
 		--bare)      __git_dir="." ;;
-- 
1.7.5
Previous: Junio C HamanoNext: Felipe Contreras
Message 5 of 28 in “git-completion: fix zsh support”
  1. git-completion: fix zsh supportFelipe Contreras, Apr 27, 2011
  2. Jonathan NiederApr 27, 2011
  3. Felipe ContrerasApr 27, 2011
  4. Junio C HamanoApr 27, 2011
  5. completion: avoid "words" as variable name for zsh portabilityJonathan Nieder, Apr 27, 2011
  6. Felipe ContrerasApr 27, 2011
  7. Jonathan NiederApr 27, 2011
  8. Felipe ContrerasApr 27, 2011
  9. John SzakmeisterApr 27, 2011
  10. Felipe ContrerasApr 27, 2011
  11. completion: move private shopt shim for zsh to __git_ namespaceJonathan Nieder, Apr 27, 2011
  12. Felipe ContrerasApr 27, 2011
  13. Jonathan NiederApr 27, 2011
  14. Jonathan NiederMay 6, 2011
  15. Felipe ContrerasMay 6, 2011
  16. SZEDER GáborMay 8, 2011
  17. SZEDER GáborApr 28, 2011
  18. 1/3 bash: don't modify the $cur variable in completion functionsSZEDER Gábor, Apr 28, 2011
  19. 2/3 bash: remove unnecessary _get_comp_words_by_ref() invocationsSZEDER Gábor, Apr 28, 2011
  20. 3/3 bash: don't declare 'local words' to make zsh happySZEDER Gábor, Apr 28, 2011
  21. Felipe ContrerasMay 3, 2011
  22. Felipe ContrerasApr 28, 2011
  23. Junio C HamanoApr 28, 2011
  24. Felipe ContrerasApr 28, 2011
  25. Felipe ContrerasApr 27, 2011
  26. Junio C HamanoApr 27, 2011
  27. Felipe ContrerasApr 27, 2011
  28. Jonathan NiederApr 27, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.