git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] refactor completion for switch and checkout
@ 2020-05-27 11:38 Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 1/9] completion: replace overloaded track term for __git_complete_refs Jacob Keller
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

completion support for git switch is subpar for a number of cases. Most
notable is difference in behavior between these two completions:

  $git switch <TAB>
  Display all 784 possibilities? (y or n)
  <list of all references and DWIM remotes>

  $git switch --track<TAB>
  jk-refactor-git-switch-completion master`

If --track is provided, tab completion becomes almost useless, because we
would expect to complete remote references, but instead can only complete
local branches!

This series was motivated by a desire to fix the completion for the above
two cases, but I noticed several other issues on the way, including some
issues understanding what the current logic did.

This series aims to improve the completion support, and comes with many
additional test cases that cover the new behavior implemented in the various
patches.

This is a rework of a previous series posted, available at the following URL:
https://lore.kernel.org/git/20200425022045.1089291-1-jacob.e.keller@intel.com/

Note that although I've marked this as a v2, I did not find the range-diff
to be satisfying or useful and have not included it. Besides not being very
useful, not many folks seem to have reviewed the original anyways.

The most notable change in behavior since v1 is how we handle the -c/-C
options. It makes sense to complete the argument of -c differently than how
we handle the start-point after we already have a completed branch name. The
exact requirements of *how* we complete branch names is easily modified if
anyone has a better suggestion.

Finally, I also applied many of the same improvements to checkout where
appropriate, and have included many more additional tests for both git
switch and git checkout.

Jacob Keller (9):
  completion: replace overloaded track term for __git_complete_refs
  completion: improve handling of DWIM mode for switch/checkout
  completion: extract function __git_dwim_remote_heads
  completion: perform DWIM logic directly in __git_complete_refs
  completion: improve completion for git switch with no options
  completion: improve handling of --detach in checkout
  completion: improve handling of --track in switch/checkout
  completion: improve handling of -c/-C and -b/-B in switch/checkout
  completion: improve handling of --orphan option of switch/checkout

 contrib/completion/git-completion.bash | 252 +++++++++++---
 t/t9902-completion.sh                  | 455 +++++++++++++++++++++++++
 2 files changed, 668 insertions(+), 39 deletions(-)

-- 
2.25.2


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

* [PATCH v2 1/9] completion: replace overloaded track term for __git_complete_refs
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

The __git_complete_refs uses the "--track" option to specify when to
enable listing of unique remote branches which are used by the DWIM
logic of git checkout and git switch.

Using the term '--track' here is confusing because the git commands
themselves have '--track' as an argument. Additionally, the completion
logic for _git_switch also checks for --track. Keeping the meaning of
track_opt and --track for __git_complete_refs straight from the --track
git switch and git checkout option is difficult when reading this code.

Use the option '--dwim' instead, indicating this is about enabling or
disabling logic related to DWIM mode. Also rename the local variable
track_opt to dwim_opt to further reduce the confusion when reading the
completion code for _git_switch.

Because it is plausible for users to have developed their own
completions which rely on __git_complete_ref, keep --track as a synonym
for --dwim, even though we no longer use it in any of the core git
completion logic. Add a comment explaining why it remains as an
alternative spelling for --dwim.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 28 ++++++++++++++------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 70ad04e1b2a8..2972df4cb4c9 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -749,7 +749,7 @@ __git_refs ()
 # Usage: __git_complete_refs [<option>]...
 # --remote=<remote>: The remote to list refs from, can be the name of a
 #                    configured remote, a path, or a URL.
-# --track: List unique remote branches for 'git checkout's tracking DWIMery.
+# --dwim: List unique remote branches for 'git switch's tracking DWIMery.
 # --pfx=<prefix>: A prefix to be added to each ref.
 # --cur=<word>: The current ref to be completed.  Defaults to the current
 #               word to be completed.
@@ -757,12 +757,14 @@ __git_refs ()
 #                 space.
 __git_complete_refs ()
 {
-	local remote track pfx cur_="$cur" sfx=" "
+	local remote dwim pfx cur_="$cur" sfx=" "
 
 	while test $# != 0; do
 		case "$1" in
 		--remote=*)	remote="${1##--remote=}" ;;
-		--track)	track="yes" ;;
+		--dwim)		dwim="yes" ;;
+		# --track is an old spelling of --dwim
+		--track)	dwim="yes" ;;
 		--pfx=*)	pfx="${1##--pfx=}" ;;
 		--cur=*)	cur_="${1##--cur=}" ;;
 		--sfx=*)	sfx="${1##--sfx=}" ;;
@@ -771,7 +773,7 @@ __git_complete_refs ()
 		shift
 	done
 
-	__gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
+	__gitcomp_direct "$(__git_refs "$remote" "$dwim" "$pfx" "$cur_" "$sfx")"
 }
 
 # __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -1370,12 +1372,12 @@ _git_checkout ()
 	*)
 		# check if --track, --no-track, or --no-guess was specified
 		# if so, disable DWIM mode
-		local flags="--track --no-track --no-guess" track_opt="--track"
+		local flags="--track --no-track --no-guess" dwim_opt="--dwim"
 		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
 		   [ -n "$(__git_find_on_cmdline "$flags")" ]; then
-			track_opt=''
+			dwim_opt=''
 		fi
-		__git_complete_refs $track_opt
+		__git_complete_refs $dwim_opt
 		;;
 	esac
 }
@@ -2226,27 +2228,27 @@ _git_switch ()
 	*)
 		# check if --track, --no-track, or --no-guess was specified
 		# if so, disable DWIM mode
-		local track_opt="--track" only_local_ref=n
+		local dwim_opt="--dwim" only_local_ref=n
 		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
 		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
-			track_opt=''
+			dwim_opt=''
 		fi
 		# explicit --guess enables DWIM mode regardless of
 		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
 		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
-			track_opt='--track'
+			dwim_opt='--dwim'
 		fi
 		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
 			only_local_ref=y
 		else
 			# --guess --detach is invalid combination, no
 			# dwim will be done when --detach is specified
-			track_opt=
+			dwim_opt=
 		fi
-		if [ $only_local_ref = y -a -z "$track_opt" ]; then
+		if [ $only_local_ref = y -a -z "$dwim_opt" ]; then
 			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
 		else
-			__git_complete_refs $track_opt
+			__git_complete_refs $dwim_opt
 		fi
 		;;
 	esac
-- 
2.25.2


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

* [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 1/9] completion: replace overloaded track term for __git_complete_refs Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 18:03   ` Junio C Hamano
  2020-05-27 11:38 ` [PATCH v2 3/9] completion: extract function __git_dwim_remote_heads Jacob Keller
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Both git switch and git checkout can "Do What I Mean" by interpreting
a name of unique remote branch as "please create and switch to a local
branch named after and tracking this remote branch".

Completion supports this DWIM mode by allowing to complete these unique
remote branch names. The current logic for when we decide to enable this
DWIM mode is not consistent between git checkout and git switch.

In both cases, this logic is enabled by default. Then, if any of
'--track', '--no-track', or '--no-guess' are provided on the command
line, then we disable it. Additionally, this logic is disabled when
GIT_COMPLETION_CHECKOUT_NO_GUESS is set to 1. For _git_switch, we also
unconditionally enable this logic if "--guess" is provided on the
command line.

The rules for when to enable or disable DWIM have gotten quite
complicated, so its time to refactor these into a function,
__git_checkout_default_dwim_mode().

First, the default is to enable DWIM logic. Then, if '--no-track' is
provided or if the GIT_COMPLETION_CHECKOUT_NO_GUESS variable is set, we
change to default to disabled.

A new helper, __git_find_last_on_cmdline is introduced, similar to the
already existing __git_find_on_cmdline, but which operates in reverse,
finding the *last* matching word of the provided wordlist.

This is then used to find the last --guess or --no-guess. In this way,
if --guess was last specified (and thus git option parsing will think
--guess is enabled), we will unconditionally enable DWIM mode. If
--no-guess is provided last, then we will unconditionally disable DWIM
mode. Finally, if neither are specified, we use the default.

This new logic is more robust, as we will correctly identify superseded
options, and ensure that both _git_switch and _git_checkout enable DWIM
in similar ways.

Add several tests which demonstrate the new expected behavior. Note that
some of the git switch tests are marked as failures because the default
git switch completion with --guess is sub-par as discussed in a previous
commit. This will be fixed in a future change.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 95 ++++++++++++++++++++-----
 t/t9902-completion.sh                  | 98 ++++++++++++++++++++++++++
 2 files changed, 174 insertions(+), 19 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 2972df4cb4c9..ed966f5e2991 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1104,6 +1104,40 @@ __git_find_on_cmdline ()
 	done
 }
 
+# Similar to __git_find_on_cmdline, except that it loops backwards and thus
+# prints the *last* word found. Useful for finding which of two options that
+# supersede each other came last, such as "--guess" and "--no-guess".
+#
+# Usage: __git_find_last_on_cmdline [<option>]... "<wordlist>"
+# --show-idx: Optionally show the index of the found word in the $words array.
+__git_find_last_on_cmdline ()
+{
+	local word c=$cword show_idx
+
+	while test $# -gt 1; do
+		case "$1" in
+		--show-idx)	show_idx=y ;;
+		*)		return 1 ;;
+		esac
+		shift
+	done
+	local wordlist="$1"
+
+	while [ $c -gt 1 ]; do
+		((c--))
+		for word in $wordlist; do
+			if [ "$word" = "${words[c]}" ]; then
+				if [ -n "$show_idx" ]; then
+					echo "$c $word"
+				else
+					echo "$word"
+				fi
+				return
+			fi
+		done
+	done
+}
+
 # Echo the value of an option set on the command line or config
 #
 # $1: short option name
@@ -1358,6 +1392,46 @@ _git_bundle ()
 	esac
 }
 
+# Helper function to decide whether or not we should enable DWIM logic for
+# git-switch and git-checkout.
+#
+# To decide between the following rules in priority order
+# 1) the last provided of "--guess" or "--no-guess" explicitly enable or
+#    disable completion of DWIM logic respectively.
+# 2) If the --no-track option is provided, take this as a hint to disable the
+#    DWIM completion logic
+# 3) If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
+#    logic, as requested by the user.
+# 4) Enable DWIM logic otherwise.
+#
+__git_checkout_default_dwim_mode ()
+{
+	local last_option dwim_opt="--dwim"
+
+	if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ]; then
+		dwim_opt=""
+	fi
+
+	# --no-track disables DWIM, but with lower priority than
+	# --guess/--no-guess
+	if [ -n "$(__git_find_on_cmdline "--no-track")" ]; then
+		dwim_opt=""
+	fi
+
+	# Find the last provided --guess or --no-guess
+	last_option="$(__git_find_last_on_cmdline "--guess --no-guess")"
+	case "$last_option" in
+		--guess)
+			dwim_opt="--dwim"
+			;;
+		--no-guess)
+			dwim_opt=""
+			;;
+	esac
+
+	echo "$dwim_opt"
+}
+
 _git_checkout ()
 {
 	__git_has_doubledash && return
@@ -1370,13 +1444,7 @@ _git_checkout ()
 		__gitcomp_builtin checkout
 		;;
 	*)
-		# check if --track, --no-track, or --no-guess was specified
-		# if so, disable DWIM mode
-		local flags="--track --no-track --no-guess" dwim_opt="--dwim"
-		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
-		   [ -n "$(__git_find_on_cmdline "$flags")" ]; then
-			dwim_opt=''
-		fi
+		local dwim_opt="$(__git_checkout_default_dwim_mode)"
 		__git_complete_refs $dwim_opt
 		;;
 	esac
@@ -2226,18 +2294,7 @@ _git_switch ()
 		__gitcomp_builtin switch
 		;;
 	*)
-		# check if --track, --no-track, or --no-guess was specified
-		# if so, disable DWIM mode
-		local dwim_opt="--dwim" only_local_ref=n
-		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
-		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
-			dwim_opt=''
-		fi
-		# explicit --guess enables DWIM mode regardless of
-		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
-		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
-			dwim_opt='--dwim'
-		fi
+		local dwim_opt="$(__git_checkout_default_dwim_mode)" only_local_ref=n
 		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
 			only_local_ref=y
 		else
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 3c44af694015..6d90d19d9fe5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1240,6 +1240,104 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
 	test_cmp expected out
 '
 
+test_expect_success 'git switch - with --no-guess, complete only local branches' '
+	test_completion "git switch --no-guess " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete only local branches' '
+	GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_failure 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
+	GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch --guess " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_failure 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
+	test_completion "git switch --no-guess --guess " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - a later --no-guess overrides previous --guess, complete only local branches' '
+	test_completion "git switch --guess --no-guess " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only completes refs' '
+	GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git checkout " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches for DWIM' '
+	GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git checkout --guess " <<-\EOF
+	HEAD Z
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with --no-guess, only completes refs' '
+	test_completion "git checkout --no-guess " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - a later --guess overrides previous --no-guess, complete refs and unique remote branches for DWIM' '
+	test_completion "git checkout --no-guess --guess " <<-\EOF
+	HEAD Z
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - a later --no-guess overrides previous --guess, complete only refs' '
+	test_completion "git checkout --guess --no-guess " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v2 3/9] completion: extract function __git_dwim_remote_heads
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 1/9] completion: replace overloaded track term for __git_complete_refs Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 4/9] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller, Junio C Hamano

From: Jacob Keller <jacob.keller@gmail.com>

__git_refs() has the ability to report unique remote names for
supporting completion of remote branch names for the DWIMery of git
checkout and git switch.

For git checkout, this is fine, because it always supports completing
all local references.

However, git switch by default only supports either switching branches
or using this DWIMery to create a local branch tracking the remote
branch.

Future work to cleanup and improve completion support for git switch
will be aided if the remote branch names can be completed separately
from __git_refs.

Extract this logic to a function __git_dwim_remote_heads(), and use it
in __git_refs.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 contrib/completion/git-completion.bash | 28 +++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index ed966f5e2991..8854812cb32e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -621,6 +621,26 @@ __git_tags ()
 			"refs/tags/$cur_*" "refs/tags/$cur_*/**"
 }
 
+# List unique branches from refs/remotes used for 'git checkout' and 'git
+# switch' tracking DWIMery.
+# 1: A prefix to be added to each listed branch (optional)
+# 2: List only branches matching this word (optional; list all branches if
+#    unset or empty).
+# 3: A suffix to be appended to each listed branch (optional).
+__git_dwim_remote_heads ()
+{
+	local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+	local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
+
+	# employ the heuristic used by git checkout and git switch
+	# Try to find a remote branch that cur_es the completion word
+	# but only output if the branch name is unique
+	__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+		--sort="refname:strip=3" \
+		"refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
+	uniq -u
+}
+
 # Lists refs from the local (by default) or from a remote repository.
 # It accepts 0, 1 or 2 arguments:
 # 1: The remote to list refs from (optional; ignored, if set but empty).
@@ -696,13 +716,7 @@ __git_refs ()
 		__git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
 			"${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
-			__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
-				--sort="refname:strip=3" \
-				"refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
-			uniq -u
+			__git_dwim_remote_heads "$pfx" "$match" "$sfx"
 		fi
 		return
 	fi
-- 
2.25.2


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

* [PATCH v2 4/9] completion: perform DWIM logic directly in __git_complete_refs
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (2 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 3/9] completion: extract function __git_dwim_remote_heads Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 5/9] completion: improve completion for git switch with no options Jacob Keller
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller, Junio C Hamano

From: Jacob Keller <jacob.keller@gmail.com>

__git_complete_refs is the main function used for completing references.
It is primarily used as a wrapper around __git_refs, and is easier to
extend since its arguments are option-like.

One major downside of __git_complete_refs and __git_refs currently, is
the lack of ability to complete only a subset of refs such as branches
(refs/heads) or tags (refs/tags).

Normally, a caller might just decide to use __git_heads() or
__git_tags(). However, in the case of git-switch, it is useful to
complete both branches *and* DWIM remote branch names.

Due to the complexity and implementation of __git_refs, it is not easy
to extend it to support listing only a subset of references.

Instead, we can extend __git_complete_refs to do this. For this to be
done, we must first ensure that "--dwim" support is not tied to calling
__git_refs.

Instead of passing $dwim into __git_refs, we can implement
a __gitcomp_direct_append function which can append to COMPREPLY after
a call to __gitcomp_direct.

If --dwim is passed to __git_complete_refs, use __gitcomp_direct_append
to add the output of __git_dwim_remote_heads to the completion list.

In this way, --dwim support is now independent of calling __git_refs.

A future change will add an additional option to control what set of
references __git_complete_refs will output.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 contrib/completion/git-completion.bash | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8854812cb32e..54cd676fdc9d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -301,6 +301,19 @@ __gitcomp_direct ()
 	COMPREPLY=($1)
 }
 
+# Similar to __gitcomp_direct, but appends to COMPREPLY instead.
+# Callers must take care of providing only words that match the current word
+# to be completed and adding any prefix and/or suffix (trailing space!), if
+# necessary.
+# 1: List of newline-separated matching completion words, complete with
+#    prefix and suffix.
+__gitcomp_direct_append ()
+{
+	local IFS=$'\n'
+
+	COMPREPLY+=($1)
+}
+
 __gitcompappend ()
 {
 	local x i=${#COMPREPLY[@]}
@@ -787,7 +800,11 @@ __git_complete_refs ()
 		shift
 	done
 
-	__gitcomp_direct "$(__git_refs "$remote" "$dwim" "$pfx" "$cur_" "$sfx")"
+	__gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")"
+
+	if [ "$dwim" = "yes" ]; then
+		__gitcomp_direct_append "$(__git_dwim_remote_heads "$pfx" "$cur_" "$sfx")"
+	fi
 }
 
 # __git_refs2 requires 1 argument (to pass to __git_refs)
-- 
2.25.2


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

* [PATCH v2 5/9] completion: improve completion for git switch with no options
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (3 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 4/9] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 6/9] completion: improve handling of --detach in checkout Jacob Keller
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

The current default behavior of completion for git switch is
sub-optimal. Unlike checkout, git switch by default focuses on switching
branches. It does not allow detaching HEAD without the --detach option.
By default, git switch accepts only branch names. If a unique remote
branch name is given, then it will "Do What I Mean" and automatically
create a local branch tracking that remote branch.

However, "git switch <TAB>" will complete more than just branch names!
It actually completes all local references, including tags and fully
specified remote branch names.

As an example, today completion will provide the following words:

  $git switch <TAB>
  branch-in-other
  master
  master-in-other
  matching-branch
  matching-tag
  other/branch-in-other
  other/master-in-other

This includes many options which are invalid, such as tags, and fully
specified remote branches. Avoid completing these.

Add a new --mode option to __git_complete_refs, which allows changing
the behavior to call __git_heads instead of __git_refs.

By passing --mode=heads, __git_complete_refs will only output local
branches. This enables using "--mode=heads --dwim" to enable listing
local branches and the remote unique branch names for DWIM.

Refactor completion support to use the new mode option, rather than
calling __git_heads directly. This has the advantage that we can now
correctly allow local branches along with suitable DWIM refs, rather
than only allowing DWIM when we complete all references.

Choose what mode it uses when calling __git_complete_refs. If -d or
--detach have been provided, then simply complete all refs, but
*without* the DWIM option as these DWIM names won't work properly in
--detach mode.

Otherwise, call __git_complete_refs with the default dwim_opt value and
use the new "heads" mode.

In this way, the basic support for completing just "git switch <TAB>"
will result in only local branches and remote unique names for DWIM, so
switch this test to test_expect_success.

Add new test cases that cover the default behavior of completion for git
switch and git checkout with no options.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 33 ++++++++++++++++----------
 t/t9902-completion.sh                  | 26 ++++++++++++++++++--
 2 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 54cd676fdc9d..53afd72d0e4e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -782,9 +782,12 @@ __git_refs ()
 #               word to be completed.
 # --sfx=<suffix>: A suffix to be appended to each ref instead of the default
 #                 space.
+# --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to
+#                complete all refs, 'heads' to complete only branches. Note
+#                that --remote is only compatible with --mode=refs.
 __git_complete_refs ()
 {
-	local remote dwim pfx cur_="$cur" sfx=" "
+	local remote dwim pfx cur_="$cur" sfx=" " mode="refs"
 
 	while test $# != 0; do
 		case "$1" in
@@ -795,13 +798,23 @@ __git_complete_refs ()
 		--pfx=*)	pfx="${1##--pfx=}" ;;
 		--cur=*)	cur_="${1##--cur=}" ;;
 		--sfx=*)	sfx="${1##--sfx=}" ;;
+		--mode=*)	mode="${1##--mode=}" ;;
 		*)		return 1 ;;
 		esac
 		shift
 	done
 
-	__gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")"
+	# complete references based on the specified mode
+	case "$mode" in
+		refs)
+			__gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")" ;;
+		heads)
+			__gitcomp_direct "$(__git_heads "$pfx" "$cur_" "$sfx")" ;;
+		*)
+			return 1 ;;
+	esac
 
+	# Append DWIM remote branch names if requested
 	if [ "$dwim" = "yes" ]; then
 		__gitcomp_direct_append "$(__git_dwim_remote_heads "$pfx" "$cur_" "$sfx")"
 	fi
@@ -2325,18 +2338,12 @@ _git_switch ()
 		__gitcomp_builtin switch
 		;;
 	*)
-		local dwim_opt="$(__git_checkout_default_dwim_mode)" only_local_ref=n
-		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
-			only_local_ref=y
+		local dwim_opt="$(__git_checkout_default_dwim_mode)"
+
+		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
+			__git_complete_refs --mode="refs"
 		else
-			# --guess --detach is invalid combination, no
-			# dwim will be done when --detach is specified
-			dwim_opt=
-		fi
-		if [ $only_local_ref = y -a -z "$dwim_opt" ]; then
-			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
-		else
-			__git_complete_refs $dwim_opt
+			__git_complete_refs $dwim_opt --mode="heads"
 		fi
 		;;
 	esac
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 6d90d19d9fe5..ec9437688cd7 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1240,6 +1240,28 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
 	test_cmp expected out
 '
 
+test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
+	test_completion "git switch " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
+	test_completion "git checkout " <<-\EOF
+	HEAD Z
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
 test_expect_success 'git switch - with --no-guess, complete only local branches' '
 	test_completion "git switch --no-guess " <<-\EOF
 	master Z
@@ -1254,7 +1276,7 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl
 	EOF
 '
 
-test_expect_failure 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
+test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
 	GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch --guess " <<-\EOF
 	branch-in-other Z
 	master Z
@@ -1263,7 +1285,7 @@ test_expect_failure 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G
 	EOF
 '
 
-test_expect_failure 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
+test_expect_success 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
 	test_completion "git switch --no-guess --guess " <<-\EOF
 	branch-in-other Z
 	master Z
-- 
2.25.2


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

* [PATCH v2 6/9] completion: improve handling of --detach in checkout
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (4 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 5/9] completion: improve completion for git switch with no options Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 7/9] completion: improve handling of --track in switch/checkout Jacob Keller
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Just like git switch, we should not complete DWIM remote branch names
if --detach has been specified. To avoid this, refactor _git_checkout in
a similar way to _git_switch.

Note that we don't simply clear dwim_opt when we find -d or --detach, as
we will be adding other modes and checks, making this flow easier to
follow.

Add new tests for the expected --detach behavior for both git switch and
git checkout.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash |  7 +++-
 t/t9902-completion.sh                  | 44 ++++++++++++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 53afd72d0e4e..38b5a5a0d874 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1489,7 +1489,12 @@ _git_checkout ()
 		;;
 	*)
 		local dwim_opt="$(__git_checkout_default_dwim_mode)"
-		__git_complete_refs $dwim_opt
+
+		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
+			__git_complete_refs --mode="refs"
+		else
+			__git_complete_refs $dwim_opt --mode="refs"
+		fi
 		;;
 	esac
 }
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index ec9437688cd7..5b1868e43632 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1360,6 +1360,50 @@ test_expect_success 'git checkout - a later --no-guess overrides previous --gues
 	EOF
 '
 
+test_expect_success 'git switch - with --detach, complete all references' '
+	test_completion "git switch --detach " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with --detach, complete only references' '
+	test_completion "git checkout --detach " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -d, complete all references' '
+	test_completion "git switch -d " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -d, complete only references' '
+	test_completion "git checkout -d " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v2 7/9] completion: improve handling of --track in switch/checkout
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (5 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 6/9] completion: improve handling of --detach in checkout Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 8/9] completion: improve handling of -c/-C and -b/-B " Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 9/9] completion: improve handling of --orphan option of switch/checkout Jacob Keller
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Current completion for the --track option of git switch and git checkout
is sub par. In addition to the DWIM logic of a bare branch name, --track
has DWIM logic to convert specified remote/branch names into a local
branch tracking that remote. For example

  $git switch --track origin/master

This will create a local branch name master, that tracks the master
branch of the origin remote.

In fact, git switch --track on its own will not accept other forms of
references. These must instead be specified manually via the -c/-C/-b/-B
options.

Introduce __git_remote_heads() and the "remote-heads" mode for
__git_complete_refs. Use this when the --track option is provided while
completing in _git_switch and _git_checkout. Just as in the --detach
case, we never enable DWIM mode for --track, because it doesn't make
sense.

It should be noted that completion support is still a bit sub par when
it comes to handling -c/-C and --orphan. This will be resolved in
a future change.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 24 +++++++++++++++++--
 t/t9902-completion.sh                  | 32 ++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 38b5a5a0d874..4cdf09987725 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -624,6 +624,19 @@ __git_heads ()
 			"refs/heads/$cur_*" "refs/heads/$cur_*/**"
 }
 
+# Lists branches from remote repositories.
+# 1: A prefix to be added to each listed branch (optional).
+# 2: List only branches matching this word (optional; list all branches if
+#    unset or empty).
+# 3: A suffix to be appended to each listed branch (optional).
+__git_remote_heads ()
+{
+	local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+
+	__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+			"refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
+}
+
 # Lists tags from the local repository.
 # Accepts the same positional parameters as __git_heads() above.
 __git_tags ()
@@ -783,8 +796,9 @@ __git_refs ()
 # --sfx=<suffix>: A suffix to be appended to each ref instead of the default
 #                 space.
 # --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to
-#                complete all refs, 'heads' to complete only branches. Note
-#                that --remote is only compatible with --mode=refs.
+#                complete all refs, 'heads' to complete only branches, or
+#                'remote-heads' to complete only remote branches. Note that
+#                --remote is only compatible with --mode=refs.
 __git_complete_refs ()
 {
 	local remote dwim pfx cur_="$cur" sfx=" " mode="refs"
@@ -810,6 +824,8 @@ __git_complete_refs ()
 			__gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")" ;;
 		heads)
 			__gitcomp_direct "$(__git_heads "$pfx" "$cur_" "$sfx")" ;;
+		remote-heads)
+			__gitcomp_direct "$(__git_remote_heads "$pfx" "$cur_" "$sfx")" ;;
 		*)
 			return 1 ;;
 	esac
@@ -1492,6 +1508,8 @@ _git_checkout ()
 
 		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
 			__git_complete_refs --mode="refs"
+		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+			__git_complete_refs --mode="remote-heads"
 		else
 			__git_complete_refs $dwim_opt --mode="refs"
 		fi
@@ -2347,6 +2365,8 @@ _git_switch ()
 
 		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
 			__git_complete_refs --mode="refs"
+		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+			__git_complete_refs --mode="remote-heads"
 		else
 			__git_complete_refs $dwim_opt --mode="heads"
 		fi
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 5b1868e43632..b09eb498d175 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1404,6 +1404,38 @@ test_expect_success 'git checkout - with -d, complete only references' '
 	EOF
 '
 
+test_expect_success 'git switch - with --track, complete only remote branches' '
+	test_completion "git switch --track " <<-\EOF
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with --track, complete only remote branches' '
+	test_completion "git checkout --track " <<-\EOF
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with --no-track, complete only local branch names' '
+	test_completion "git switch --no-track " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - with --no-track, complete only local references' '
+	test_completion "git checkout --no-track " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v2 8/9] completion: improve handling of -c/-C and -b/-B in switch/checkout
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (6 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 7/9] completion: improve handling of --track in switch/checkout Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  2020-05-27 11:38 ` [PATCH v2 9/9] completion: improve handling of --orphan option of switch/checkout Jacob Keller
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Currently, completion for git switch and git checkout do not take into
account the branch creation options, -c/-C and -b/-B respectively.

