git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/16] improve switch/checkout completion
@ 2020-05-28 18:10 Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 01/16] completion: add test showing subpar git switch completion Jacob Keller
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

Recently I noticed that the completion support for git switch is subpar, and
can lead to frustrating moments. Specifically the difference between
completing `git switch` vs `git switch --track`:

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

Notice that git switch will complete basically all references, including
things like HEAD, and remote references like "origin/master". Many of these
values are useless and clutter the output. It's not too bad, but could be
somewhat improved. At least it still completes useful words.

However, the same cannot be said for --track:

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

Notice that with --track, we suddenly start completing only local branches.
Not only does this severely limit what we complete, it includes *only*
useless words. This leads to incredibly frustrating moments, as I often use
"git switch --track remote/<TAB>" to create tracking branches. I found
myself often having to backspace --track just to complete what I wanted and
then re-add it.

The primary original motivation of this series was to improve the handling
of track. Several other issues were discovered, and I attempt to improve
pretty much every mode of switch.

The first few patches simply add new test cases, most of which fail. This is
done in order to give time to fully explain what I believe is deficient
about the specific case.

Following these patches are some cleanup improvements to remove some
confusing terminology and introduce new functions that will be useful to
improve the completion functions.

Finally, several patches which improve completion follow. By the end,
completion for both git switch and git checkout should be more useful and
more aware of the intention of the current argument being completed.

A patch to initially fix just the --track behavior was posted at
https://lore.kernel.org/git/20200422201541.3766173-1-jacob.e.keller@intel.com/

This original patch was reviewed by Jonathan Nieder, and he suggested
further improvements, which led to v1 of this series
https://lore.kernel.org/git/20200425022045.1089291-1-jacob.e.keller@intel.com/

This was further reviewed, and some issues with the handling of completing
the argument to -c/-C were brought up by Junio, which I then improved in v2
https://lore.kernel.org/git/20200527113831.3294409-1-jacob.e.keller@intel.com/

Junio mentioned that the commit messages for this v2 series were difficult
to follow. I have split the tests from the implementation patches and
re-written the commit messages. Hopefully this now better highlights the
places where completion isn't behaving as I would expect.

Once again, given the nature of the rework I did not find a suitable
range-diff that expressed the evolution from v2 to v3, so I haven't included
it here.

I would really like opinions on whether the suggested -c/-C argument
completion makes sense. I opted to limit it to complete local branch names,
and optionally the unique remote branch names. I'm open to alternative
suggestions if anyone has a better suggestion? I had thought perhaps also
some users might wish to see tags, if they have branches named after tags or
similar.. I don't think completing all references makes sense: branches with
remote prefixes in their name would be confusing.

Jacob Keller (16):
  completion: add test showing subpar git switch completion
  completion: add tests showing subpar DWIM logic for switch/checkout
  completion: add tests showing subar checkout --detach logic
  completion: add tests showing subpar switch/checkout --track logic
  completion: add tests showing subpar -c/-C startpoint completion
  completion: add tests showing subpar -c/C argument completion
  completion: add tests showing subpar switch/checkout --orphan logic
  completion: replace overloaded track term for __git_complete_refs
  completion: extract function __git_dwim_remote_heads
  completion: perform DWIM logic directly in __git_complete_refs
  completion: improve handling of DWIM mode for switch/checkout
  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                  | 456 +++++++++++++++++++++++++
 2 files changed, 669 insertions(+), 39 deletions(-)

-- 
2.25.2

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

* [PATCH v3 01/16] completion: add test showing subpar git switch completion
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 02/16] completion: add tests showing subpar DWIM logic for switch/checkout Jacob Keller
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When provided with no options, git switch only allows switching between
branches. The one exception to this is the "Do What I Mean" logic that
allows a unique remote branch name to be interpreted as a request to
create a branch of the same name that is tracking that remote branch.

