Merge branch 'fc/completion'

* fc/completion:
  completion: remove __git_index_file_list_filter()
  completion: add space after completed filename
  completion: add hack to enable file mode in bash < 4
  completion: refactor __git_complete_index_file()
  completion: refactor diff_index wrappers
  completion: use __gitcompadd for __gitcomp_file
  completion; remove unuseful comments
  completion: document tilde expansion failure in tests
  completion: add file completion tests
This commit is contained in:
Junio C Hamano 2013-06-02 15:48:12 -07:00
commit f241c08d40
2 changed files with 107 additions and 115 deletions

View File

@ -252,106 +252,50 @@ __gitcomp_file ()
# since tilde expansion is not applied. # since tilde expansion is not applied.
# This means that COMPREPLY will be empty and Bash default # This means that COMPREPLY will be empty and Bash default
# completion will be used. # completion will be used.
COMPREPLY=($(compgen -P "${2-}" -W "$1" -- "${3-$cur}")) __gitcompadd "$1" "${2-}" "${3-$cur}" ""
# Tell Bash that compspec generates filenames. # use a hack to enable file mode in bash < 4
compopt -o filenames 2>/dev/null compopt -o filenames +o nospace 2>/dev/null ||
compgen -f /non-existing-dir/ > /dev/null
} }
__git_index_file_list_filter_compat () # Execute 'git ls-files', unless the --committable option is specified, in
{ # which case it runs 'git diff-index' to find out the files that can be
local path # committed. It return paths relative to the directory specified in the first
# argument, and using the options specified in the second argument.
while read -r path; do
case "$path" in
?*/*) echo "${path%%/*}/" ;;
*) echo "$path" ;;
esac
done
}
__git_index_file_list_filter_bash ()
{
local path
while read -r path; do
case "$path" in
?*/*)
# XXX if we append a slash to directory names when using
# `compopt -o filenames`, Bash will append another slash.
# This is pretty stupid, and this the reason why we have to
# define a compatible version for this function.
echo "${path%%/*}" ;;
*)
echo "$path" ;;
esac
done
}
# Process path list returned by "ls-files" and "diff-index --name-only"
# commands, in order to list only file names relative to a specified
# directory, and append a slash to directory names.
__git_index_file_list_filter ()
{
# Default to Bash >= 4.x
__git_index_file_list_filter_bash
}
# Execute git ls-files, returning paths relative to the directory
# specified in the first argument, and using the options specified in
# the second argument.
__git_ls_files_helper () __git_ls_files_helper ()
{ {
( (
test -n "${CDPATH+set}" && unset CDPATH test -n "${CDPATH+set}" && unset CDPATH
# NOTE: $2 is not quoted in order to support multiple options cd "$1"
cd "$1" && git ls-files --exclude-standard $2 if [ "$2" == "--committable" ]; then
git diff-index --name-only --relative HEAD
else
# NOTE: $2 is not quoted in order to support multiple options
git ls-files --exclude-standard $2
fi
) 2>/dev/null ) 2>/dev/null
} }
# Execute git diff-index, returning paths relative to the directory
# specified in the first argument, and using the tree object id
# specified in the second argument.
__git_diff_index_helper ()
{
(
test -n "${CDPATH+set}" && unset CDPATH
cd "$1" && git diff-index --name-only --relative "$2"
) 2>/dev/null
}
# __git_index_files accepts 1 or 2 arguments: # __git_index_files accepts 1 or 2 arguments:
# 1: Options to pass to ls-files (required). # 1: Options to pass to ls-files (required).
# Supported options are --cached, --modified, --deleted, --others,
# and --directory.
# 2: A directory path (optional). # 2: A directory path (optional).
# If provided, only files within the specified directory are listed. # If provided, only files within the specified directory are listed.
# Sub directories are never recursed. Path must have a trailing # Sub directories are never recursed. Path must have a trailing
# slash. # slash.
__git_index_files () __git_index_files ()
{ {
local dir="$(__gitdir)" root="${2-.}" local dir="$(__gitdir)" root="${2-.}" file
if [ -d "$dir" ]; then if [ -d "$dir" ]; then
__git_ls_files_helper "$root" "$1" | __git_index_file_list_filter | __git_ls_files_helper "$root" "$1" |
sort | uniq while read -r file; do
fi case "$file" in
} ?*/*) echo "${file%%/*}" ;;
*) echo "$file" ;;
# __git_diff_index_files accepts 1 or 2 arguments: esac
# 1) The id of a tree object. done | sort | uniq
# 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.
__git_diff_index_files ()
{
local dir="$(__gitdir)" root="${2-.}"
if [ -d "$dir" ]; then
__git_diff_index_helper "$root" "$1" | __git_index_file_list_filter |
sort | uniq
fi fi
} }
@ -552,44 +496,23 @@ __git_complete_revlist_file ()
} }
# __git_complete_index_file requires 1 argument: the options to pass to # __git_complete_index_file requires 1 argument:
# ls-file # 1: the options to pass to ls-file
#
# The exception is --committable, which finds the files appropriate commit.
__git_complete_index_file () __git_complete_index_file ()
{ {
local pfx cur_="$cur" local pfx="" cur_="$cur"
case "$cur_" in case "$cur_" in
?*/*) ?*/*)
pfx="${cur_%/*}" pfx="${cur_%/*}"
cur_="${cur_##*/}" cur_="${cur_##*/}"
pfx="${pfx}/" pfx="${pfx}/"
__gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
;;
*)
__gitcomp_file "$(__git_index_files "$1")" "" "$cur_"
;; ;;
esac esac
}
# __git_complete_diff_index_file requires 1 argument: the id of a tree __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
# object
__git_complete_diff_index_file ()
{
local pfx cur_="$cur"
case "$cur_" in
?*/*)
pfx="${cur_%/*}"
cur_="${cur_##*/}"
pfx="${pfx}/"
__gitcomp_file "$(__git_diff_index_files "$1" "$pfx")" "$pfx" "$cur_"
;;
*)
__gitcomp_file "$(__git_diff_index_files "$1")" "" "$cur_"
;;
esac
} }
__git_complete_file () __git_complete_file ()
@ -1213,7 +1136,7 @@ _git_commit ()
esac esac
if git rev-parse --verify --quiet HEAD >/dev/null; then if git rev-parse --verify --quiet HEAD >/dev/null; then
__git_complete_diff_index_file "HEAD" __git_complete_index_file "--committable"
else else
# This is the first commit # This is the first commit
__git_complete_index_file "--cached" __git_complete_index_file "--cached"
@ -2702,14 +2625,6 @@ if [[ -n ${ZSH_VERSION-} ]]; then
compdef _git git gitk compdef _git git gitk
return return
elif [[ -n ${BASH_VERSION-} ]]; then
if ((${BASH_VERSINFO[0]} < 4)); then
# compopt is not supported
__git_index_file_list_filter ()
{
__git_index_file_list_filter_compat
}
fi
fi fi
__git_func_wrap () __git_func_wrap ()

