git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] bash-completion fixes for global git options handling
@ 2015-10-28 17:21 Peter Wu
  2015-10-28 17:21 ` [PATCH 1/3] completion: ignore git options for subcommand completion Peter Wu
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Peter Wu @ 2015-10-28 17:21 UTC (permalink / raw)
  To: git

Hi,

These patches improve bash-completion when global git options are present.
Consider this problem:

    bash-4.3$ git -C ../linux sta<TAB>error: invalid key: alias.../linux

This happens because the current script is unaware of the -C option. In general,
global options are not handled well (patch 001 fixes this).

Patch 2 makes the completions more aware of the --git-dir option.

Patch 3 builds on previous patches and makes completions aware of the -C option.

Kind regards,
Peter

Peter Wu (3):
  completion: ignore git options for subcommand completion
  completion: pass --git-dir to more commands
  completion: handle git -C option

 contrib/completion/git-completion.bash | 119 ++++++++++++++++++++-------------
 1 file changed, 72 insertions(+), 47 deletions(-)

-- 
2.6.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] completion: ignore git options for subcommand completion
  2015-10-28 17:21 [PATCH 0/3] bash-completion fixes for global git options handling Peter Wu
@ 2015-10-28 17:21 ` Peter Wu
  2015-10-28 17:21 ` [PATCH 2/3] completion: pass --git-dir to more commands Peter Wu
  2015-10-28 17:21 ` [PATCH 3/3] completion: handle git -C option Peter Wu
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Wu @ 2015-10-28 17:21 UTC (permalink / raw)
  To: git

Do not assume that the first option after "git" is a subcommand. This
fixes completion such as:

    # do not detect "push" as remote name
    git --git-dir=git/.git push origin <TAB>
    # do not overwrite "--git-dir=..." with the alias expansion
    git --git-dir=git/.git gerrit-diff <TAB>