Unfortunately, the logic for the completion of git switch results in
completing not just branch names, but also pseudorefs like HEAD, tags,
and fully specified <remote>/<branch> references.

For example, we currently complete the following:

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

Indeed, if one were to attempt to use git switch with some of these
provided options, git will reject the request:

 $git switch HEAD
 fatal: a branch is expected, got 'HEAD

 $git switch matching-tag
 fatal: a branch is expected, got tag 'matching-tag'

 $git switch other/branch-in-other
 fatal: a branch is expected, got remote branch 'other/branch-in-other'

Ideally, git switch without options ought to complete only words which
will be accepted. Without options, this means to list local branch names
and the unique remote branch names without their remote name pre-pended.

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

Add a test case that highlights this subpar completion. Also add
a similar test for git checkout completion that shows that due to the
complex nature of git checkout, it must complete all references.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 3c44af694015..8f25fd24b43e 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1240,6 +1240,29 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
 	test_cmp expected out
 '
 
+#TODO: git switch completion includes unexpected references
+test_expect_failure '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 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v3 02/16] completion: add tests showing subpar DWIM logic for switch/checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 01/16] completion: add test showing subpar git switch completion Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 03/16] completion: add tests showing subar checkout --detach logic Jacob Keller
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When provided with a single argument that is the name of a remote branch
that does not yet exist locally, both git switch and git checkout can
interpret this as a request to create a local branch that tracks that
remote branch. We call this behavior "Do What I Mean", or DWIM for
short.

To aid in using this DWIM, it makes sense for completion to list these
unique remote branch names when completing possible arguments for git
switch and git checkout. Indeed, both _git_checkout and _git_switch
implement support for completing such DWIM branch names.

In other words, in addition to the usual completions provided for git
switch, this "DWIM" logic means completion will include the names of
branches on remotes that are unique and thus there can be no ambiguity
of which remote to track when creating the local branch.

However, the DWIM logic is not always active. Many options, such as
--no-guess, --no-track, and --track disable this DWIM logic, as they
cause git switch and git checkout to behave in different modes.

Additionally, some completion users do not wish to have tab completion
include these remote names by default, and thus introduced
GIT_COMPLETION_CHECKOUT_NO_GUESS as an optional way to configure the
completion support to disable this feature of completion support.

For this reason, _git_checkout and _git_switch have many rules about
when to enable or disable completing of these remote refs. The two
commands follow similar but not identical rules.

Set aside the question of command modes that do not accept this DWIM
logic (--track, -c, --orphan, --detach) for now. Thinking just about the
main mode of git checkout and git switch, the following guidelines will
help explain the basic rules we ought to support when deciding whether
to list the remote branches for DWIM in completion.

1.  if --guess is enabled, we should list DWIM remote branch names, even
    if something else would disable it
2.  if --no-guess, --no-track or GIT_COMPLETION_CHECKOUT_NO_GUESS=1,
    then we should disable listing DWIM remote branch names.
3.  Since the '--guess' option is a boolean option, a later --guess
    should override --no-guess, and a later --no-guess should override
    --guess.

Putting all of these together, add some tests that highlight the
expected behavior of this DWIM logic.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 105 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 8f25fd24b43e..ff234dee4da9 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1263,6 +1263,111 @@ test_expect_success 'git checkout - completes refs and unique remote branches fo
 	EOF
 '
 
+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
+'
+
+#TODO: --guess/--no-guess ordering is not taken into account
+#TODO: git switch completion includes unexpected references
+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
+'
+
+#TODO: --guess/--no-guess ordering is not taken into account
+#TODO: git switch completion includes unexpected references
+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
+'
+
+#TODO: --guess/--no-guess ordering is not taken into account
+test_expect_failure '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
+'
+
+#TODO: git checkout does not override variable when --guess is provided
+test_expect_failure '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
+'
+
+#TODO: --guess/--no-guess ordering is not taken into account
+test_expect_failure '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] 17+ messages in thread

