completion: hide dotfiles by default for path completion

The previous implementation required callers to explicitly pass a "hide-dotfiles" flag to __git_complete_index_file to avoid cluttering completions with hidden files. This led to inconsistent behavior across commands (e.g., `git add` and `git mv` behaved differently) and forced callers to maintain repetitive logic. As suggested by Junio C Hamano, this commit simplifies the logic: 1. __git_complete_index_file now unconditionally hides dotfiles when no match pattern is provided. 2. The awk loop in __git_index_files is refactored to check the dotfile condition in a single, obvious place after handling path dequoting, removing the previous duplication. 3. Callers no longer need to pass "hide-dotfiles". This provides a cleaner API and ensures a consistent, expected behavior where dotfiles are hidden unless explicitly requested by typing a dot. Signed-off-by: Zakariyah Ali <zakariyahali100@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Zakariyah Ali committed Jun 20, 2026 at 17:55 UTC 2cc01b9c55de08d03f30e7ec43236aa08a829dc0
2 files changed +38 -36
contrib/completion/git-completion.bash
+30 -35
@@ -638,20 +638,23 @@ __git_ls_files_helper ()
638 }
639
640
641 -# __git_index_files accepts 1 to 4 arguments:
641 +# __git_index_files accepts 1 to 3 arguments:
642 # 1: Options to pass to ls-files (required).
643 # 2: A directory path (optional).
644 # If provided, only files within the specified directory are listed.
645 # Sub directories are never recursed. Path must have a trailing
646 # slash.
647 # 3: List only paths matching this path component (optional).
648 -# 4: Hide paths whose first component starts with a dot if this is
649 -# "hide-dotfiles" and the third argument is empty (optional).
648 +#
649 +# If the third argument is empty, paths that begin with a dot (dotfiles)
650 +# are hidden. This matches user expectations where dotfiles are considered
651 +# hidden configuration files/directories and shouldn't clutter default
652 +# completions unless explicitly requested by typing a dot.
653 __git_index_files ()
654 {
652 - local root="$2" match="$3" hide_dotfiles="${4-}"
655 + local root="$2" match="$3"
656 local hide_dotfiles_awk=0
654 - if [ "$hide_dotfiles" = "hide-dotfiles" ] && [ -z "$match" ]; then
657 + if [ -z "$match" ]; then
658 hide_dotfiles_awk=1
659 fi
660
@@ -661,28 +664,22 @@ __git_index_files ()
664 }
665 END {
666 for (p in paths) {
664 - if (substr(p, 1, 1) != "\"") {
665 - # No special characters, easy!
666 - if (hide_dotfiles == 1 && substr(p, 1, 1) == ".")
667 + if (substr(p, 1, 1) == "\"") {
668 + # The path is quoted.
669 + p = dequote(p)
670 + if (p == "")
671 continue
668 - print pfx p
669 - continue
670 - }
671 -
672 - # The path is quoted.
673 - p = dequote(p)
674 - if (p == "")
675 - continue
672
677 - # Even when a directory name itself does not contain
678 - # any special characters, it will still be quoted if
679 - # any of its (stripped) trailing path components do.
680 - # Because of this we may have seen the same directory
681 - # both quoted and unquoted.
682 - if (p in paths)
683 - # We have seen the same directory unquoted,
684 - # skip it.
685 - continue
673 + # Even when a directory name itself does not contain
674 + # any special characters, it will still be quoted if
675 + # any of its (stripped) trailing path components do.
676 + # Because of this we may have seen the same directory
677 + # both quoted and unquoted.
678 + if (p in paths)
679 + # We have seen the same directory unquoted,
680 + # skip it.
681 + continue
682 + }
683
684 if (hide_dotfiles == 1 && substr(p, 1, 1) == ".")
685 continue
@@ -731,15 +728,13 @@ __git_index_files ()
728 }'
729 }
730
734 -# __git_complete_index_file accepts 1 or 2 arguments:
735 -# 1: the options to pass to ls-file
736 -# 2: Hide paths whose first component starts with a dot if this is
737 -# "hide-dotfiles" and the current word is empty (optional).
731 +# __git_complete_index_file accepts 1 argument:
732 +# 1: the options to pass to ls-files
733 #
734 # The exception is --committable, which finds the files appropriate commit.
735 __git_complete_index_file ()
736 {
742 - local dequoted_word pfx="" cur_ hide_dotfiles="${2-}"
737 + local dequoted_word pfx="" cur_
738
739 __git_dequote "$cur"
740
@@ -752,7 +747,7 @@ __git_complete_index_file ()
747 cur_="$dequoted_word"
748 esac
749
755 - __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_" "$hide_dotfiles")"
750 + __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")"
751 }
752
753 # Lists branches from the local repository.
@@ -2176,7 +2171,7 @@ _git_ls_files ()
2171
2172 # XXX ignore options like --modified and always suggest all cached
2173 # files.
2179 - __git_complete_index_file "--cached" hide-dotfiles
2174 + __git_complete_index_file "--cached"
2175 }
2176
2177 _git_ls_remote ()
@@ -2409,9 +2404,9 @@ _git_mv ()
2404 if [ $(__git_count_arguments "mv") -gt 0 ]; then
2405 # We need to show both cached and untracked files (including
2406 # empty directories) since this may not be the last argument.
2412 - __git_complete_index_file "--cached --others --directory" hide-dotfiles
2407 + __git_complete_index_file "--cached --others --directory"
2408 else
2414 - __git_complete_index_file "--cached" hide-dotfiles
2409 + __git_complete_index_file "--cached"
2410 fi
2411 }
2412
@@ -3231,7 +3226,7 @@ _git_rm ()
3226 ;;
3227 esac
3228
3234 - __git_complete_index_file "--cached" hide-dotfiles
3229 + __git_complete_index_file "--cached"
3230 }
3231
3232 _git_shortlog ()
t/t9902-completion.sh
+8 -1
@@ -2360,6 +2360,7 @@ test_expect_success 'setup for path completion tests' '
2360 "spaces in dir" \
2361 árvíztűrő &&
2362 touch simple-dir/simple-file \
2363 + simple-dir/.dotfile-in-dir \
2364 "spaces in dir/spaces in file" \
2365 "árvíztűrő/Сайн яваарай" &&
2366 if test_have_prereq !MINGW &&
@@ -2380,6 +2381,11 @@ test_expect_success '__git_complete_index_file - simple' '
2381 test_path_completion simple-dir/simple simple-dir/simple-file
2382 '
2383
2384 +test_expect_success '__git_complete_index_file - dotfiles' '
2385 + test_path_completion "simple-dir/" "simple-dir/simple-file" &&
2386 + test_path_completion "simple-dir/." "simple-dir/.dotfile-in-dir"
2387 +'
2388 +
2389 test_expect_success \
2390 '__git_complete_index_file - escaped characters on cmdline' '
2391 test_path_completion spac "spaces in dir" && # Bash will turn this
@@ -2789,7 +2795,8 @@ test_expect_success 'complete files' '
2795 echo "out_sorted" >> .gitignore &&
2796
2797 git add .gitignore &&
2792 - test_completion "git commit " ".gitignore" &&
2798 + test_completion "git commit " "" &&
2799 + test_completion "git commit ." ".gitignore" &&
2800
2801 git commit -m ignore &&
2802