Signed-off-by: Peter Wu <peter@lekensteyn.nl>
---
 contrib/completion/git-completion.bash | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 482ca84..bd9ef4c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -531,8 +531,8 @@ __git_complete_revlist ()
 
 __git_complete_remote_or_refspec ()
 {
-	local cur_="$cur" cmd="${words[1]}"
-	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+	local cur_="$cur" cmd="${words[command_word_index]}"
+	local i c=$((command_word_index+1)) remote="" pfx="" lhs=1 no_complete_refspec=0
 	if [ "$cmd" = "remote" ]; then
 		((c++))
 	fi
@@ -788,7 +788,7 @@ __git_aliased_command ()
 # __git_find_on_cmdline requires 1 argument
 __git_find_on_cmdline ()
 {
-	local word subcommand c=1
+	local word subcommand c=$command_word_index
 	while [ $c -lt $cword ]; do
 		word="${words[c]}"
 		for subcommand in $1; do
@@ -803,7 +803,7 @@ __git_find_on_cmdline ()
 
 __git_has_doubledash ()
 {
-	local c=1
+	local c=$command_word_index
 	while [ $c -lt $cword ]; do
 		if [ "--" = "${words[c]}" ]; then
 			return 0
@@ -826,8 +826,8 @@ __git_count_arguments ()
 {
 	local word i c=0
 
-	# Skip "git" (first argument)
-	for ((i=1; i < ${#words[@]}; i++)); do
+	# Skip "git" (first argument) and any options such as --git-dir.
+	for ((i=command_word_index; i < ${#words[@]}; i++)); do
 		word="${words[i]}"
 
 		case "$word" in
@@ -957,7 +957,7 @@ _git_bisect ()
 
 _git_branch ()
 {
-	local i c=1 only_local_ref="n" has_r="n"
+	local i c=$command_word_index only_local_ref="n" has_r="n"
 
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
@@ -992,7 +992,7 @@ _git_branch ()
 
 _git_bundle ()
 {
-	local cmd="${words[2]}"
+	local cmd="${words[command_word_index+1]}"
 	case "$cword" in
 	2)
 		__gitcomp "create list-heads verify unbundle"
@@ -1760,7 +1760,7 @@ _git_stage ()
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
-	while [ $c -gt 1 ]; do
+	while [ $c -gt $command_word_index ]; do
 		word="${words[c]}"
 		case "$word" in
 		--system|--global|--local|--file=*)
@@ -2516,7 +2516,7 @@ _git_svn ()
 
 _git_tag ()
 {
-	local i c=1 f=0
+	local i c=$command_word_index f=0
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
 		case "$i" in
@@ -2562,7 +2562,7 @@ _git_whatchanged ()
 
 __git_main ()
 {
-	local i c=1 command __git_dir
+	local i c=1 command command_word_index __git_dir
 
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
@@ -2573,7 +2573,7 @@ __git_main ()
 		--help) command="help"; break ;;
 		-c|--work-tree|--namespace) ((c++)) ;;
 		-*) ;;
-		*) command="$i"; break ;;
+		*) command="$i"; command_word_index=$c; break ;;
 		esac
 		((c++))
 	done
@@ -2608,7 +2608,7 @@ __git_main ()
 
 	local expansion=$(__git_aliased_command "$command")
 	if [ -n "$expansion" ]; then
-		words[1]=$expansion
+		words[command_word_index]=$expansion
 		completion_func="_git_${expansion//-/_}"
 		declare -f $completion_func >/dev/null && $completion_func
 	fi
-- 
2.6.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] completion: pass --git-dir to more commands
  2015-10-28 17:21 [PATCH 0/3] bash-completion fixes for global git options handling Peter Wu
  2015-10-28 17:21 ` [PATCH 1/3] completion: ignore git options for subcommand completion Peter Wu
@ 2015-10-28 17:21 ` Peter Wu
  2015-10-28 17:21 ` [PATCH 3/3] completion: handle git -C option Peter Wu
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Wu @ 2015-10-28 17:21 UTC (permalink / raw)
  To: git

The --git-dir option influences more commands, but was not applied
during completions. For example:

    # previously empty because --git-dir was not passed to ls-remote
    git --git-dir=git/.git config merge.o<TAB>

Add --git-dir to more git commands (but not for repo-independent
commands such as git help) and add a new internal "__git_options"
array to store this option. In future patches, the -C option will also
be added.  (Alternatively, a new wrapper function can be added instead
of duplicating `${__git_options[@]}` all over the place, but let's keep
it simple for now.)

Add a variable and comments to __git_refs for clarity. (Note that
`--git-dir` needs to be kept there because it may not be the same as
the current repo, e.g. via `git fetch /tmp/repo <TAB>`.)

Signed-off-by: Peter Wu <peter@lekensteyn.nl>
---
 contrib/completion/git-completion.bash | 52 ++++++++++++++++++++--------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index bd9ef4c..fdf0f16 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -282,10 +282,10 @@ __gitcomp_file ()
 __git_ls_files_helper ()
 {
 	if [ "$2" == "--committable" ]; then
-		git -C "$1" diff-index --name-only --relative HEAD
+		git "${__git_options[@]}" -C "$1" diff-index --name-only --relative HEAD
 	else
 		# NOTE: $2 is not quoted in order to support multiple options
-		git -C "$1" ls-files --exclude-standard $2
+		git "${__git_options[@]}" -C "$1" ls-files --exclude-standard $2
 	fi 2>/dev/null
 }
 
@@ -315,7 +315,7 @@ __git_heads ()
 {
 	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
-		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+		git "${__git_options[@]}" for-each-ref --format='%(refname:short)' \
 			refs/heads
 		return
 	fi
@@ -325,7 +325,7 @@ __git_tags ()
 {
 	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
-		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+		git "${__git_options[@]}" for-each-ref --format='%(refname:short)' \
 			refs/tags
 		return
 	fi
@@ -336,8 +336,9 @@ __git_tags ()
 # by checkout for tracking branches
 __git_refs ()
 {
-	local i hash dir="$(__gitdir "${1-}")" track="${2-}"
+	local i hash dir="$(__gitdir "${1-}")" track="${2-}" repo
 	local format refs
+	# Try refs from a local repository directory (e.g. "../linux")
 	if [ -d "$dir" ]; then
 		case "$cur" in
 		refs|refs/*)
@@ -353,14 +354,15 @@ __git_refs ()
 			refs="refs/tags refs/heads refs/remotes"
 			;;
 		esac
-		git --git-dir="$dir" for-each-ref --format="%($format)" \
-			$refs
+		git "${__git_options[@]}" --git-dir="$dir" \
+			for-each-ref --format="%($format)" $refs
 		if [ -n "$track" ]; then
 			# employ the heuristic used by git checkout
 			# Try to find a remote branch that matches the completion word
 			# but only output if the branch name is unique
 			local ref entry
-			git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
+			git "${__git_options[@]}" --git-dir="$dir" \
+				for-each-ref --shell --format="ref=%(refname:short)" \
 				"refs/remotes/" | \
 			while read -r entry; do
 				eval "$entry"
@@ -372,9 +374,11 @@ __git_refs ()
 		fi
 		return
 	fi
+	# Try refs from a remote repository by name (e.g. "origin") or a URL
+	repo="${1-}"
 	case "$cur" in
 	refs|refs/*)
-		git ls-remote "$dir" "$cur*" 2>/dev/null | \
+		git "${__git_options[@]}" ls-remote "$repo" "$cur*" 2>/dev/null | \
 		while read -r hash i; do
 			case "$i" in
 			*^{}) ;;
@@ -384,8 +388,8 @@ __git_refs ()
 		;;
 	*)
 		echo "HEAD"
-		git for-each-ref --format="%(refname:short)" -- \
-			"refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
+		git "${__git_options[@]}" for-each-ref --format="%(refname:short)" -- \
+			"refs/remotes/$repo/" 2>/dev/null | sed -e "s#^$repo/##"
 		;;
 	esac
 }
@@ -403,7 +407,7 @@ __git_refs2 ()
 __git_refs_remotes ()
 {
 	local i hash
-	git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+	git "${__git_options[@]}" ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
 	while read -r hash i; do
 		echo "$i:refs/remotes/$1/${i#refs/heads/}"
 	done
@@ -413,7 +417,7 @@ __git_remotes ()
 {
 	local d="$(__gitdir)"
 	test -d "$d/remotes" && ls -1 "$d/remotes"
-	git --git-dir="$d" remote
+	git "${__git_options[@]}" remote
 }
 
 __git_list_merge_strategies ()
@@ -467,7 +471,7 @@ __git_complete_revlist_file ()
 		*)   pfx="$ref:$pfx" ;;
 		esac
 
-		__gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
+		__gitcomp_nl "$(git "${__git_options[@]}" ls-tree "$ls" 2>/dev/null \
 				| sed '/^100... blob /{
 				           s,^.*	,,
 				           s,$, ,
@@ -744,7 +748,7 @@ __git_compute_porcelain_commands ()
 __git_get_config_variables ()
 {
 	local section="$1" i IFS=$'\n'
-	for i in $(git --git-dir="$(__gitdir)" config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
+	for i in $(git "${__git_options[@]}" config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
 		echo "${i#$section.}"
 	done
 }
@@ -762,7 +766,7 @@ __git_aliases ()
 # __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
-	local word cmdline=$(git --git-dir="$(__gitdir)" \
+	local word cmdline=$(git "${__git_options[@]}" \
 		config --get "alias.$1")
 	for word in $cmdline; do
 		case "$word" in
@@ -1133,7 +1137,7 @@ _git_commit ()
 		return
 	esac
 
-	if git rev-parse --verify --quiet HEAD >/dev/null; then
+	if git "${__git_options[@]}" rev-parse --verify --quiet HEAD >/dev/null; then
 		__git_complete_index_file "--committable"
 	else
 		# This is the first commit
@@ -1425,7 +1429,7 @@ _git_log ()
 {
 	__git_has_doubledash && return
 
-	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local g="$(git "${__git_options[@]}" rev-parse --git-dir 2>/dev/null)"
 	local merge=""
 	if [ -f "$g/MERGE_HEAD" ]; then
 		merge="--merge"
@@ -1776,7 +1780,7 @@ __git_config_get_set_variables ()
 		c=$((--c))
 	done
 
-	git --git-dir="$(__gitdir)" config $config_file --name-only --list 2>/dev/null
+	git "${__git_options[@]}" config $config_file --name-only --list 2>/dev/null
 }
 
 _git_config ()
@@ -1811,7 +1815,7 @@ _git_config ()
 	remote.*.push)
 		local remote="${prev#remote.}"
 		remote="${remote%.push}"
-		__gitcomp_nl "$(git --git-dir="$(__gitdir)" \
+		__gitcomp_nl "$(git "${__git_options[@]}" \
 			for-each-ref --format='%(refname):%(refname)' \
 			refs/heads)"
 		return
@@ -2389,7 +2393,7 @@ _git_stash ()
 		show,--*|drop,--*|branch,--*)
 			;;
 		show,*|apply,*|drop,*|pop,*|branch,*)
-			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
+			__gitcomp_nl "$(git "${__git_options[@]}" stash list \
 					| sed -n -e 's/:.*//p')"
 			;;
 		*)
@@ -2562,7 +2566,7 @@ _git_whatchanged ()
 
 __git_main ()
 {
-	local i c=1 command command_word_index __git_dir
+	local i c=1 command command_word_index __git_dir __git_options
 
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
@@ -2578,6 +2582,10 @@ __git_main ()
 		((c++))
 	done
 
+	__git_options=(
+		--git-dir="$(__gitdir)"
+	)
+
 	if [ -z "$command" ]; then
 		case "$cur" in
 		--*)   __gitcomp "
-- 
2.6.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] completion: handle git -C option
  2015-10-28 17:21 [PATCH 0/3] bash-completion fixes for global git options handling Peter Wu
  2015-10-28 17:21 ` [PATCH 1/3] completion: ignore git options for subcommand completion Peter Wu
  2015-10-28 17:21 ` [PATCH 2/3] completion: pass --git-dir to more commands Peter Wu
@ 2015-10-28 17:21 ` Peter Wu
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Wu @ 2015-10-28 17:21 UTC (permalink / raw)
  To: git

Avoid the "fatal: bad config file line 5 in config" message and properly
complete git commands having the "-C" option.

Besides the trivial command parsing, __gitdir is rewritten to apply any
directory changes requested via `git -C otherdir ...`.

Signed-off-by: Peter Wu <peter@lekensteyn.nl>
---
 contrib/completion/git-completion.bash | 45 +++++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index fdf0f16..1646f61 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -34,24 +34,39 @@ case "$COMP_WORDBREAKS" in
 esac
 
 # __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
+# outputs location of .git repo if it exists, nothing otherwise.
 __gitdir ()
 {
-	if [ -z "${1-}" ]; then
-		if [ -n "${__git_dir-}" ]; then
-			echo "$__git_dir"
-		elif [ -n "${GIT_DIR-}" ]; then
-			test -d "${GIT_DIR-}" || return 1
-			echo "$GIT_DIR"
-		elif [ -d .git ]; then
-			echo .git
+	local gitdir=${1:-}
+
+	if [ -z "$gitdir" ]; then
+		# Try the first matching --git-dir or GIT_DIR, print nothing if these
+		# directories are invalid.
+		for gitdir in "${__git_dir-}" "${GIT_DIR-}"; do
+			[ -n "$gitdir" ] || continue
+			if [[ "$gitdir" != /* ]]; then
+				gitdir="${__git_cd:-.}/$gitdir"
+			fi
+			if [ -d "$gitdir" ]; then
+				echo "$gitdir"
+			fi
+			return
+		done
+
+		if [ -d "${__git_cd:-.}/.git" ]; then
+			echo "${__git_cd:-.}/.git"
 		else
-			git rev-parse --git-dir 2>/dev/null
+			git -C "$__git_cd" rev-parse --git-dir 2>/dev/null
 		fi
-	elif [ -d "$1/.git" ]; then
-		echo "$1/.git"
 	else
-		echo "$1"
+		if [[ "$gitdir" != /* ]]; then
+			gitdir="${__git_cd:-.}/$gitdir"
+		fi
+		if [ -d "$gitdir/.git" ]; then
+			echo "$gitdir/.git"
+		elif [ -d "$gitdir" ]; then
+			echo "$gitdir"
+		fi
 	fi
 }
 
@@ -2566,11 +2581,12 @@ _git_whatchanged ()
 
 __git_main ()
 {
-	local i c=1 command command_word_index __git_dir __git_options
+	local i c=1 command command_word_index __git_dir __git_options __git_cd=.
 
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
 		case "$i" in
+		-C)          ((c++)) ; __git_cd="${words[c]}" ;;
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
 		--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
 		--bare)      __git_dir="." ;;
@@ -2583,6 +2599,7 @@ __git_main ()
 	done
 
 	__git_options=(
+		-C "$__git_cd"
 		--git-dir="$(__gitdir)"
 	)
 
-- 
2.6.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2015-10-28 17:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-28 17:21 [PATCH 0/3] bash-completion fixes for global git options handling Peter Wu
2015-10-28 17:21 ` [PATCH 1/3] completion: ignore git options for subcommand completion Peter Wu
2015-10-28 17:21 ` [PATCH 2/3] completion: pass --git-dir to more commands Peter Wu
2015-10-28 17:21 ` [PATCH 3/3] completion: handle git -C option Peter Wu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).