* [PATCH v3 03/16] completion: add tests showing subar checkout --detach logic
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 01/16] completion: add test showing subpar git switch completion Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 02/16] completion: add tests showing subpar DWIM logic for switch/checkout Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 04/16] completion: add tests showing subpar switch/checkout --track logic Jacob Keller
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When completing words for git switch, the completion function correctly
disables the DWIM remote branch names when in the '--detach' mode. These
DWIM remote branch names will not work when the --detach option is
specified, so it does not make sense to complete them.

git checkout, however, does not disable the completion of DWIM remote
branch names in this case.

Add test cases for both git switch and git checkout showing the expected
behavior.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 46 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index ff234dee4da9..01aba598e7fc 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1368,6 +1368,52 @@ 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
+'
+
+#TODO: checkout --detach incorrectly includes DWIM remote branch names
+test_expect_failure '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
+'
+
+#TODO: checkout -d incorrectly includes DWIM remote branch names
+test_expect_failure '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] 17+ messages in thread

* [PATCH v3 04/16] completion: add tests showing subpar switch/checkout --track logic
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (2 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 03/16] completion: add tests showing subar checkout --detach logic Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 05/16] completion: add tests showing subpar -c/-C startpoint completion Jacob Keller
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When the --track option is provided to git switch or git checkout, and
no branch is specified by -c or -b, git will interpret the tracking
branch to determine the local branch name to use. This "Do What I Mean"
logic is similar but distinct from the default DWIM logic of
interpreting a unique remote branch name as a request to create and
track that branch.

For example, `git switch --track origin/master` is interpreted as
a request to create a local branch named master that is tracking
origin/master.

The current completion for git checkout in this regard is only somewhat
poor:

 $git checkout --track <TAB>
 HEAD
 master
 matching-branch
 matching-tag
 other/branch-in-other
 other/master-in-other

At least it still includes remote references. The clutter from including
all references isn't too bad.

However, git switch completion is terrible:

 $git switch --track <TAB>
 master
 matching-branch

It only shows local branches, not even allowing any form of completion
of the remote references!

Add tests which highlight the expected behavior of completing --track on
its own.

Note that when -c/-C or -b/-B are provided we do expect completing more
references, but this will be discussed in a future change that addresses
these options specifically.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 01aba598e7fc..8a3995f82f38 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1414,6 +1414,40 @@ test_expect_failure 'git checkout - with -d, complete only references' '
 	EOF
 '
 
+#TODO: --track should only complete fully specified remote branches
+test_expect_failure '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
+'
+
+#TODO: --track should only complete fully specified remote branches
+test_expect_failure '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] 17+ messages in thread

* [PATCH v3 05/16] completion: add tests showing subpar -c/-C startpoint completion
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (3 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 04/16] completion: add tests showing subpar switch/checkout --track logic Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 06/16] completion: add tests showing subpar -c/C argument completion Jacob Keller
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When using the branch creation argument for git switch or git checkout,
-c/-C or -b/-B, the commands operate in a different mode: `git switch -c
<branch> <some-reference>` means to create a branch named <branch> at
the commit referred to by <some-reference>.

When completing the start-point, we ought to always complete all valid
references.

Add tests for the completion of the start-point to -c/-C and -b/-B.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 140 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 8a3995f82f38..609981244e9b 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1448,6 +1448,146 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
 	EOF
 '
 
+#TODO: completing the start point of -c/-C should not include DWIM references
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -c/-C should not include DWIM references
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -c/-C should include all references, not just local branches
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -c/-C should include all references, not just local branches
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -c/-C should include all references, not just local branches
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -c/-C should include all references, not just local branches
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -b/-B should not include DWIM references
+test_expect_failure '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
+'
+
+#TODO: completing the start point of -b/-B should not include DWIM references
+test_expect_failure '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 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
-- 
2.25.2


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