View File

@ -347,4 +347,81 @@ test_expect_success 'send-email' '
test_completion "git send-email ma" "master " test_completion "git send-email ma" "master "
' '
test_expect_success 'complete files' '
git init tmp && cd tmp &&
test_when_finished "cd .. && rm -rf tmp" &&
echo "expected" > .gitignore &&
echo "out" >> .gitignore &&
git add .gitignore &&
test_completion "git commit " ".gitignore" &&
git commit -m ignore &&
touch new &&
test_completion "git add " "new" &&
git add new &&
git commit -a -m new &&
test_completion "git add " "" &&
git mv new modified &&
echo modify > modified &&
test_completion "git add " "modified" &&
touch untracked &&
: TODO .gitignore should not be here &&
test_completion "git rm " <<-\EOF &&
.gitignore
modified
EOF
test_completion "git clean " "untracked" &&
: TODO .gitignore should not be here &&
test_completion "git mv " <<-\EOF &&
.gitignore
modified
EOF
mkdir dir &&
touch dir/file-in-dir &&
git add dir/file-in-dir &&
git commit -m dir &&
mkdir untracked-dir &&
: TODO .gitignore should not be here &&
test_completion "git mv modified " <<-\EOF &&
.gitignore
dir
modified
untracked
untracked-dir
EOF
test_completion "git commit " "modified" &&
: TODO .gitignore should not be here &&
test_completion "git ls-files " <<-\EOF
.gitignore
dir
modified
EOF
touch momified &&
test_completion "git add mom" "momified"
'
test_expect_failure 'complete with tilde expansion' '
git init tmp && cd tmp &&
test_when_finished "cd .. && rm -rf tmp" &&
touch ~/tmp/file &&
test_completion "git add ~/tmp/" "~/tmp/file"
'
test_done test_done