Subject: [PATCH] bash completion: Resolve git show ref:path<tab> losing ref: portion

From: "Shawn O. Pearce" <[email-masked]>
Date: Tue, 15 Jul 2008 05:39:07 +0000

Linus reported that the bash completion for git show often dropped
the ref portion of the argument (stuff before the :) when trying
to complete a file name of a file in another branch or tag.

Bj?rn Steinbrink tracked it down to the gvfs completion script
which comes standard on many Fedora Core based systems.  That is
removing : from COMP_WORDBREAKS, making readline treat the entire
argument (including the ref) as the name that must be completed.
When the git completion routines supplied a completion of just
the filename, readline replaced everything.

Since Git users often need to use "ref:path" or "ref:ref" sort of
arguments, and expect completion support on both sides of the :
we really want the : in COMP_WORDBREAKS to provide a good user
experience.  This is also the default that ships with bash as it
can be useful in other contexts, such as rcp/scp.

We now try to add : back to COMP_WORDBREAKS if it has been removed
by a script that loaded before us.  However if this doesn't work
(as the : is stripped after we load) we fallback in the completion
routines to include "ref:" as part of the prefix for completions,
allowing readine to fully insert the argument the user wanted.

Signed-off-by: Shawn O. Pearce <[email-masked]>
---
 contrib/completion/git-completion.bash |   27 +++++++++++++++++++++++++--
 1 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c03230a..5844804 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -45,6 +45,11 @@
 #       [email-masked]
 #
 
+case "$COMP_WORDBREAKS" in
+*:*) : great ;;
+*)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
+esac
+
 __gitdir ()
 {
 	if [ -z "$1" ]; then
@@ -294,6 +299,12 @@ __git_complete_file ()
 			ls="$ref"
 			;;
 	    esac
+
+		case "$COMP_WORDBREAKS" in
+		*:*) : great ;;
+		*)   pfx="$ref:$pfx" ;;
+		esac
+
 		local IFS=$'\n'
 		COMPREPLY=($(compgen -P "$pfx" \
 			-W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
@@ -700,7 +711,12 @@ _git_fetch ()
 	*)
 		case "$cur" in
 		*:*)
-			__gitcomp "$(__git_refs)" "" "${cur#*:}"
+			local pfx=""
+			case "$COMP_WORDBREAKS" in
+			*:*) : great ;;
+			*)   pfx="${cur%%:*}:" ;;
+			esac
+			__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
 			;;
 		*)
 			local remote
@@ -876,7 +892,14 @@ _git_push ()
 			git-push)  remote="${COMP_WORDS[1]}" ;;
 			git)       remote="${COMP_WORDS[2]}" ;;
 			esac
-			__gitcomp "$(__git_refs "$remote")" "" "${cur#*:}"
+
+			local pfx=""
+			case "$COMP_WORDBREAKS" in
+			*:*) : great ;;
+			*)   pfx="${cur%%:*}:" ;;
+			esac
+
+			__gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}"
 			;;
 		+*)
 			__gitcomp "$(__git_refs)" + "${cur#+}"
-- 
1.5.6.2.393.g45096