* [PATCH v3 06/16] completion: add tests showing subpar -c/C argument completion
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (4 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 05/16] completion: add tests showing subpar -c/-C startpoint completion Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 07/16] completion: add tests showing subpar switch/checkout --orphan logic Jacob Keller
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

When using the branch creation argument for git switch or git checkout
(-c/-C or -b/-B), the commands switch to a different mode: `git switch
-c <branch> <some-referance>` means to create a branch named <branch> at
the commit referred to by <some-reference>.

When completing git switch or git checkout, it makes sense to complete
the branch name differently from the start point.

When completing a branch, one might consider that we do not have
anything worth completing. After all, a new branch must have an entirely
new name. Consider, however, that if a user names branches using some
similar scheme, they might wish to name a new branch by modifying the
name of an existing branch.

To avoid overloading completion for the argument, it seems reasonable to
complete only the local branch names and the valid "Do What I Mean"
remote branch names.

Add tests for the completion of the argument to -c/-C and -b/-B,
highlighting this preferred completion behavior.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 100 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 609981244e9b..63daf43b89ec 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1588,6 +1588,106 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen
 	EOF
 '
 
+#TODO: -c/-C argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -c/-C argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: -b/-B argument completion should not include all references
+test_expect_failure '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] 17+ messages in thread

* [PATCH v3 07/16] completion: add tests showing subpar switch/checkout --orphan logic
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (5 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 06/16] completion: add tests showing subpar -c/C argument completion Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 08/16] completion: replace overloaded track term for __git_complete_refs Jacob Keller
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

Similar to -c/-C, --orphan takes an argument which is the branch name to
use. We ought to complete this branch name using similar rules as to how
we complete new branch names for -c/-C and -b/-B. Namely, limit the
total number of options provided by completing to the local branches.

Additionally, git switch --orphan does not take any start point and will
always create using the empty-tree. Thus, after the branch name is
completed, git switch --orphan should not complete any references.

Add test cases showing the expected behavior of --orphan, for both the
argument and starting point.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t9902-completion.sh | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 63daf43b89ec..e4c48b20d39d 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1688,6 +1688,45 @@ test_expect_failure 'git checkout - for -B with --no-track, complete local branc
 	EOF
 '
 
+#TODO: --orphan argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: switch --orphan does not take a start-point and thus has nothing to complete
+test_expect_failure 'git switch - --orphan with branch already provided completes nothing else' '
+	test_completion "git switch --orphan master " <<-\EOF
+
+	EOF
+'
+
+#TODO: --orphan argument completion should not include all references
+test_expect_failure '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
+'
+
+#TODO: checkout --orphan start-point completion should not included DWIM remote unique branch names
+test_expect_failure '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] 17+ messages in thread

* [PATCH v3 08/16] completion: replace overloaded track term for __git_complete_refs
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (6 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 07/16] completion: add tests showing subpar switch/checkout --orphan logic Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 09/16] completion: extract function __git_dwim_remote_heads Jacob Keller
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, 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] 17+ messages in thread

* [PATCH v3 09/16] completion: extract function __git_dwim_remote_heads
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (7 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 08/16] completion: replace overloaded track term for __git_complete_refs Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 10/16] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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>
---
 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 2972df4cb4c9..ffe3c499e1a4 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] 17+ messages in thread

* [PATCH v3 10/16] completion: perform DWIM logic directly in __git_complete_refs
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (8 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 09/16] completion: extract function __git_dwim_remote_heads Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 11/16] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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>
---
 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 ffe3c499e1a4..5c13d2cd0fde 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] 17+ messages in thread

* [PATCH v3 11/16] completion: improve handling of DWIM mode for switch/checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (9 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 10/16] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 12/16] completion: improve completion for git switch with no options Jacob Keller
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

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.

Use this in a new __git_checkout_default_dwim_mode() function that will
determine when to enable listing of DWIM remote branches.

The __git_find_last_on_cmdline() function is used to determine which
--guess or --no-guess is in effect. If either one is provided, then we
unconditionally enable or disable the DWIM mode based on the last
provided option.

