summaryrefslogtreecommitdiff
path: root/contrib/completion
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/completion')
-rw-r--r--contrib/completion/git-completion.bash251
-rw-r--r--contrib/completion/git-completion.tcsh4
-rw-r--r--contrib/completion/git-completion.zsh13
-rw-r--r--contrib/completion/git-prompt.sh431
4 files changed, 428 insertions, 271 deletions
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 6c3bafeea5..482ca84b45 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1,5 +1,3 @@
-#!bash
-#
# bash/zsh completion support for core Git.
#
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
@@ -18,11 +16,17 @@
#
# To use these routines:
#
-# 1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
+# 1) Copy this file to somewhere (e.g. ~/.git-completion.bash).
# 2) Add the following line to your .bashrc/.zshrc:
-# source ~/.git-completion.sh
+# source ~/.git-completion.bash
# 3) Consider changing your PS1 to also show the current branch,
# see git-prompt.sh for details.
+#
+# If you use complex aliases of form '!f() { ... }; f', you can use the null
+# command ':' as the first command in the function body to declare the desired
+# completion style. For example '!f() { : git commit ; ... }; f' will
+# tell the completion to use commit completion. This also works with aliases
+# of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '".
case "$COMP_WORDBREAKS" in
*:*) : great ;;
@@ -33,8 +37,6 @@ esac
# returns location of .git repo
__gitdir ()
{
- # Note: this function is duplicated in git-prompt.sh
- # When updating it, make sure you update the other one to match.
if [ -z "${1-}" ]; then
if [ -n "${__git_dir-}" ]; then
echo "$__git_dir"
@@ -182,9 +184,9 @@ _get_comp_words_by_ref ()
}
fi
-__gitcompadd ()
+__gitcompappend ()
{
- local i=0
+ local x i=${#COMPREPLY[@]}
for x in $1; do
if [[ "$x" == "$3"* ]]; then
COMPREPLY[i++]="$2$x$4"
@@ -192,6 +194,12 @@ __gitcompadd ()
done
}
+__gitcompadd ()
+{
+ COMPREPLY=()
+ __gitcompappend "$@"
+}
+
# Generates completion reply, appending a space to possible completion words,
# if necessary.
# It accepts 1 to 4 arguments:
@@ -222,6 +230,14 @@ __gitcomp ()
esac
}
+# Variation of __gitcomp_nl () that appends to the existing list of
+# completion candidates, COMPREPLY.
+__gitcomp_nl_append ()
+{
+ local IFS=$'\n'
+ __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
+}
+
# Generates completion reply from newline-separated possible completion words
# by appending a space to all of them.
# It accepts 1 to 4 arguments:
@@ -233,8 +249,8 @@ __gitcomp ()
# appended.
__gitcomp_nl ()
{
- local IFS=$'\n'
- __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
+ COMPREPLY=()
+ __gitcomp_nl_append "$@"
}
# Generates completion reply with compgen from newline-separated possible
@@ -265,16 +281,12 @@ __gitcomp_file ()
# argument, and using the options specified in the second argument.
__git_ls_files_helper ()
{
- (
- test -n "${CDPATH+set}" && unset CDPATH
- cd "$1"
- 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
+ if [ "$2" == "--committable" ]; then
+ git -C "$1" diff-index --name-only --relative HEAD
+ else
+ # NOTE: $2 is not quoted in order to support multiple options
+ git -C "$1" ls-files --exclude-standard $2
+ fi 2>/dev/null
}
@@ -372,7 +384,8 @@ __git_refs ()
;;
*)
echo "HEAD"
- git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
+ git for-each-ref --format="%(refname:short)" -- \
+ "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
;;
esac
}
@@ -398,12 +411,9 @@ __git_refs_remotes ()
__git_remotes ()
{
- local i IFS=$'\n' d="$(__gitdir)"
+ local d="$(__gitdir)"
test -d "$d/remotes" && ls -1 "$d/remotes"
- for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
- i="${i#remote.}"
- echo "${i/.url*/}"
- done
+ git --git-dir="$d" remote
}
__git_list_merge_strategies ()
@@ -506,7 +516,7 @@ __git_complete_index_file ()
;;
esac
- __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
+ __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_"
}
__git_complete_file ()
@@ -650,12 +660,13 @@ __git_list_porcelain_commands ()
cat-file) : plumbing;;
check-attr) : plumbing;;
check-ignore) : plumbing;;
+ check-mailmap) : plumbing;;
check-ref-format) : plumbing;;
checkout-index) : plumbing;;
commit-tree) : plumbing;;
count-objects) : infrequent;;
- credential-cache) : credentials helper;;
- credential-store) : credentials helper;;
+ credential) : credentials;;
+ credential-*) : credentials helper;;
cvsexportcommit) : export;;
cvsimport) : import;;
cvsserver) : daemon;;
@@ -674,7 +685,6 @@ __git_list_porcelain_commands ()
index-pack) : plumbing;;
init-db) : deprecated;;
local-fetch) : plumbing;;
- lost-found) : infrequent;;
ls-files) : plumbing;;
ls-remote) : plumbing;;
ls-tree) : plumbing;;
@@ -688,14 +698,12 @@ __git_list_porcelain_commands ()
pack-refs) : plumbing;;
parse-remote) : plumbing;;
patch-id) : plumbing;;
- peek-remote) : plumbing;;
prune) : plumbing;;
prune-packed) : plumbing;;
quiltimport) : import;;
read-tree) : plumbing;;
receive-pack) : plumbing;;
remote-*) : transport;;
- repo-config) : deprecated;;
rerere) : plumbing;;
rev-list) : plumbing;;
rev-parse) : plumbing;;
@@ -708,7 +716,6 @@ __git_list_porcelain_commands ()
ssh-*) : transport;;
stripspace) : plumbing;;
symbolic-ref) : plumbing;;
- tar-tree) : deprecated;;
unpack-file) : plumbing;;
unpack-objects) : plumbing;;
update-index) : plumbing;;
@@ -728,35 +735,28 @@ __git_list_porcelain_commands ()
__git_porcelain_commands=
__git_compute_porcelain_commands ()
{
- __git_compute_all_commands
test -n "$__git_porcelain_commands" ||
__git_porcelain_commands=$(__git_list_porcelain_commands)
}
-__git_pretty_aliases ()
+# Lists all set config variables starting with the given section prefix,
+# with the prefix removed.
+__git_get_config_variables ()
{
- local i IFS=$'\n'
- for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
- case "$i" in
- pretty.*)
- i="${i#pretty.}"
- echo "${i/ */}"
- ;;
- esac
+ local section="$1" i IFS=$'\n'
+ for i in $(git --git-dir="$(__gitdir)" config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
+ echo "${i#$section.}"
done
}
+__git_pretty_aliases ()
+{
+ __git_get_config_variables "pretty"
+}
+
__git_aliases ()
{
- local i IFS=$'\n'
- for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
- case "$i" in
- alias.*)
- i="${i#alias.}"
- echo "${i/ */}"
- ;;
- esac
- done
+ __git_get_config_variables "alias"
}
# __git_aliased_command requires 1 argument
@@ -774,6 +774,10 @@ __git_aliased_command ()
-*) : option ;;
*=*) : setting env ;;
git) : git itself ;;
+ \(\)) : skip parens of shell function definition ;;
+ {) : skip start of shell helper function ;;
+ :) : skip null command ;;
+ \'*) : skip opening quote after sh -c ;;
*)
echo "$word"
return
@@ -902,7 +906,7 @@ _git_add ()
esac
# XXX should we check for --update and --all options ?
- __git_complete_index_file "--others --modified"
+ __git_complete_index_file "--others --modified --directory --no-empty-directory"
}
_git_archive ()
@@ -966,7 +970,7 @@ _git_branch ()
case "$cur" in
--set-upstream-to=*)
- __gitcomp "$(__git_refs)" "" "${cur##--set-upstream-to=}"
+ __gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}"
;;
--*)
__gitcomp "
@@ -1034,7 +1038,7 @@ _git_checkout ()
_git_cherry ()
{
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_cherry_pick ()
@@ -1064,7 +1068,7 @@ _git_clean ()
esac
# XXX should we check for -x option ?
- __git_complete_index_file "--others"
+ __git_complete_index_file "--others --directory"
}
_git_clone ()
@@ -1103,7 +1107,7 @@ _git_commit ()
case "$cur" in
--cleanup=*)
- __gitcomp "default strip verbatim whitespace
+ __gitcomp "default scissors strip verbatim whitespace
" "" "${cur##--cleanup=}"
return
;;
@@ -1158,8 +1162,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--full-index --binary --abbrev --diff-filter=
--find-copies-harder
--text --ignore-space-at-eol --ignore-space-change
- --ignore-all-space --exit-code --quiet --ext-diff
- --no-ext-diff
+ --ignore-all-space --ignore-blank-lines --exit-code
+ --quiet --ext-diff --no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--inter-hunk-context=
--patience --histogram --minimal
@@ -1189,8 +1193,8 @@ _git_diff ()
__git_complete_revlist_file
}
-__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
- tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare
+__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
+ tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
"
_git_difftool ()
@@ -1214,14 +1218,20 @@ _git_difftool ()
__git_complete_revlist_file
}
+__git_fetch_recurse_submodules="yes on-demand no"
+
__git_fetch_options="
--quiet --verbose --append --upload-pack --force --keep --depth=
- --tags --no-tags --all --prune --dry-run
+ --tags --no-tags --all --prune --dry-run --recurse-submodules=
"
_git_fetch ()
{
case "$cur" in
+ --recurse-submodules=*)
+ __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+ return
+ ;;
--*)
__gitcomp "$__git_fetch_options"
return
@@ -1285,7 +1295,7 @@ _git_gitk ()
}
__git_match_ctag() {
- awk "/^${1////\\/}/ { print \$1 }" "$2"
+ awk "/^${1//\//\\/}/ { print \$1 }" "$2"
}
_git_grep ()
@@ -1405,7 +1415,7 @@ __git_log_gitk_options="
# Options that go well for log and shortlog (not gitk)
__git_log_shortlog_options="
--author= --committer= --grep=
- --all-match
+ --all-match --invert-grep
"
__git_log_pretty_formats="oneline short medium full fuller email raw format:"
@@ -1431,7 +1441,7 @@ _git_log ()
return
;;
--decorate=*)
- __gitcomp "long short" "" "${cur##--decorate=}"
+ __gitcomp "full short no" "" "${cur##--decorate=}"
return
;;
--*)
@@ -1444,6 +1454,7 @@ _git_log ()
--abbrev-commit --abbrev=
--relative-date --date=
--pretty= --format= --oneline
+ --show-signature
--cherry-pick
--graph
--decorate --decorate=
@@ -1459,9 +1470,12 @@ _git_log ()
__git_complete_revlist
}
+# Common merge options shared by git-merge(1) and git-pull(1).
__git_merge_options="
--no-commit --no-stat --log --no-log --squash --strategy
--commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
+ --verify-signatures --no-verify-signatures --gpg-sign
+ --quiet --verbose --progress --no-progress
"
_git_merge ()
@@ -1470,7 +1484,8 @@ _git_merge ()
case "$cur" in
--*)
- __gitcomp "$__git_merge_options"
+ __gitcomp "$__git_merge_options
+ --rerere-autoupdate --no-rerere-autoupdate --abort"
return
esac
__gitcomp_nl "$(__git_refs)"
@@ -1492,6 +1507,12 @@ _git_mergetool ()
_git_merge_base ()
{
+ case "$cur" in
+ --*)
+ __gitcomp "--octopus --independent --is-ancestor --fork-point"
+ return
+ ;;
+ esac
__gitcomp_nl "$(__git_refs)"
}
@@ -1570,6 +1591,10 @@ _git_pull ()
__git_complete_strategy && return
case "$cur" in
+ --recurse-submodules=*)
+ __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+ return
+ ;;
--*)
__gitcomp "
--rebase --no-rebase
@@ -1582,22 +1607,55 @@ _git_pull ()
__git_complete_remote_or_refspec
}
+__git_push_recurse_submodules="check on-demand"
+
+__git_complete_force_with_lease ()
+{
+ local cur_=$1
+
+ case "$cur_" in
+ --*=)
+ ;;
+ *:*)
+ __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}"
+ ;;
+ *)
+ __gitcomp_nl "$(__git_refs)" "" "$cur_"
+ ;;
+ esac
+}
+
_git_push ()
{
case "$prev" in
--repo)
__gitcomp_nl "$(__git_remotes)"
return
+ ;;
+ --recurse-submodules)
+ __gitcomp "$__git_push_recurse_submodules"
+ return
+ ;;
esac
case "$cur" in
--repo=*)
__gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
return
;;
+ --recurse-submodules=*)
+ __gitcomp "$__git_push_recurse_submodules" "" "${cur##--recurse-submodules=}"
+ return
+ ;;
+ --force-with-lease=*)
+ __git_complete_force_with_lease "${cur##--force-with-lease=}"
+ return
+ ;;
--*)
__gitcomp "
--all --mirror --tags --dry-run --force --verbose
+ --quiet --prune --delete --follow-tags
--receive-pack= --repo= --set-upstream
+ --force-with-lease --force-with-lease= --recurse-submodules=
"
return
;;
@@ -1608,7 +1666,10 @@ _git_push ()
_git_rebase ()
{
local dir="$(__gitdir)"
- if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
+ if [ -f "$dir"/rebase-merge/interactive ]; then
+ __gitcomp "--continue --skip --abort --edit-todo"
+ return
+ elif [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
__gitcomp "--continue --skip --abort"
return
fi
@@ -1624,7 +1685,8 @@ _git_rebase ()
--preserve-merges --stat --no-stat
--committer-date-is-author-date --ignore-date
--ignore-whitespace --whitespace=
- --autosquash
+ --autosquash --fork-point --no-fork-point
+ --autostash
"
return
@@ -1714,15 +1776,7 @@ __git_config_get_set_variables ()
c=$((--c))
done
- git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
- while read -r line
- do
- case "$line" in
- *.*=*)
- echo "${line/=*/}"
- ;;
- esac
- done
+ git --git-dir="$(__gitdir)" config $config_file --name-only --list 2>/dev/null
}
_git_config ()
@@ -1807,6 +1861,10 @@ _git_config ()
__gitcomp "$__git_send_email_suppresscc_options"
return
;;
+ sendemail.transferencoding)
+ __gitcomp "7bit 8bit quoted-printable base64"
+ return
+ ;;
--get|--get-all|--unset|--unset-all)
__gitcomp_nl "$(__git_config_get_set_variables)"
return
@@ -1823,6 +1881,7 @@ _git_config ()
--get --get-all --get-regexp
--add --unset --unset-all
--remove-section --rename-section
+ --name-only
"
return
;;
@@ -1834,6 +1893,7 @@ _git_config ()
branch.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+ __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
return
;;
guitool.*.*)
@@ -1876,6 +1936,7 @@ _git_config ()
remote.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+ __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
return
;;
url.*.*)
@@ -1939,6 +2000,7 @@ _git_config ()
color.status.changed
color.status.header
color.status.nobranch
+ color.status.unmerged
color.status.untracked
color.status.updated
color.ui
@@ -1959,7 +2021,6 @@ _git_config ()
core.fileMode
core.fsyncobjectfiles
core.gitProxy
- core.ignoreCygwinFSTricks
core.ignoreStat
core.ignorecase
core.logAllRefUpdates
@@ -1999,6 +2060,7 @@ _git_config ()
fetch.unpackLimit
format.attach
format.cc
+ format.coverLetter
format.headers
format.numbered
format.pretty
@@ -2050,6 +2112,8 @@ _git_config ()
http.noEPSV
http.postBuffer
http.proxy
+ http.sslCipherList
+ http.sslVersion
http.sslCAInfo
http.sslCAPath
http.sslCert
@@ -2113,6 +2177,7 @@ _git_config ()
pull.octopus
pull.twohead
push.default
+ push.followTags
rebase.autosquash
rebase.stat
receive.autogc
@@ -2186,12 +2251,7 @@ _git_remote ()
__git_complete_remote_or_refspec
;;
update)
- local i c='' IFS=$'\n'
- for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
- i="${i#remotes.}"
- c="$c ${i/ */}"
- done
- __gitcomp "$c"
+ __gitcomp "$(__git_get_config_variables "remotes")"
;;
*)
;;
@@ -2218,6 +2278,11 @@ _git_reset ()
_git_revert ()
{
+ local dir="$(__gitdir)"
+ if [ -f "$dir"/REVERT_HEAD ]; then
+ __gitcomp "--continue --quit --abort"
+ return
+ fi
case "$cur" in
--*)
__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
@@ -2272,6 +2337,7 @@ _git_show ()
;;
--*)
__gitcomp "--pretty= --format= --abbrev-commit --oneline
+ --show-signature
$__git_diff_common_options
"
return
@@ -2477,6 +2543,16 @@ _git_tag ()
__gitcomp_nl "$(__git_refs)"
;;
esac
+
+ case "$cur" in
+ --*)
+ __gitcomp "
+ --list --delete --verify --annotate --message --file
+ --sign --cleanup --local-user --force --column --sort
+ --contains --points-at
+ "
+ ;;
+ esac
}
_git_whatchanged ()
@@ -2492,9 +2568,10 @@ __git_main ()
i="${words[c]}"
case "$i" in
--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+ --git-dir) ((c++)) ; __git_dir="${words[c]}" ;;
--bare) __git_dir="." ;;
--help) command="help"; break ;;
- -c) c=$((++c)) ;;
+ -c|--work-tree|--namespace) ((c++)) ;;
-*) ;;
*) command="$i"; break ;;
esac
@@ -2512,6 +2589,7 @@ __git_main ()
--exec-path
--exec-path=
--html-path
+ --man-path
--info-path
--work-tree=
--namespace=
@@ -2530,6 +2608,7 @@ __git_main ()
local expansion=$(__git_aliased_command "$command")
if [ -n "$expansion" ]; then
+ words[1]=$expansion
completion_func="_git_${expansion//-/_}"
declare -f $completion_func >/dev/null && $completion_func
fi
@@ -2580,7 +2659,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then
--*=*|*.) ;;
*) c="$c " ;;
esac
- array+=("$c")
+ array[${#array[@]}+1]="$c"
done
compset -P '*[=:]'
compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh
index eaacaf0c3e..4a790d8f4e 100644
--- a/contrib/completion/git-completion.tcsh
+++ b/contrib/completion/git-completion.tcsh
@@ -1,5 +1,3 @@
-#!tcsh
-#
# tcsh completion support for core Git.
#
# Copyright (C) 2012 Marc Khouzam <marc.khouzam@gmail.com>
@@ -43,7 +41,7 @@ if ( ! -e ${__git_tcsh_completion_original_script} ) then
exit
endif
-cat << EOF > ${__git_tcsh_completion_script}
+cat << EOF >! ${__git_tcsh_completion_script}
#!bash
#
# This script is GENERATED and will be overwritten automatically.
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index fac5e711eb..e25541308a 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -9,7 +9,7 @@
#
# If your script is somewhere else, you can configure it on your ~/.zshrc:
#
-# zstyle ':completion:*:*:git:*' script ~/.git-completion.sh
+# zstyle ':completion:*:*:git:*' script ~/.git-completion.zsh
#
# The recommended way to install this script is to copy to '~/.zsh/_git', and
# then add the following to your ~/.zshrc file:
@@ -30,10 +30,10 @@ if [ -z "$script" ]; then
local -a locations
local e
locations=(
+ $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
'/etc/bash_completion.d/git' # fedora, old debian
'/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
'/usr/share/bash-completion/git' # gentoo
- $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
)
for e in $locations; do
test -f $e && script="$e" && break
@@ -76,6 +76,14 @@ __gitcomp_nl ()
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
}
+__gitcomp_nl_append ()
+{
+ emulate -L zsh
+
+ local IFS=$'\n'
+ compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
+}
+
__gitcomp_file ()
{
emulate -L zsh
@@ -96,6 +104,7 @@ __git_zsh_bash_func ()
local expansion=$(__git_aliased_command "$command")
if [ -n "$expansion" ]; then
+ words[1]=$expansion
completion_func="_git_${expansion//-/_}"
declare -f $completion_func >/dev/null && $completion_func
fi
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 07a6218d10..07b52bedf1 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -3,7 +3,7 @@
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
# Distributed under the GNU General Public License, version 2.0.
#
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
#
# To enable:
#
@@ -13,24 +13,27 @@
# 3a) Change your PS1 to call __git_ps1 as
# command-substitution:
# Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-# ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+# ZSH: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
# the optional argument will be used as format string.
-# 3b) Alternatively, if you are using bash, __git_ps1 can be
-# used for PROMPT_COMMAND with two parameters, <pre> and
-# <post>, which are strings you would put in $PS1 before
-# and after the status string generated by the git-prompt
-# machinery. e.g.
+# 3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+# be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+# with two parameters, <pre> and <post>, which are strings
+# you would put in $PS1 before and after the status string
+# generated by the git-prompt machinery. e.g.
# Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+# will show username, at-sign, host, colon, cwd, then
+# various status string, followed by dollar and SP, as
+# your prompt.
# ZSH: precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
-# will show username, at-sign, host, colon, cwd, then
-# various status string, followed by dollar and SP, as
-# your prompt.
+# will show username, pipe, then various status string,
+# followed by colon, cwd, dollar and SP, as your prompt.
# Optionally, you can supply a third argument with a printf
# format string to finetune the output of the branch status
#
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository. The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
#
# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
# unstaged (*) and staged (+) changes will be shown next to the branch
@@ -57,11 +60,16 @@
# of values:
#
# verbose show number of commits ahead/behind (+/-) upstream
+# name if verbose, then also show the upstream abbrev name
# legacy don't use the '--count' option available in recent
# versions of git-rev-list
# git always compare HEAD to @{upstream}
# svn always compare HEAD to your SVN upstream
#
+# You can change the separator between the branch name and the above
+# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator
+# is SP.
+#
# By default, __git_ps1 will compare HEAD to your SVN upstream if it can
# find one, or @{upstream} otherwise. Once you have set
# GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
@@ -78,31 +86,17 @@
#
# If you would like a colored hint about the current dirty state, set
# GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
+#
+# If you would like __git_ps1 to do nothing in the case when the current
+# directory is set up to be ignored by git, then set
+# GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the
+# repository level by setting bash.hideIfPwdIgnored to "false".
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
- # Note: this function is duplicated in git-completion.bash
- # When updating it, make sure you update the other one to match.
- if [ -z "${1-}" ]; then
- if [ -n "${__git_dir-}" ]; then
- echo "$__git_dir"
- elif [ -n "${GIT_DIR-}" ]; then
- test -d "${GIT_DIR-}" || return 1
- echo "$GIT_DIR"
- elif [ -d .git ]; then
- echo .git
- else
- git rev-parse --git-dir 2>/dev/null
- fi
- elif [ -d "$1/.git" ]; then
- echo "$1/.git"
- else
- echo "$1"
- fi
-}
+# check whether printf supports -v
+__git_printf_supports_v=
+printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
# stores the divergence from upstream in $p
# used by GIT_PS1_SHOWUPSTREAM
@@ -110,7 +104,7 @@ __git_ps1_show_upstream ()
{
local key value
local svn_remote svn_url_pattern count n
- local upstream=git legacy="" verbose=""
+ local upstream=git legacy="" verbose="" name=""
svn_remote=()
# get some config options from git-config
@@ -126,7 +120,7 @@ __git_ps1_show_upstream ()
;;
svn-remote.*.url)
svn_remote[$((${#svn_remote[@]} + 1))]="$value"
- svn_url_pattern+="\\|$value"
+ svn_url_pattern="$svn_url_pattern\\|$value"
upstream=svn+git # default upstream is SVN if available, else git
;;
esac
@@ -138,6 +132,7 @@ __git_ps1_show_upstream ()
git|svn) upstream="$option" ;;
verbose) verbose=1 ;;
legacy) legacy=1 ;;
+ name) name=1 ;;
esac
done
@@ -220,13 +215,25 @@ __git_ps1_show_upstream ()
*) # diverged from upstream
p=" u+${count#* }-${count% *}" ;;
esac
+ if [[ -n "$count" && -n "$name" ]]; then
+ __git_ps1_upstream_name=$(git rev-parse \
+ --abbrev-ref "$upstream" 2>/dev/null)
+ if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+ p="$p \${__git_ps1_upstream_name}"
+ else
+ p="$p ${__git_ps1_upstream_name}"
+ # not needed anymore; keep user's
+ # environment clean
+ unset __git_ps1_upstream_name
+ fi
+ fi
fi
}
# Helper function that is meant to be called from __git_ps1. It
-# builds up a gitstring injecting color codes into the appropriate
-# places.
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
__git_ps1_colorize_gitstring ()
{
if [[ -n ${ZSH_VERSION-} ]]; then
@@ -234,74 +241,47 @@ __git_ps1_colorize_gitstring ()
local c_green='%F{green}'
local c_lblue='%F{blue}'
local c_clear='%f'
- local bad_color=$c_red
- local ok_color=$c_green
- local branch_color="$c_clear"
- local flags_color="$c_lblue"
- local branchstring="$c${b##refs/heads/}"
-
- if [ $detached = no ]; then
- branch_color="$ok_color"
- else
- branch_color="$bad_color"
- fi
-
- gitstring="$branch_color$branchstring$c_clear"
-
- if [ -n "$w$i$s$u$r$p" ]; then
- gitstring="$gitstring$z"
- fi
- if [ "$w" = "*" ]; then
- gitstring="$gitstring$bad_color$w"
- fi
- if [ -n "$i" ]; then
- gitstring="$gitstring$ok_color$i"
- fi
- if [ -n "$s" ]; then
- gitstring="$gitstring$flags_color$s"
- fi
- if [ -n "$u" ]; then
- gitstring="$gitstring$bad_color$u"
- fi
- gitstring="$gitstring$c_clear$r$p"
- return
+ else
+ # Using \[ and \] around colors is necessary to prevent
+ # issues with command line editing/browsing/completion!
+ local c_red='\[\e[31m\]'
+ local c_green='\[\e[32m\]'
+ local c_lblue='\[\e[1;34m\]'
+ local c_clear='\[\e[0m\]'
fi
- local c_red='\e[31m'
- local c_green='\e[32m'
- local c_lblue='\e[1;34m'
- local c_clear='\e[0m'
local bad_color=$c_red
local ok_color=$c_green
- local branch_color="$c_clear"
local flags_color="$c_lblue"
- local branchstring="$c${b##refs/heads/}"
+ local branch_color=""
if [ $detached = no ]; then
branch_color="$ok_color"
else
branch_color="$bad_color"
fi
+ c="$branch_color$c"
- # Setting gitstring directly with \[ and \] around colors
- # is necessary to prevent wrapping issues!
- gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
-
- if [ -n "$w$i$s$u$r$p" ]; then
- gitstring="$gitstring$z"
- fi
+ z="$c_clear$z"
if [ "$w" = "*" ]; then
- gitstring="$gitstring\[$bad_color\]$w"
+ w="$bad_color$w"
fi
if [ -n "$i" ]; then
- gitstring="$gitstring\[$ok_color\]$i"
+ i="$ok_color$i"
fi
if [ -n "$s" ]; then
- gitstring="$gitstring\[$flags_color\]$s"
+ s="$flags_color$s"
fi
if [ -n "$u" ]; then
- gitstring="$gitstring\[$bad_color\]$u"
+ u="$bad_color$u"
fi
- gitstring="$gitstring\[$c_clear\]$r$p"
+ r="$c_clear$r"
+}
+
+__git_eread ()
+{
+ local f="$1"
+ shift
+ test -r "$f" && read "$@" <"$f"
}
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
@@ -317,6 +297,8 @@ __git_ps1_colorize_gitstring ()
# In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
__git_ps1 ()
{
+ # preserve exit status
+ local exit=$?
local pcmode=no
local detached=no
local ps1pc_start='\u@\h:\w '
@@ -328,57 +310,132 @@ __git_ps1 ()
ps1pc_start="$1"
ps1pc_end="$2"
printf_format="${3:-$printf_format}"
+ # set PS1 to a plain prompt so that we can
+ # simply return early if the prompt should not
+ # be decorated
+ PS1="$ps1pc_start$ps1pc_end"
;;
0|1) printf_format="${1:-$printf_format}"
;;
- *) return
+ *) return $exit
;;
esac
- local g="$(__gitdir)"
- if [ -z "$g" ]; then
- if [ $pcmode = yes ]; then
- #In PC mode PS1 always needs to be set
- PS1="$ps1pc_start$ps1pc_end"
+ # ps1_expanded: This variable is set to 'yes' if the shell
+ # subjects the value of PS1 to parameter expansion:
+ #
+ # * bash does unless the promptvars option is disabled
+ # * zsh does not unless the PROMPT_SUBST option is set
+ # * POSIX shells always do
+ #
+ # If the shell would expand the contents of PS1 when drawing
+ # the prompt, a raw ref name must not be included in PS1.
+ # This protects the user from arbitrary code execution via
+ # specially crafted ref names. For example, a ref named
+ # 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the
+ # shell to execute 'sudo rm -rf /' when the prompt is drawn.
+ #
+ # Instead, the ref name should be placed in a separate global
+ # variable (in the __git_ps1_* namespace to avoid colliding
+ # with the user's environment) and that variable should be
+ # referenced from PS1. For example:
+ #
+ # __git_ps1_foo=$(do_something_to_get_ref_name)
+ # PS1="...stuff...\${__git_ps1_foo}...stuff..."
+ #
+ # If the shell does not expand the contents of PS1, the raw
+ # ref name must be included in PS1.
+ #
+ # The value of this variable is only relevant when in pcmode.
+ #
+ # Assume that the shell follows the POSIX specification and
+ # expands PS1 unless determined otherwise. (This is more
+ # likely to be correct if the user has a non-bash, non-zsh
+ # shell and safer than the alternative if the assumption is
+ # incorrect.)
+ #
+ local ps1_expanded=yes
+ [ -z "$ZSH_VERSION" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no
+ [ -z "$BASH_VERSION" ] || shopt -q promptvars || ps1_expanded=no
+
+ local repo_info rev_parse_exit_code
+ repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+ --is-bare-repository --is-inside-work-tree \
+ --short HEAD 2>/dev/null)"
+ rev_parse_exit_code="$?"
+
+ if [ -z "$repo_info" ]; then
+ return $exit
+ fi
+
+ local short_sha
+ if [ "$rev_parse_exit_code" = "0" ]; then
+ short_sha="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ fi
+ local inside_worktree="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ local bare_repo="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ local inside_gitdir="${repo_info##*$'\n'}"
+ local g="${repo_info%$'\n'*}"
+
+ if [ "true" = "$inside_worktree" ] &&
+ [ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] &&
+ [ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] &&
+ git check-ignore -q .
+ then
+ return $exit
+ fi
+
+ local r=""
+ local b=""
+ local step=""
+ local total=""
+ if [ -d "$g/rebase-merge" ]; then
+ __git_eread "$g/rebase-merge/head-name" b
+ __git_eread "$g/rebase-merge/msgnum" step
+ __git_eread "$g/rebase-merge/end" total
+ if [ -f "$g/rebase-merge/interactive" ]; then
+ r="|REBASE-i"
+ else
+ r="|REBASE-m"
fi
else
- local r=""
- local b=""
- local step=""
- local total=""
- if [ -d "$g/rebase-merge" ]; then
- b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)"
- step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null)
- total=$(cat "$g/rebase-merge/end" 2>/dev/null)
- if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
+ if [ -d "$g/rebase-apply" ]; then
+ __git_eread "$g/rebase-apply/next" step
+ __git_eread "$g/rebase-apply/last" total
+ if [ -f "$g/rebase-apply/rebasing" ]; then
+ __git_eread "$g/rebase-apply/head-name" b
+ r="|REBASE"
+ elif [ -f "$g/rebase-apply/applying" ]; then
+ r="|AM"
else
- r="|REBASE-m"
+ r="|AM/REBASE"
fi
+ elif [ -f "$g/MERGE_HEAD" ]; then
+ r="|MERGING"
+ elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+ r="|CHERRY-PICKING"
+ elif [ -f "$g/REVERT_HEAD" ]; then
+ r="|REVERTING"
+ elif [ -f "$g/BISECT_LOG" ]; then
+ r="|BISECTING"
+ fi
+
+ if [ -n "$b" ]; then
+ :
+ elif [ -h "$g/HEAD" ]; then
+ # symlink symbolic ref
+ b="$(git symbolic-ref HEAD 2>/dev/null)"
else
- if [ -d "$g/rebase-apply" ]; then
- step=$(cat "$g/rebase-apply/next" 2>/dev/null)
- total=$(cat "$g/rebase-apply/last" 2>/dev/null)
- if [ -f "$g/rebase-apply/rebasing" ]; then
- b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)"
- r="|REBASE"
- elif [ -f "$g/rebase-apply/applying" ]; then
- r="|AM"
- else
- r="|AM/REBASE"
- fi
- elif [ -f "$g/MERGE_HEAD" ]; then
- r="|MERGING"
- elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
- r="|CHERRY-PICKING"
- elif [ -f "$g/REVERT_HEAD" ]; then
- r="|REVERTING"
- elif [ -f "$g/BISECT_LOG" ]; then
- r="|BISECTING"
+ local head=""
+ if ! __git_eread "$g/HEAD" head; then
+ return $exit
fi
-
- test -n "$b" ||
- b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+ # is it a symbolic ref?
+ b="${head#ref: }"
+ if [ "$head" = "$b" ]; then
detached=yes
b="$(
case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -392,70 +449,84 @@ __git_ps1 ()
git describe --tags --exact-match HEAD ;;
esac 2>/dev/null)" ||
- b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
- b="unknown"
+ b="$short_sha..."
b="($b)"
- }
+ fi
fi
+ fi
- if [ -n "$step" ] && [ -n "$total" ]; then
- r="$r $step/$total"
- fi
+ if [ -n "$step" ] && [ -n "$total" ]; then
+ r="$r $step/$total"
+ fi
- local w=""
- local i=""
- local s=""
- local u=""
- local c=""
- local p=""
+ local w=""
+ local i=""
+ local s=""
+ local u=""
+ local c=""
+ local p=""
- if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
- if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
- c="BARE:"
+ if [ "true" = "$inside_gitdir" ]; then
+ if [ "true" = "$bare_repo" ]; then
+ c="BARE:"
+ else
+ b="GIT_DIR!"
+ fi
+ elif [ "true" = "$inside_worktree" ]; then
+ if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
+ [ "$(git config --bool bash.showDirtyState)" != "false" ]
+ then
+ git diff --no-ext-diff --quiet --exit-code || w="*"
+ if [ -n "$short_sha" ]; then
+ git diff-index --cached --quiet HEAD -- || i="+"
else
- b="GIT_DIR!"
- fi
- elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
- if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
- [ "$(git config --bool bash.showDirtyState)" != "false" ]
- then
- git diff --no-ext-diff --quiet --exit-code || w="*"
- if git rev-parse --quiet --verify HEAD >/dev/null; then
- git diff-index --cached --quiet HEAD -- || i="+"
- else
- i="#"
- fi
- fi
- if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
- git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+ i="#"
fi
+ fi
+ if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+ git rev-parse --verify --quiet refs/stash >/dev/null
+ then
+ s="$"
+ fi
- if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
- [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
- [ -n "$(git ls-files --others --exclude-standard)" ]
- then
- u="%${ZSH_VERSION+%}"
- fi
+ if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+ [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+ git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null
+ then
+ u="%${ZSH_VERSION+%}"
+ fi
- if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
- __git_ps1_show_upstream
- fi
+ if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+ __git_ps1_show_upstream
fi
+ fi
- local z="${GIT_PS1_STATESEPARATOR-" "}"
- local f="$w$i$s$u"
- if [ $pcmode = yes ]; then
- local gitstring=
- if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
- __git_ps1_colorize_gitstring
- else
- gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
- fi
+ local z="${GIT_PS1_STATESEPARATOR-" "}"
+
+ # NO color option unless in PROMPT_COMMAND mode
+ if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+ __git_ps1_colorize_gitstring
+ fi
+
+ b=${b##refs/heads/}
+ if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+ __git_ps1_branch_name=$b
+ b="\${__git_ps1_branch_name}"
+ fi
+
+ local f="$w$i$s$u"
+ local gitstring="$c$b${f:+$z$f}$r$p"
+
+ if [ $pcmode = yes ]; then
+ if [ "${__git_printf_supports_v-}" != yes ]; then
gitstring=$(printf -- "$printf_format" "$gitstring")
- PS1="$ps1pc_start$gitstring$ps1pc_end"
else
- # NO color option unless in PROMPT_COMMAND mode
- printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p"
+ printf -v gitstring -- "$printf_format" "$gitstring"
fi
+ PS1="$ps1pc_start$gitstring$ps1pc_end"
+ else
+ printf -- "$printf_format" "$gitstring"
fi
+
+ return $exit
}