The form of git switch with -c or -C is to take a branch name followed
by a starting point. For this reason, when completing -c or -C, we ought
to be able to complete the start point as any arbitrary reference.

However, we probably do not want to complete the actual argument for
these options with any reference, as this would provide too many
unexpected items.

Use the wordlist to determine the previous word on the command line. If
it's -c or -C (-b/-B for checkout), then we know that we are completing
the argument for the branch name. One possible assumption is that we
have no valid completion. But consider that in many cases a user may
wish to complete a branch by editing the name of a pre-existing branch.

Given that a user who already knows the branch name they want to
complete will simply not use completion, it makes sense to complete the
small subset of local branches when completing the argument for -c/-C.

In all other cases, if -c/-C are on the command line but are not the
most recent option, then we must be completing a start-point, and should
allow completing against all references.

Add many tests to cover various behavior and interaction around enabling
or disabling DWIM mode.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash |  49 +++++-
 t/t9902-completion.sh                  | 224 +++++++++++++++++++++++++
 2 files changed, 271 insertions(+), 2 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 4cdf09987725..60666429dba4 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1505,8 +1505,31 @@ _git_checkout ()
 		;;
 	*)
 		local dwim_opt="$(__git_checkout_default_dwim_mode)"
+		local prevword prevword="${words[cword-1]}"
 
