completion: hide dotfiles for selected path completion

The completion helper for index paths uses git ls-files rather than
shell filename completion. As a result, leading-dot paths such as a
tracked .gitignore were offered even when the user had not started the
path with ".".

Hide leading-dot path components for git rm, git mv, and git ls-files
when completing an empty path component. Explicit dot completion is
still preserved, so git rm . can still complete .gitignore.

This matches standard shell filename completion behavior, where dotfiles
are hidden by default unless the user starts their input with a dot.
This also resolves four TODO comments in t/9902-completion.sh which
have been present since 2013 (commit ddf07bddef, "completion: add file
completion tests", 2013-04-27), expecting that .gitignore would not be
shown when completing on an empty path component.

Signed-off-by: Zakariyah Ali <zakariyahali100@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Zakariyah Ali
2026-05-26 15:23:07 +00:00
committed by Junio C Hamano
parent 56a4f3c3a2
commit 96d1225ad9
2 changed files with 26 additions and 20 deletions

View File

@@ -638,25 +638,33 @@ __git_ls_files_helper ()
}
# __git_index_files accepts 1 or 2 arguments:
# __git_index_files accepts 1 to 4 arguments:
# 1: Options to pass to ls-files (required).
# 2: A directory path (optional).
# If provided, only files within the specified directory are listed.
# Sub directories are never recursed. Path must have a trailing
# slash.
# 3: List only paths matching this path component (optional).
# 4: Hide paths whose first component starts with a dot if this is
# "hide-dotfiles" and the third argument is empty (optional).
__git_index_files ()
{
local root="$2" match="$3"
local root="$2" match="$3" hide_dotfiles="${4-}"
local hide_dotfiles_awk=0
if [ "$hide_dotfiles" = "hide-dotfiles" ] && [ -z "$match" ]; then
hide_dotfiles_awk=1
fi
__git_ls_files_helper "$root" "$1" "${match:-?}" |
awk -F / -v pfx="${2//\\/\\\\}" '{
awk -F / -v pfx="${2//\\/\\\\}" -v hide_dotfiles="$hide_dotfiles_awk" '{
paths[$1] = 1
}
END {
for (p in paths) {
if (substr(p, 1, 1) != "\"") {
# No special characters, easy!
if (hide_dotfiles == 1 && substr(p, 1, 1) == ".")
continue
print pfx p
continue
}
@@ -675,8 +683,10 @@ __git_index_files ()
# We have seen the same directory unquoted,
# skip it.
continue
else
print pfx p
if (hide_dotfiles == 1 && substr(p, 1, 1) == ".")
continue
print pfx p
}
}
function dequote(p, bs_idx, out, esc, esc_idx, dec) {
@@ -721,13 +731,15 @@ __git_index_files ()
}'
}
# __git_complete_index_file requires 1 argument:
# __git_complete_index_file accepts 1 or 2 arguments:
# 1: the options to pass to ls-file
# 2: Hide paths whose first component starts with a dot if this is
# "hide-dotfiles" and the current word is empty (optional).
#
# The exception is --committable, which finds the files appropriate commit.
__git_complete_index_file ()
{
local dequoted_word pfx="" cur_
local dequoted_word pfx="" cur_ hide_dotfiles="${2-}"
__git_dequote "$cur"
@@ -740,7 +752,7 @@ __git_complete_index_file ()
cur_="$dequoted_word"
esac
__gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")"
__gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_" "$hide_dotfiles")"
}
# Lists branches from the local repository.
@@ -2164,7 +2176,7 @@ _git_ls_files ()
# XXX ignore options like --modified and always suggest all cached
# files.
__git_complete_index_file "--cached"
__git_complete_index_file "--cached" hide-dotfiles
}
_git_ls_remote ()
@@ -2397,9 +2409,9 @@ _git_mv ()
if [ $(__git_count_arguments "mv") -gt 0 ]; then
# We need to show both cached and untracked files (including
# empty directories) since this may not be the last argument.
__git_complete_index_file "--cached --others --directory"
__git_complete_index_file "--cached --others --directory" hide-dotfiles
else
__git_complete_index_file "--cached"
__git_complete_index_file "--cached" hide-dotfiles
fi
}
@@ -3219,7 +3231,7 @@ _git_rm ()
;;
esac
__git_complete_index_file "--cached"
__git_complete_index_file "--cached" hide-dotfiles
}
_git_shortlog ()

View File

@@ -2811,17 +2811,15 @@ test_expect_success 'complete files' '
touch untracked &&
: TODO .gitignore should not be here &&
test_completion "git rm " <<-\EOF &&
.gitignore
modified
EOF
test_completion "git rm ." ".gitignore" &&
test_completion "git clean " "untracked" &&
: TODO .gitignore should not be here &&
test_completion "git mv " <<-\EOF &&
.gitignore
modified
EOF
@@ -2832,9 +2830,7 @@ test_expect_success 'complete files' '
mkdir untracked-dir &&
: TODO .gitignore should not be here &&
test_completion "git mv modified " <<-\EOF &&
.gitignore
dir
modified
untracked
@@ -2843,9 +2839,7 @@ test_expect_success 'complete files' '
test_completion "git commit " "modified" &&
: TODO .gitignore should not be here &&
test_completion "git ls-files " <<-\EOF &&
.gitignore
dir
modified
EOF