Skip to content

Commit 425e7f0

Browse files
committed
Merge branch 'en/complete-sparse-checkout'
Command line completion (in contrib/) learned to complete path arguments to the "add/set" subcommands of "git sparse-checkout" better. * en/complete-sparse-checkout: completion: avoid user confusion in non-cone mode completion: avoid misleading completions in cone mode completion: fix logic for determining whether cone mode is active completion: squelch stray errors in sparse-checkout completion
2 parents 624eb90 + a1fbe26 commit 425e7f0

File tree

2 files changed

+127
-5
lines changed

2 files changed

+127
-5
lines changed

contrib/completion/git-completion.bash

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3084,19 +3084,127 @@ __gitcomp_directories ()
30843084
COMPREPLY+=("$c/")
30853085
_found=1
30863086
fi
3087-
done < <(git ls-tree -z -d --name-only HEAD $_tmp_dir)
3087+
done < <(__git ls-tree -z -d --name-only HEAD $_tmp_dir)
30883088

30893089
if [[ $_found == 0 ]] && [[ "$cur" =~ /$ ]]; then
30903090
# No possible further completions any deeper, so assume we're at
30913091
# a leaf directory and just consider it complete
30923092
__gitcomp_direct_append "$cur "
3093+
elif [[ $_found == 0 ]]; then
3094+
# No possible completions found. Avoid falling back to
3095+
# bash's default file and directory completion, because all
3096+
# valid completions have already been searched and the
3097+
# fallbacks can do nothing but mislead. In fact, they can
3098+
# mislead in three different ways:
3099+
# 1) Fallback file completion makes no sense when asking
3100+
# for directory completions, as this function does.
3101+
# 2) Fallback directory completion is bad because
3102+
# e.g. "/pro" is invalid and should NOT complete to
3103+
# "/proc".
3104+
# 3) Fallback file/directory completion only completes
3105+
# on paths that exist in the current working tree,
3106+
# i.e. which are *already* part of their
3107+
# sparse-checkout. Thus, normal file and directory
3108+
# completion is always useless for "git
3109+
# sparse-checkout add" and is also probelmatic for
3110+
# "git sparse-checkout set" unless using it to
3111+
# strictly narrow the checkout.
3112+
COMPREPLY=( "" )
3113+
fi
3114+
}
3115+
3116+
# In non-cone mode, the arguments to {set,add} are supposed to be
3117+
# patterns, relative to the toplevel directory. These can be any kind
3118+
# of general pattern, like 'subdir/*.c' and we can't complete on all
3119+
# of those. However, if the user presses Tab to get tab completion, we
3120+
# presume that they are trying to provide a pattern that names a specific
3121+
# path.
3122+
__gitcomp_slash_leading_paths ()
3123+
{
3124+
local dequoted_word pfx="" cur_ toplevel
3125+
3126+
# Since we are dealing with a sparse-checkout, subdirectories may not
3127+
# exist in the local working copy. Therefore, we want to run all
3128+
# ls-files commands relative to the repository toplevel.
3129+
toplevel="$(git rev-parse --show-toplevel)/"
3130+
3131+
__git_dequote "$cur"
3132+
3133+
# If the paths provided by the user already start with '/', then
3134+
# they are considered relative to the toplevel of the repository
3135+
# already. If they do not start with /, then we need to adjust
3136+
# them to start with the appropriate prefix.
3137+
case "$cur" in
3138+
/*)
3139+
cur="${cur:1}"
3140+
;;
3141+
*)
3142+
pfx="$(__git rev-parse --show-prefix)"
3143+
esac
3144+
3145+
# Since sparse-index is limited to cone-mode, in non-cone-mode the
3146+
# list of valid paths is precisely the cached files in the index.
3147+
#
3148+
# NEEDSWORK:
3149+
# 1) We probably need to take care of cases where ls-files
3150+
# responds with special quoting.
3151+
# 2) We probably need to take care of cases where ${cur} has
3152+
# some kind of special quoting.
3153+
# 3) On top of any quoting from 1 & 2, we have to provide an extra
3154+
# level of quoting for any paths that contain a '*', '?', '\',
3155+
# '[', ']', or leading '#' or '!' since those will be
3156+
# interpreted by sparse-checkout as something other than a
3157+
# literal path character.
3158+
# Since there are two types of quoting here, this might get really
3159+
# complex. For now, just punt on all of this...
3160+
completions="$(__git -C "${toplevel}" -c core.quotePath=false \
3161+
ls-files --cached -- "${pfx}${cur}*" \
3162+
| sed -e s%^%/% -e 's%$% %')"
3163+
# Note, above, though that we needed all of the completions to be
3164+
# prefixed with a '/', and we want to add a space so that bash
3165+
# completion will actually complete an entry and let us move on to
3166+
# the next one.
3167+
3168+
# Return what we've found.
3169+
if test -n "$completions"; then
3170+
# We found some completions; return them
3171+
local IFS=$'\n'
3172+
COMPREPLY=($completions)
3173+
else
3174+
# Do NOT fall back to bash-style all-local-files-and-dirs
3175+
# when we find no match. Such options are worse than
3176+
# useless:
3177+
# 1. "git sparse-checkout add" needs paths that are NOT
3178+
# currently in the working copy. "git
3179+
# sparse-checkout set" does as well, except in the
3180+
# special cases when users are only trying to narrow
3181+
# their sparse checkout to a subset of what they
3182+
# already have.
3183+
#
3184+
# 2. A path like '.config' is ambiguous as to whether
3185+
# the user wants all '.config' files throughout the
3186+
# tree, or just the one under the current directory.
3187+
# It would result in a warning from the
3188+
# sparse-checkout command due to this. As such, all
3189+
# completions of paths should be prefixed with a
3190+
# '/'.
3191+
#
3192+
# 3. We don't want paths prefixed with a '/' to
3193+
# complete files in the system root directory, we
3194+
# want it to complete on files relative to the
3195+
# repository root.
3196+
#
3197+
# As such, make sure that NO completions are offered rather
3198+
# than falling back to bash's default completions.
3199+
COMPREPLY=( "" )
30933200
fi
30943201
}
30953202

30963203
_git_sparse_checkout ()
30973204
{
30983205
local subcommands="list init set disable add reapply"
30993206
local subcommand="$(__git_find_on_cmdline "$subcommands")"
3207+
local using_cone=true
31003208
if [ -z "$subcommand" ]; then
31013209
__gitcomp "$subcommands"
31023210
return
@@ -3107,9 +3215,18 @@ _git_sparse_checkout ()
31073215
__gitcomp_builtin sparse-checkout_$subcommand "" "--"
31083216
;;
31093217
set,*|add,*)
3110-
if [ "$(__git config core.sparseCheckoutCone)" == "true" ] ||
3111-
[ -n "$(__git_find_on_cmdline --cone)" ]; then
3218+
if [[ "$(__git config core.sparseCheckout)" == "true" &&
3219+
"$(__git config core.sparseCheckoutCone)" == "false" &&
3220+
-z "$(__git_find_on_cmdline --cone)" ]]; then
3221+
using_cone=false
3222+
fi
3223+
if [[ -n "$(__git_find_on_cmdline --no-cone)" ]]; then
3224+
using_cone=false
3225+
fi
3226+
if [[ "$using_cone" == "true" ]]; then
31123227
__gitcomp_directories
3228+
else
3229+
__gitcomp_slash_leading_paths
31133230
fi
31143231
esac
31153232
}

t/t9902-completion.sh

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,7 @@ test_expect_success FUNNYNAMES,!CYGWIN 'cone mode sparse-checkout completes dire
15711571
)
15721572
'
15731573

1574-
test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
1574+
test_expect_success 'non-cone mode sparse-checkout gives rooted paths' '
15751575
# reset sparse-checkout repo to non-cone mode
15761576
git -C sparse-checkout sparse-checkout disable &&
15771577
git -C sparse-checkout sparse-checkout set --no-cone &&
@@ -1581,7 +1581,12 @@ test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
15811581
# expected to be empty since we have not configured
15821582
# custom completion for non-cone mode
15831583
test_completion "git sparse-checkout set f" <<-\EOF
1584-
1584+
/folder1/0/1/t.txt Z
1585+
/folder1/expected Z
1586+
/folder1/out Z
1587+
/folder1/out_sorted Z
1588+
/folder2/0/t.txt Z
1589+
/folder3/t.txt Z
15851590
EOF
15861591
)
15871592
'

0 commit comments

Comments
 (0)