@ -13,6 +13,7 @@
# *) .git/remotes file names
# *) .git/remotes file names
# *) git 'subcommands'
# *) git 'subcommands'
# *) tree paths within 'ref:path/to/file' expressions
# *) tree paths within 'ref:path/to/file' expressions
# *) file paths within current working directory and index
# *) common --long-options
# *) common --long-options
#
#
# To use these routines:
# To use these routines:
@ -233,6 +234,118 @@ __gitcomp_nl ()
COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
}
}
# Generates completion reply with compgen from newline-separated possible
# completion filenames.
# It accepts 1 to 3 arguments:
# 1: List of possible completion filenames, separated by a single newline.
# 2: A directory prefix to be added to each possible completion filename
# (optional).
# 3: Generate possible completion matches for this word (optional).
__gitcomp_file ()
{
local IFS=$'\n'
# XXX does not work when the directory prefix contains a tilde,
# since tilde expansion is not applied.
# This means that COMPREPLY will be empty and Bash default
# completion will be used.
COMPREPLY=($(compgen -P "${2-}" -W "$1" -- "${3-$cur}"))
# Tell Bash that compspec generates filenames.
compopt -o filenames 2>/dev/null
}
__git_index_file_list_filter_compat ()
{
local path
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 ()
{
# NOTE: $2 is not quoted in order to support multiple options
cd "$1" && git ls-files --exclude-standard $2
} 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 ()
{
cd "$1" && git diff-index --name-only --relative "$2"
} 2>/dev/null
# __git_index_files accepts 1 or 2 arguments:
# 1: Options to pass to ls-files (required).
# Supported options are --cached, --modified, --deleted, --others,
# and --directory.
# 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_index_files ()
{
local dir="$(__gitdir)" root="${2-.}"
if [ -d "$dir" ]; then
__git_ls_files_helper "$root" "$1" | __git_index_file_list_filter |
sort | uniq
fi
}
# __git_diff_index_files accepts 1 or 2 arguments:
# 1) The id of a tree object.
# 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
}
__git_heads ()
__git_heads ()
{
{
local dir="$(__gitdir)"
local dir="$(__gitdir)"
@ -430,6 +543,46 @@ __git_complete_revlist_file ()
}
}
# __git_complete_index_file requires 1 argument: the options to pass to
# ls-file
__git_complete_index_file ()
{
local pfx cur_="$cur"
case "$cur_" in
?*/*)
pfx="${cur_%/*}"
cur_="${cur_##*/}"
pfx="${pfx}/"
__gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
;;
*)
__gitcomp_file "$(__git_index_files "$1")" "" "$cur_"
;;
esac
}
# __git_complete_diff_index_file requires 1 argument: the id of a tree
# 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 ()
{
{
__git_complete_revlist_file
__git_complete_revlist_file
@ -722,6 +875,43 @@ __git_has_doubledash ()
return 1
return 1
}
}
# Try to count non option arguments passed on the command line for the
# specified git command.
# When options are used, it is necessary to use the special -- option to
# tell the implementation were non option arguments begin.
# XXX this can not be improved, since options can appear everywhere, as
# an example:
# git mv x -n y
#
# __git_count_arguments requires 1 argument: the git command executed.
__git_count_arguments ()
{
local word i c=0
# Skip "git" (first argument)
for ((i=1; i < ${#words[@]}; i++)); do
word="${words[i]}"
case "$word" in
--)
# Good; we can assume that the following are only non
# option arguments.
((c = 0))
;;
"$1")
# Skip the specified git command and discard git
# main options
((c = 0))
;;
?*)
((c++))
;;
esac
done
printf "%d" $c
}
__git_whitespacelist="nowarn warn error error-all fix"
__git_whitespacelist="nowarn warn error error-all fix"
_git_am ()
_git_am ()
@ -770,8 +960,6 @@ _git_apply ()
_git_add ()
_git_add ()
{
{
__git_has_doubledash && return
case "$cur" in
case "$cur" in
--*)
--*)
__gitcomp "
__gitcomp "
@ -780,7 +968,9 @@ _git_add ()
"
"
return
return
esac
esac
COMPREPLY=()
# XXX should we check for --update and --all options ?
__git_complete_index_file "--others --modified"
}
}
_git_archive ()
_git_archive ()
@ -930,15 +1120,15 @@ _git_cherry_pick ()
_git_clean ()
_git_clean ()
{
{
__git_has_doubledash && return
case "$cur" in
case "$cur" in
--*)
--*)
__gitcomp "--dry-run --quiet"
__gitcomp "--dry-run --quiet"
return
return
;;
;;
esac
esac
COMPREPLY=()
# XXX should we check for -x option ?
__git_complete_index_file "--others"
}
}
_git_clone ()
_git_clone ()
@ -969,7 +1159,12 @@ _git_clone ()
_git_commit ()
_git_commit ()
{
{
__git_has_doubledash && return
case "$prev" in
-c|-C)
__gitcomp_nl "$(__git_refs)" "" "${cur}"
return
;;
esac
case "$cur" in
case "$cur" in
--cleanup=*)
--cleanup=*)
@ -998,7 +1193,13 @@ _git_commit ()
"
"
return
return
esac
esac
COMPREPLY=()
if git rev-parse --verify --quiet HEAD >/dev/null; then
__git_complete_diff_index_file "HEAD"
else
# This is the first commit
__git_complete_index_file "--cached"
fi
}
}
_git_describe ()
_git_describe ()
@ -1216,8 +1417,6 @@ _git_init ()
_git_ls_files ()
_git_ls_files ()
{
{
__git_has_doubledash && return
case "$cur" in
case "$cur" in
--*)
--*)
__gitcomp "--cached --deleted --modified --others --ignored
__gitcomp "--cached --deleted --modified --others --ignored
@ -1230,7 +1429,10 @@ _git_ls_files ()
return
return
;;
;;
esac
esac
COMPREPLY=()
# XXX ignore options like --modified and always suggest all cached
# files.
__git_complete_index_file "--cached"
}
}
_git_ls_remote ()
_git_ls_remote ()
@ -1362,7 +1564,14 @@ _git_mv ()
return
return
;;
;;
esac
esac
COMPREPLY=()
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"
else
__git_complete_index_file "--cached"
fi
}
}
_git_name_rev ()
_git_name_rev ()
@ -2068,15 +2277,14 @@ _git_revert ()
_git_rm ()
_git_rm ()
{
{
__git_has_doubledash && return
case "$cur" in
case "$cur" in
--*)
--*)
__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
return
return
;;
;;
esac
esac
COMPREPLY=()
__git_complete_index_file "--cached"
}
}
_git_shortlog ()
_git_shortlog ()
@ -2441,6 +2649,15 @@ if [[ -n ${ZSH_VERSION-} ]]; then
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
}
}
__gitcomp_file ()
{
emulate -L zsh
local IFS=$'\n'
compset -P '*[=:]'
compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
}
__git_zsh_helper ()
__git_zsh_helper ()
{
{
emulate -L ksh
emulate -L ksh
@ -2462,6 +2679,14 @@ 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 ()