If neither --guess nor --no-guess is provided, then we check for
--no-track, and finally for GIT_COMPLETION_CHECKOUT_NO_GUESS=1.

This function is then used in _git_switch and _git_checkout to improve
the handling for when we enable listing of these DWIM remote branches.

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.

We can now update a few tests to indicate they pass. A few of the tests
previously added to highlight issues with the old DWIM logic still fail.
This is because of a separate issue related to the default completion
behavior of git switch, which will be addressed in a future change.

Additionally, due to this change, a few tests for the -b/-B handling of
git checkout now fail. This is a minor regression, and will be fixed by
a following change that improves the overall handling of -b/-B. Mark
these tests as known breakages for now.

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

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 5c13d2cd0fde..54cd676fdc9d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1135,6 +1135,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
@@ -1389,6 +1423,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
@@ -1401,13 +1475,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
@@ -2257,18 +2325,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 e4c48b20d39d..13d5a47e2912 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1277,7 +1277,6 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl
 	EOF
 '
 
-#TODO: --guess/--no-guess ordering is not taken into account
 #TODO: git switch completion includes unexpected references
 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
@@ -1288,7 +1287,6 @@ test_expect_failure 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G
 	EOF
 '
 
-#TODO: --guess/--no-guess ordering is not taken into account
 #TODO: git switch completion includes unexpected references
 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
@@ -1299,8 +1297,7 @@ test_expect_failure 'git switch - a later --guess overrides previous --no-guess,
 	EOF
 '
 
-#TODO: --guess/--no-guess ordering is not taken into account
-test_expect_failure 'git switch - a later --no-guess overrides previous --guess, complete only local branches' '
+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
@@ -1318,8 +1315,7 @@ test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only complete
 	EOF
 '
 
-#TODO: git checkout does not override variable when --guess is provided
-test_expect_failure 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches for DWIM' '
+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
@@ -1343,8 +1339,7 @@ test_expect_success 'git checkout - with --no-guess, only completes refs' '
 	EOF
 '
 
-#TODO: --guess/--no-guess ordering is not taken into account
-test_expect_failure 'git checkout - a later --guess overrides previous --no-guess, complete refs and unique remote branches for DWIM' '
+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
@@ -1544,7 +1539,8 @@ test_expect_failure 'git checkout - with -B, complete all references' '
 	EOF
 '
 
-test_expect_success 'git checkout - with -b and --track, complete all references' '
+#TODO: completing the start point of -b/-B should not include DWIM references
+test_expect_failure 'git checkout - with -b and --track, complete all references' '
 	test_completion "git checkout -b new-branch --track " <<-EOF
 	HEAD Z
 	master Z
@@ -1555,7 +1551,8 @@ test_expect_success 'git checkout - with -b and --track, complete all references
 	EOF
 '
 
-test_expect_success 'git checkout - with -B and --track, complete all references' '
+#TODO: completing the start point of -b/-B should not include DWIM references
+test_expect_failure 'git checkout - with -B and --track, complete all references' '
 	test_completion "git checkout -B new-branch --track " <<-EOF
 	HEAD Z
 	master Z
-- 
2.25.2


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

* [PATCH v3 12/16] completion: improve completion for git switch with no options
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (10 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 11/16] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 13/16] completion: improve handling of --detach in checkout Jacob Keller
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

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.

The basic no-options tests for git switch, as well as several of the
-c/-C tests now pass, so remove the known breakage tags.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 33 ++++++++++++++++----------
 t/t9902-completion.sh                  | 19 ++++++---------
 2 files changed, 27 insertions(+), 25 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 13d5a47e2912..f4f3a3ca3d55 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1240,8 +1240,7 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
 	test_cmp expected out
 '
 
-#TODO: git switch completion includes unexpected references
-test_expect_failure 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
+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
@@ -1277,8 +1276,8 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl
 	EOF
 '
 