-		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
+		case "$prevword" in
+			-b|-B)
+				# Complete local branches (and DWIM branch
+				# remote branch names) for an option argument
+				# specifying a new branch name. This is for
+				# convenience, assuming new branches are
+				# possibly based on pre-existing branch names.
+				__git_complete_refs $dwim_opt --mode="heads"
+				return
+				;;
+			*)
+				;;
+		esac
+
+		# At this point, we've already handled special completion for
+		# the arguments to -b/-B. There are 3 main things left we can
+		# possibly complete:
+		# 1) a start-point for -b/-B or -d/--detach
+		# 2) a remote head, for --track
+		# 3) an arbitrary reference, possibly including DWIM names
+		#
+
+		if [ -n "$(__git_find_on_cmdline "-b -B -d --detach")" ]; then
 			__git_complete_refs --mode="refs"
 		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
 			__git_complete_refs --mode="remote-heads"
@@ -2362,8 +2385,30 @@ _git_switch ()
 		;;
 	*)
 		local dwim_opt="$(__git_checkout_default_dwim_mode)"
+		local prevword prevword="${words[cword-1]}"
 
-		if [ -n "$(__git_find_on_cmdline "-d --detach")" ]; then
+		case "$prevword" in
+			-c|-C)
+				# Complete local branches (and DWIM branch
+				# remote branch names) for an option argument
+				# specifying a new branch name. This is for
+				# convenience, assuming new branches are
+				# possibly based on pre-existing branch names.
+				__git_complete_refs $dwim_opt --mode="heads"
+				return
+				;;
+			*)
+				;;
+		esac
+
+		# At this point, we've already handled special completion for
+		# -c/-C. There are 3 main things left to
+		# complete:
+		# 1) a start-point for -c/-C or -d/--detach
+		# 2) a remote head, for --track
+		# 3) a branch name, possibly including DWIM remote branches
+
+		if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then
 			__git_complete_refs --mode="refs"
 		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
 			__git_complete_refs --mode="remote-heads"
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index b09eb498d175..14ebd175dfa5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1436,6 +1436,230 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
 	EOF
 '
 
+test_expect_success 'git switch - with -c, complete all references' '
+	test_completion "git switch -c new-branch " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -C, complete all references' '
+	test_completion "git switch -C new-branch " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -c and --track, complete all references' '
+	test_completion "git switch -c new-branch --track " <<-EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -C and --track, complete all references' '
+	test_completion "git switch -C new-branch --track " <<-EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -c and --no-track, complete all references' '
+	test_completion "git switch -c new-branch --no-track " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - with -C and --no-track, complete all references' '
+	test_completion "git switch -C new-branch --no-track " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -b, complete all references' '
+	test_completion "git checkout -b new-branch " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -B, complete all references' '
+	test_completion "git checkout -B new-branch " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -b and --track, complete all references' '
+	test_completion "git checkout -b new-branch --track " <<-EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -B and --track, complete all references' '
+	test_completion "git checkout -B new-branch --track " <<-EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -b and --no-track, complete all references' '
+	test_completion "git checkout -b new-branch --no-track " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git checkout - with -B and --no-track, complete all references' '
+	test_completion "git checkout -B new-branch --no-track " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
+test_expect_success 'git switch - for -c, complete local branches and unique remote branches' '
+	test_completion "git switch -c " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - for -C, complete local branches and unique remote branches' '
+	test_completion "git switch -C " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - for -c with --no-guess, complete local branches only' '
+	test_completion "git switch --no-guess -c " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - for -C with --no-guess, complete local branches only' '
+	test_completion "git switch --no-guess -C " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - for -c with --no-track, complete local branches only' '
+	test_completion "git switch --no-track -c " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - for -C with --no-track, complete local branches only' '
+	test_completion "git switch --no-track -C " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -b, complete local branches and unique remote branches' '
+	test_completion "git checkout -b " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -B, complete local branches and unique remote branches' '
+	test_completion "git checkout -B " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -b with --no-guess, complete local branches only' '
+	test_completion "git checkout --no-guess -b " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -B with --no-guess, complete local branches only' '
+	test_completion "git checkout --no-guess -B " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -b with --no-track, complete local branches only' '
+	test_completion "git checkout --no-track -b " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - for -B with --no-track, complete local branches only' '
+	test_completion "git checkout --no-track -B " <<-\EOF
+	master Z
+	matching-branch Z
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v2 9/9] completion: improve handling of --orphan option of switch/checkout
  2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
                   ` (7 preceding siblings ...)
  2020-05-27 11:38 ` [PATCH v2 8/9] completion: improve handling of -c/-C and -b/-B " Jacob Keller