-#TODO: git switch completion includes unexpected references
-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
@@ -1287,8 +1286,7 @@ test_expect_failure 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G
 	EOF
 '
 
-#TODO: git switch completion includes unexpected references
-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
@@ -1585,8 +1583,7 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen
 	EOF
 '
 
-#TODO: -c/-C argument completion should not include all references
-test_expect_failure 'git switch - for -c, complete local branches and unique remote branches' '
+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
@@ -1595,8 +1592,7 @@ test_expect_failure 'git switch - for -c, complete local branches and unique rem
 	EOF
 '
 
-#TODO: -c/-C argument completion should not include all references
-test_expect_failure 'git switch - for -C, complete local branches and unique remote branches' '
+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
@@ -1685,8 +1681,7 @@ test_expect_failure 'git checkout - for -B with --no-track, complete local branc
 	EOF
 '
 
-#TODO: --orphan argument completion should not include all references
-test_expect_failure 'git switch - with --orphan completes local branch names and unique remote branch names' '
+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
-- 
2.25.2


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

* [PATCH v3 13/16] completion: improve handling of --detach in checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (11 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 12/16] completion: improve completion for git switch with no options Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 14/16] completion: improve handling of --track in switch/checkout Jacob Keller
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, 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.

Update the previously failing tests to show that the breakage has been
resolved.

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

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 f4f3a3ca3d55..7e56a62a9bb3 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1372,8 +1372,7 @@ test_expect_success 'git switch - with --detach, complete all references' '
 	EOF
 '
 
-#TODO: checkout --detach incorrectly includes DWIM remote branch names
-test_expect_failure 'git checkout - with --detach, complete only references' '
+test_expect_success 'git checkout - with --detach, complete only references' '
 	test_completion "git checkout --detach " <<-\EOF
 	HEAD Z
 	master Z
@@ -1395,8 +1394,7 @@ test_expect_success 'git switch - with -d, complete all references' '
 	EOF
 '
 
-#TODO: checkout -d incorrectly includes DWIM remote branch names
-test_expect_failure 'git checkout - with -d, complete only references' '
+test_expect_success 'git checkout - with -d, complete only references' '
 	test_completion "git checkout -d " <<-\EOF
 	HEAD Z
 	master Z
-- 
2.25.2


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

* [PATCH v3 14/16] completion: improve handling of --track in switch/checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (12 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 13/16] completion: improve handling of --detach in checkout Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 15/16] completion: improve handling of -c/-C and -b/-B " Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 16/16] completion: improve handling of --orphan option of switch/checkout Jacob Keller
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, 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                  |  6 ++----
 2 files changed, 24 insertions(+), 6 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 7e56a62a9bb3..f8319868080f 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1405,16 +1405,14 @@ test_expect_success 'git checkout - with -d, complete only references' '
 	EOF
 '
 
-#TODO: --track should only complete fully specified remote branches
-test_expect_failure 'git switch - with --track, complete only remote branches' '
+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
 '
 
-#TODO: --track should only complete fully specified remote branches
-test_expect_failure 'git checkout - with --track, complete only remote branches' '
+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
-- 
2.25.2


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

* [PATCH v3 15/16] completion: improve handling of -c/-C and -b/-B in switch/checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (13 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 14/16] completion: improve handling of --track in switch/checkout Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  2020-05-28 18:10 ` [PATCH v3 16/16] completion: improve handling of --orphan option of switch/checkout Jacob Keller
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, Jacob Keller

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

A previous commit added several test cases highlighting the subpar
completion logic for -c/-C and -b/-B when completing git switch and git
checkout.

In order to distinguish completing the argument vs the start-point for
this option, we now use the wordlist to determine the previous full 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.

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.

Update the -c/-C and -b/-B tests to indicate they now pass.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 49 ++++++++++++++++++++++++--
 t/t9902-completion.sh                  | 48 +++++++++----------------
 2 files changed, 63 insertions(+), 34 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 f8319868080f..810661b10b33 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1437,8 +1437,7 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should not include DWIM references
-test_expect_failure 'git switch - with -c, complete all references' '
+test_expect_success 'git switch - with -c, complete all references' '
 	test_completion "git switch -c new-branch " <<-\EOF
 	HEAD Z
 	master Z
@@ -1449,8 +1448,7 @@ test_expect_failure 'git switch - with -c, complete all references' '
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should not include DWIM references
-test_expect_failure 'git switch - with -C, complete all references' '
+test_expect_success 'git switch - with -C, complete all references' '
 	test_completion "git switch -C new-branch " <<-\EOF
 	HEAD Z
 	master Z
@@ -1461,8 +1459,7 @@ test_expect_failure 'git switch - with -C, complete all references' '
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should include all references, not just local branches
-test_expect_failure 'git switch - with -c and --track, complete all references' '
+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
@@ -1473,8 +1470,7 @@ test_expect_failure 'git switch - with -c and --track, complete all references'
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should include all references, not just local branches
-test_expect_failure 'git switch - with -C and --track, complete all references' '
+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
@@ -1485,8 +1481,7 @@ test_expect_failure 'git switch - with -C and --track, complete all references'
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should include all references, not just local branches
-test_expect_failure 'git switch - with -c and --no-track, complete all references' '
+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
@@ -1497,8 +1492,7 @@ test_expect_failure 'git switch - with -c and --no-track, complete all reference
 	EOF
 '
 
-#TODO: completing the start point of -c/-C should include all references, not just local branches
-test_expect_failure 'git switch - with -C and --no-track, complete all references' '
+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
@@ -1509,8 +1503,7 @@ test_expect_failure 'git switch - with -C and --no-track, complete all reference
 	EOF
 '
 
-#TODO: completing the start point of -b/-B should not include DWIM references
-test_expect_failure 'git checkout - with -b, complete all references' '
+test_expect_success 'git checkout - with -b, complete all references' '
 	test_completion "git checkout -b new-branch " <<-\EOF
 	HEAD Z
 	master Z
@@ -1521,8 +1514,7 @@ test_expect_failure 'git checkout - with -b, complete all references' '
 	EOF
 '
 
-#TODO: completing the start point of -b/-B should not include DWIM references
-test_expect_failure 'git checkout - with -B, complete all references' '
+test_expect_success 'git checkout - with -B, complete all references' '
 	test_completion "git checkout -B new-branch " <<-\EOF
 	HEAD Z
 	master Z
@@ -1533,8 +1525,7 @@ test_expect_failure 'git checkout - with -B, complete all references' '
 	EOF
 '
 
-#TODO: completing the start point of -b/-B should not include DWIM references
-test_expect_failure 'git checkout - with -b and --track, complete all references' '
+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
@@ -1545,8 +1536,7 @@ test_expect_failure 'git checkout - with -b and --track, complete all references
 	EOF
 '
 
-#TODO: completing the start point of -b/-B should not include DWIM references
-test_expect_failure 'git checkout - with -B and --track, complete all references' '
+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
@@ -1625,8 +1615,7 @@ test_expect_success 'git switch - for -C with --no-track, complete local branche
 	EOF
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -b, complete local branches and unique remote branches' '
+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
@@ -1635,8 +1624,7 @@ test_expect_failure 'git checkout - for -b, complete local branches and unique r
 	EOF
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -B, complete local branches and unique remote branches' '
+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
@@ -1645,32 +1633,28 @@ test_expect_failure 'git checkout - for -B, complete local branches and unique r
 	EOF
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -b with --no-guess, complete local branches only' '
+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
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -B with --no-guess, complete local branches only' '
+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
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -b with --no-track, complete local branches only' '
+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
 '
 
-#TODO: -b/-B argument completion should not include all references
-test_expect_failure 'git checkout - for -B with --no-track, complete local branches only' '
+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
-- 
2.25.2


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

* [PATCH v3 16/16] completion: improve handling of --orphan option of switch/checkout
  2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
                   ` (14 preceding siblings ...)
  2020-05-28 18:10 ` [PATCH v3 15/16] completion: improve handling of -c/-C and -b/-B " Jacob Keller
@ 2020-05-28 18:10 ` Jacob Keller
  15 siblings, 0 replies; 17+ messages in thread