@ 2020-05-27 11:38 ` Jacob Keller
  8 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 11:38 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

The --orphan option is used to create a local branch which is detached
from the current history. In git switch, it always resets to the empty
tree, and thus the only completion we can provide is a branch name.
Follow the same rules for -c/-C (and -b/-B) when completing the argument
to --orphan.

In the case of git switch, after we complete the argument, there is
nothing more we can complete for git switch, so do not even try. Nothing
else would be valid.

In the case of git checkout, --orphan takes a start point which it uses
to determine the checked out tree, even though it created orphaned
history.

Add test cases which cover this completion behavior.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 21 ++++++++++------
 t/t9902-completion.sh                  | 35 ++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 60666429dba4..de85b84f8116 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1508,7 +1508,7 @@ _git_checkout ()
 		local prevword prevword="${words[cword-1]}"
 
 		case "$prevword" in
-			-b|-B)
+			-b|-B|--orphan)
 				# Complete local branches (and DWIM branch
 				# remote branch names) for an option argument
 				# specifying a new branch name. This is for
@@ -1522,14 +1522,14 @@ _git_checkout ()
 		esac
 
 		# At this point, we've already handled special completion for
-		# the arguments to -b/-B. There are 3 main things left we can
-		# possibly complete:
-		# 1) a start-point for -b/-B or -d/--detach
+		# the arguments to -b/-B, and --orphan. There are 3 main
+		# things left we can possibly complete:
+		# 1) a start-point for -b/-B, -d/--detach, or --orphan
 		# 2) a remote head, for --track
 		# 3) an arbitrary reference, possibly including DWIM names
 		#
 
-		if [ -n "$(__git_find_on_cmdline "-b -B -d --detach")" ]; then
+		if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
 			__git_complete_refs --mode="refs"
 		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
 			__git_complete_refs --mode="remote-heads"
@@ -2388,7 +2388,7 @@ _git_switch ()
 		local prevword prevword="${words[cword-1]}"
 
 		case "$prevword" in
-			-c|-C)
+			-c|-C|--orphan)
 				# Complete local branches (and DWIM branch
 				# remote branch names) for an option argument
 				# specifying a new branch name. This is for
@@ -2401,8 +2401,15 @@ _git_switch ()
 				;;
 		esac
 
+		# Unlike in git checkout, git switch --orphan does not take
+		# a start point. Thus we really have nothing to complete after
+		# the branch name.
+		if [ -n "$(__git_find_on_cmdline "--orphan")" ]; then
+			return
+		fi
+
 		# At this point, we've already handled special completion for
-		# -c/-C. There are 3 main things left to
+		# -c/-C, and --orphan. There are 3 main things left to
 		# complete:
 		# 1) a start-point for -c/-C or -d/--detach
 		# 2) a remote head, for --track
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 14ebd175dfa5..8f434a0931e5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1660,6 +1660,41 @@ test_expect_success 'git checkout - for -B with --no-track, complete local branc
 	EOF
 '
 
+test_expect_success 'git switch - with --orphan completes local branch names and unique remote branch names' '
+	test_completion "git switch --orphan " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git switch - --orphan with branch already provided completes nothing else' '
+	test_completion "git switch --orphan master " <<-\EOF
+
+	EOF
+'
+
+test_expect_success 'git checkout - with --orphan completes local branch names and unique remote branch names' '
+	test_completion "git checkout --orphan " <<-\EOF
+	branch-in-other Z
+	master Z
+	master-in-other Z
+	matching-branch Z
+	EOF
+'
+
+test_expect_success 'git checkout - --orphan with branch already provided completes local refs for a start-point' '
+	test_completion "git checkout --orphan master " <<-\EOF
+	HEAD Z
+	master Z
+	matching-branch Z
+	matching-tag Z
+	other/branch-in-other Z
+	other/master-in-other Z
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* Re: [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout
  2020-05-27 11:38 ` [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
@ 2020-05-27 18:03   ` Junio C Hamano
  2020-05-27 19:41     ` Jacob Keller
  0 siblings, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2020-05-27 18:03 UTC (permalink / raw)
  To: Jacob Keller; +Cc: git, Jonathan Nieder, Jacob Keller

Jacob Keller <jacob.e.keller@intel.com> writes:

> This new logic is more robust, as we will correctly identify superseded
> options, and ensure that both _git_switch and _git_checkout enable DWIM
> in similar ways.
>
> Add several tests which demonstrate the new expected behavior. Note that
> some of the git switch tests are marked as failures because the default
> git switch completion with --guess is sub-par as discussed in a previous
> commit. This will be fixed in a future change.

"as discussed in a previous commit"?  1/9 does not mention --guess
at all.  Perhaps this is an unintended damage due to rebasing?

In any case, up to this point, the log messages are a bit
frustrating read, primarily because the author has spent too much
time on DWIM and expect everybody understands exactly what he means
when he says "DWIM mode is enabled", while a reader in me keeps
asking:

 - what refs get included when I type <TAB> without DWIM mode?

 - what extra refs get included when DWIM mode is enabled?

 - under DWIM mode, do certain refs (that would be included without
   DWIM mode) stop appearing in the completion?

and without getting any answer to them.  Perhaps the proposed log
message for [1/9] can be made a bit more robust to cover it?

Thanks.


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

* Re: [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout
  2020-05-27 18:03   ` Junio C Hamano
@ 2020-05-27 19:41     ` Jacob Keller
  2020-05-28  8:53       ` Jacob Keller
  0 siblings, 1 reply; 13+ messages in thread
From: Jacob Keller @ 2020-05-27 19:41 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jacob Keller, Git mailing list, Jonathan Nieder

On Wed, May 27, 2020 at 11:03 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Jacob Keller <jacob.e.keller@intel.com> writes:
>
> > This new logic is more robust, as we will correctly identify superseded
> > options, and ensure that both _git_switch and _git_checkout enable DWIM
> > in similar ways.
> >
> > Add several tests which demonstrate the new expected behavior. Note that
> > some of the git switch tests are marked as failures because the default
> > git switch completion with --guess is sub-par as discussed in a previous
> > commit. This will be fixed in a future change.
>
> "as discussed in a previous commit"?  1/9 does not mention --guess
> at all.  Perhaps this is an unintended damage due to rebasing?
>

Yes I originally had a patch earlier in the series which just added
test cases, but I later moved that into the patch that fixed the
tests.

> In any case, up to this point, the log messages are a bit
> frustrating read, primarily because the author has spent too much
> time on DWIM and expect everybody understands exactly what he means
> when he says "DWIM mode is enabled", while a reader in me keeps
> asking:

Right, I thought I had an explanation of this somewhere but it appears
to have been lost while re-ordering and re-wording.

>
>  - what refs get included when I type <TAB> without DWIM mode?
>
>  - what extra refs get included when DWIM mode is enabled?
>
>  - under DWIM mode, do certain refs (that would be included without
>    DWIM mode) stop appearing in the completion?
>
> and without getting any answer to them.  Perhaps the proposed log
> message for [1/9] can be made a bit more robust to cover it?
>

Sure, I'll look at what I can do to make this more logical.

> Thanks.
>

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

* Re: [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout
  2020-05-27 19:41     ` Jacob Keller
@ 2020-05-28  8:53       ` Jacob Keller
  0 siblings, 0 replies; 13+ messages in thread
From: Jacob Keller @ 2020-05-28  8:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jacob Keller, Git mailing list, Jonathan Nieder

On Wed, May 27, 2020 at 12:41 PM Jacob Keller <jacob.keller@gmail.com> wrote:
> >
> > and without getting any answer to them.  Perhaps the proposed log
> > message for [1/9] can be made a bit more robust to cover it?
> >
>
> Sure, I'll look at what I can do to make this more logical.
>
> > Thanks.
> >

I'll have a v3 which splits most of the tests into their own patch
with a better description of the reasoning of the problem with output
and the new expected behavior. The commit descriptions for the
improvements will then focus primarily on the reasons for the
particular method of implementing that output. It's a few more
patches, but hopefully it will read more logically, and we can help
settle some of the open questions:

particular to interest me is: what sort of words should we complete
when completing a new branch name for --orphan, -c/-C and -b/-B? I
think all of these ought to remain as consistent as possible, but I'm
not sure what other folks think.

Thanks,
Jake

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

end of thread, other threads:[~2020-05-28  8:53 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-27 11:38 [PATCH v2 0/9] refactor completion for switch and checkout Jacob Keller
2020-05-27 11:38 ` [PATCH v2 1/9] completion: replace overloaded track term for __git_complete_refs Jacob Keller
2020-05-27 11:38 ` [PATCH v2 2/9] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
2020-05-27 18:03   ` Junio C Hamano
2020-05-27 19:41     ` Jacob Keller
2020-05-28  8:53       ` Jacob Keller
2020-05-27 11:38 ` [PATCH v2 3/9] completion: extract function __git_dwim_remote_heads Jacob Keller
2020-05-27 11:38 ` [PATCH v2 4/9] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
2020-05-27 11:38 ` [PATCH v2 5/9] completion: improve completion for git switch with no options Jacob Keller
2020-05-27 11:38 ` [PATCH v2 6/9] completion: improve handling of --detach in checkout Jacob Keller
2020-05-27 11:38 ` [PATCH v2 7/9] completion: improve handling of --track in switch/checkout Jacob Keller
2020-05-27 11:38 ` [PATCH v2 8/9] completion: improve handling of -c/-C and -b/-B " Jacob Keller
2020-05-27 11:38 ` [PATCH v2 9/9] completion: improve handling of --orphan option of switch/checkout Jacob Keller

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).