From: Jacob Keller @ 2020-05-28 18:10 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Junio C Hamano, 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.

Update the previously added test cases as they are now passing.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 contrib/completion/git-completion.bash | 21 ++++++++++++++-------
 t/t9902-completion.sh                  |  9 +++------
 2 files changed, 17 insertions(+), 13 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 810661b10b33..d42b4191b9cb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1670,15 +1670,13 @@ test_expect_success 'git switch - with --orphan completes local branch names and
 	EOF
 '
 
-#TODO: switch --orphan does not take a start-point and thus has nothing to complete
-test_expect_failure 'git switch - --orphan with branch already provided completes nothing else' '
+test_expect_success 'git switch - --orphan with branch already provided completes nothing else' '
 	test_completion "git switch --orphan master " <<-\EOF
 
 	EOF
 '
 
-#TODO: --orphan argument completion should not include all references
-test_expect_failure 'git checkout - with --orphan completes local branch names and unique remote branch names' '
+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
@@ -1687,8 +1685,7 @@ test_expect_failure 'git checkout - with --orphan completes local branch names a
 	EOF
 '
 
-#TODO: checkout --orphan start-point completion should not included DWIM remote unique branch names
-test_expect_failure 'git checkout - --orphan with branch already provided completes local refs for a start-point' '
+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
-- 
2.25.2


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

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

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-28 18:10 [PATCH v3 00/16] improve switch/checkout completion Jacob Keller
2020-05-28 18:10 ` [PATCH v3 01/16] completion: add test showing subpar git switch completion Jacob Keller
2020-05-28 18:10 ` [PATCH v3 02/16] completion: add tests showing subpar DWIM logic for switch/checkout Jacob Keller
2020-05-28 18:10 ` [PATCH v3 03/16] completion: add tests showing subar checkout --detach logic Jacob Keller
2020-05-28 18:10 ` [PATCH v3 04/16] completion: add tests showing subpar switch/checkout --track logic Jacob Keller
2020-05-28 18:10 ` [PATCH v3 05/16] completion: add tests showing subpar -c/-C startpoint completion Jacob Keller
2020-05-28 18:10 ` [PATCH v3 06/16] completion: add tests showing subpar -c/C argument completion Jacob Keller
2020-05-28 18:10 ` [PATCH v3 07/16] completion: add tests showing subpar switch/checkout --orphan logic Jacob Keller
2020-05-28 18:10 ` [PATCH v3 08/16] completion: replace overloaded track term for __git_complete_refs Jacob Keller
2020-05-28 18:10 ` [PATCH v3 09/16] completion: extract function __git_dwim_remote_heads Jacob Keller
2020-05-28 18:10 ` [PATCH v3 10/16] completion: perform DWIM logic directly in __git_complete_refs Jacob Keller
2020-05-28 18:10 ` [PATCH v3 11/16] completion: improve handling of DWIM mode for switch/checkout Jacob Keller
2020-05-28 18:10 ` [PATCH v3 12/16] completion: improve completion for git switch with no options Jacob Keller
2020-05-28 18:10 ` [PATCH v3 13/16] completion: improve handling of --detach in checkout Jacob Keller
2020-05-28 18:10 ` [PATCH v3 14/16] completion: improve handling of --track in switch/checkout Jacob Keller
2020-05-28 18:10 ` [PATCH v3 15/16] completion: improve handling of -c/-C and -b/-B " Jacob Keller
2020-05-28 18:10 ` [PATCH v3 16/16] 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).