浏览代码

Merge pull request #2059 from felipec/fc/gitfast

gitfast: update to upstream v1.8.4
Robby Russell 11 年之前
父节点
当前提交
0e31bed555
共有 3 个文件被更改,包括 435 次插入363 次删除
  1. 146 17
      plugins/gitfast/_git
  2. 99 202
      plugins/gitfast/git-completion.bash
  3. 190 144
      plugins/gitfast/git-prompt.sh

+ 146 - 17
plugins/gitfast/_git

@@ -2,18 +2,19 @@
 
 
 # zsh completion wrapper for git
 # zsh completion wrapper for git
 #
 #
-# You need git's bash completion script installed somewhere, by default on the
-# same directory as this script.
+# Copyright (c) 2012-2013 Felipe Contreras <felipe.contreras@gmail.com>
 #
 #
-# If your script is on ~/.git-completion.sh instead, you can configure it on
-# your ~/.zshrc:
+# You need git's bash completion script installed somewhere, by default it
+# would be the location bash-completion uses.
+#
+# 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.sh
 #
 #
-# The recommended way to install this script is to copy to
-# '~/.zsh/completion/_git', and then add the following to your ~/.zshrc file:
+# The recommended way to install this script is to copy to '~/.zsh/_git', and
+# then add the following to your ~/.zshrc file:
 #
 #
-#  fpath=(~/.zsh/completion $fpath)
+#  fpath=(~/.zsh $fpath)
 
 
 complete ()
 complete ()
 {
 {
@@ -21,8 +22,23 @@ complete ()
 	return 0
 	return 0
 }
 }
 
 
+zstyle -T ':completion:*:*:git:*' tag-order && \
+	zstyle ':completion:*:*:git:*' tag-order 'common-commands'
+
 zstyle -s ":completion:*:*:git:*" script script
 zstyle -s ":completion:*:*:git:*" script script
-test -z "$script" && script="$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash
+if [ -z "$script" ]; then
+	local -a locations
+	local e
+	locations=(
+		'/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
+	done
+fi
 ZSH_VERSION='' . "$script"
 ZSH_VERSION='' . "$script"
 
 
 __gitcomp ()
 __gitcomp ()
@@ -69,18 +85,131 @@ __gitcomp_file ()
 	compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 	compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 }
 }
 
 
+__git_zsh_bash_func ()
+{
+	emulate -L ksh
+
+	local command=$1
+
+	local completion_func="_git_${command//-/_}"
+	declare -f $completion_func >/dev/null && $completion_func && return
+
+	local expansion=$(__git_aliased_command "$command")
+	if [ -n "$expansion" ]; then
+		completion_func="_git_${expansion//-/_}"
+		declare -f $completion_func >/dev/null && $completion_func
+	fi
+}
+
+__git_zsh_cmd_common ()
+{
+	local -a list
+	list=(
+	add:'add file contents to the index'
+	bisect:'find by binary search the change that introduced a bug'
+	branch:'list, create, or delete branches'
+	checkout:'checkout a branch or paths to the working tree'
+	clone:'clone a repository into a new directory'
+	commit:'record changes to the repository'
+	diff:'show changes between commits, commit and working tree, etc'
+	fetch:'download objects and refs from another repository'
+	grep:'print lines matching a pattern'
+	init:'create an empty Git repository or reinitialize an existing one'
+	log:'show commit logs'
+	merge:'join two or more development histories together'
+	mv:'move or rename a file, a directory, or a symlink'
+	pull:'fetch from and merge with another repository or a local branch'
+	push:'update remote refs along with associated objects'
+	rebase:'forward-port local commits to the updated upstream head'
+	reset:'reset current HEAD to the specified state'
+	rm:'remove files from the working tree and from the index'
+	show:'show various types of objects'
+	status:'show the working tree status'
+	tag:'create, list, delete or verify a tag object signed with GPG')
+	_describe -t common-commands 'common commands' list && _ret=0
+}
+
+__git_zsh_cmd_alias ()
+{
+	local -a list
+	list=(${${${(0)"$(git config -z --get-regexp '^alias\.')"}#alias.}%$'\n'*})
+	_describe -t alias-commands 'aliases' list $* && _ret=0
+}
+
+__git_zsh_cmd_all ()
+{
+	local -a list
+	emulate ksh -c __git_compute_all_commands
+	list=( ${=__git_all_commands} )
+	_describe -t all-commands 'all commands' list && _ret=0
+}
+
+__git_zsh_main ()
+{
+	local curcontext="$curcontext" state state_descr line
+	typeset -A opt_args
+	local -a orig_words
+
+	orig_words=( ${words[@]} )
+
+	_arguments -C \
+		'(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
+		'(-p --paginate)--no-pager[do not pipe git output into a pager]' \
+		'--git-dir=-[set the path to the repository]: :_directories' \
+		'--bare[treat the repository as a bare repository]' \
+		'(- :)--version[prints the git suite version]' \
+		'--exec-path=-[path to where your core git programs are installed]:: :_directories' \
+		'--html-path[print the path where git''s HTML documentation is installed]' \
+		'--info-path[print the path where the Info files are installed]' \
+		'--man-path[print the manpath (see `man(1)`) for the man pages]' \
+		'--work-tree=-[set the path to the working tree]: :_directories' \
+		'--namespace=-[set the git namespace]' \
+		'--no-replace-objects[do not use replacement refs to replace git objects]' \
+		'(- :)--help[prints the synopsis and a list of the most commonly used commands]: :->arg' \
+		'(-): :->command' \
+		'(-)*:: :->arg' && return
+
+	case $state in
+	(command)
+		_alternative \
+                         'alias-commands:alias:__git_zsh_cmd_alias' \
+                         'common-commands:common:__git_zsh_cmd_common' \
+                         'all-commands:all:__git_zsh_cmd_all' && _ret=0
+		;;
+	(arg)
+		local command="${words[1]}" __git_dir
+
+		if (( $+opt_args[--bare] )); then
+			__git_dir='.'
+		else
+			__git_dir=${opt_args[--git-dir]}
+		fi
+
+		(( $+opt_args[--help] )) && command='help'
+
+		words=( ${orig_words[@]} )
+
+		__git_zsh_bash_func $command
+		;;
+	esac
+}
+
 _git ()
 _git ()
 {
 {
 	local _ret=1
 	local _ret=1
-	() {
-		emulate -L ksh
-		local cur cword prev
-		cur=${words[CURRENT-1]}
-		prev=${words[CURRENT-2]}
-		let cword=CURRENT-1
-		__${service}_main
-	}
-	let _ret && _default -S '' && _ret=0
+	local cur cword prev
+
+	cur=${words[CURRENT]}
+	prev=${words[CURRENT-1]}
+	let cword=CURRENT-1
+
+	if (( $+functions[__${service}_zsh_main] )); then
+		__${service}_zsh_main
+	else
+		emulate ksh -c __${service}_main
+	fi
+
+	let _ret && _default && _ret=0
 	return _ret
 	return _ret
 }
 }
 
 

+ 99 - 202
plugins/gitfast/git-completion.bash

@@ -33,8 +33,6 @@ esac
 # returns location of .git repo
 # returns location of .git repo
 __gitdir ()
 __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 [ -z "${1-}" ]; then
 		if [ -n "${__git_dir-}" ]; then
 		if [ -n "${__git_dir-}" ]; then
 			echo "$__git_dir"
 			echo "$__git_dir"
@@ -53,19 +51,6 @@ __gitdir ()
 	fi
 	fi
 }
 }
 
 
-__gitcomp_1 ()
-{
-	local c IFS=$' \t\n'
-	for c in $1; do
-		c="$c$2"
-		case $c in
-		--*=*|*.) ;;
-		*) c="$c " ;;
-		esac
-		printf '%s\n' "$c"
-	done
-}
-
 # The following function is based on code from:
 # The following function is based on code from:
 #
 #
 #   bash_completion - programmable completion functions for bash 3.2+
 #   bash_completion - programmable completion functions for bash 3.2+
@@ -195,8 +180,18 @@ _get_comp_words_by_ref ()
 }
 }
 fi
 fi
 
 
-# Generates completion reply with compgen, appending a space to possible
-# completion words, if necessary.
+__gitcompadd ()
+{
+	local i=0
+	for x in $1; do
+		if [[ "$x" == "$3"* ]]; then
+			COMPREPLY[i++]="$2$x$4"
+		fi
+	done
+}
+
+# Generates completion reply, appending a space to possible completion words,
+# if necessary.
 # It accepts 1 to 4 arguments:
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words.
 # 1: List of possible completion words.
 # 2: A prefix to be added to each possible completion word (optional).
 # 2: A prefix to be added to each possible completion word (optional).
@@ -208,19 +203,25 @@ __gitcomp ()
 
 
 	case "$cur_" in
 	case "$cur_" in
 	--*=)
 	--*=)
-		COMPREPLY=()
 		;;
 		;;
 	*)
 	*)
-		local IFS=$'\n'
-		COMPREPLY=($(compgen -P "${2-}" \
-			-W "$(__gitcomp_1 "${1-}" "${4-}")" \
-			-- "$cur_"))
+		local c i=0 IFS=$' \t\n'
+		for c in $1; do
+			c="$c${4-}"
+			if [[ $c == "$cur_"* ]]; then
+				case $c in
+				--*=*|*.) ;;
+				*) c="$c " ;;
+				esac
+				COMPREPLY[i++]="${2-}$c"
+			fi
+		done
 		;;
 		;;
 	esac
 	esac
 }
 }
 
 
-# Generates completion reply with compgen from newline-separated possible
-# completion words by appending a space to all of them.
+# Generates completion reply from newline-separated possible completion words
+# by appending a space to all of them.
 # It accepts 1 to 4 arguments:
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words, separated by a single newline.
 # 1: List of possible completion words, separated by a single newline.
 # 2: A prefix to be added to each possible completion word (optional).
 # 2: A prefix to be added to each possible completion word (optional).
@@ -231,7 +232,7 @@ __gitcomp ()
 __gitcomp_nl ()
 __gitcomp_nl ()
 {
 {
 	local IFS=$'\n'
 	local IFS=$'\n'
-	COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
+	__gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
 }
 }
 
 
 # Generates completion reply with compgen from newline-separated possible
 # Generates completion reply with compgen from newline-separated possible
@@ -249,106 +250,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}"))
-
-	# Tell Bash that compspec generates filenames.
-	compopt -o filenames 2>/dev/null
-}
-
-__git_index_file_list_filter_compat ()
-{
-	local path
+	__gitcompadd "$1" "${2-}" "${3-$cur}" ""
 
 
-	while read -r path; do
-		case "$path" in
-		?*/*) echo "${path%%/*}/" ;;
-		*) echo "$path" ;;
-		esac
-	done
+	# use a hack to enable file mode in bash < 4
+	compopt -o filenames +o nospace 2>/dev/null ||
+	compgen -f /non-existing-dir/ > /dev/null
 }
 }
 
 
-__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.
+# 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
+# committed.  It return 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" && git ls-files --exclude-standard $2
+		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
 	) 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 |
-			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
+		__git_ls_files_helper "$root" "$1" |
+		while read -r file; do
+			case "$file" in
+			?*/*) echo "${file%%/*}" ;;
+			*) echo "$file" ;;
+			esac
+		done | sort | uniq
 	fi
 	fi
 }
 }
 
 
@@ -424,14 +369,8 @@ __git_refs ()
 		done
 		done
 		;;
 		;;
 	*)
 	*)
-		git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
-		while read -r hash i; do
-			case "$i" in
-			*^{}) ;;
-			refs/*) echo "${i#refs/*/}" ;;
-			*) echo "$i" ;;
-			esac
-		done
+		echo "HEAD"
+		git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
 		;;
 		;;
 	esac
 	esac
 }
 }
@@ -549,44 +488,23 @@ __git_complete_revlist_file ()
 }
 }
 
 
 
 
-# __git_complete_index_file requires 1 argument: the options to pass to
-# ls-file
+# __git_complete_index_file requires 1 argument:
+# 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
-# 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
+	__gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
 }
 }
 
 
 __git_complete_file ()
 __git_complete_file ()
@@ -614,7 +532,6 @@ __git_complete_remote_or_refspec ()
 			case "$cmd" in
 			case "$cmd" in
 			push) no_complete_refspec=1 ;;
 			push) no_complete_refspec=1 ;;
 			fetch)
 			fetch)
-				COMPREPLY=()
 				return
 				return
 				;;
 				;;
 			*) ;;
 			*) ;;
@@ -630,7 +547,6 @@ __git_complete_remote_or_refspec ()
 		return
 		return
 	fi
 	fi
 	if [ $no_complete_refspec = 1 ]; then
 	if [ $no_complete_refspec = 1 ]; then
-		COMPREPLY=()
 		return
 		return
 	fi
 	fi
 	[ "$remote" = "." ] && remote=
 	[ "$remote" = "." ] && remote=
@@ -732,6 +648,7 @@ __git_list_porcelain_commands ()
 		cat-file)         : plumbing;;
 		cat-file)         : plumbing;;
 		check-attr)       : plumbing;;
 		check-attr)       : plumbing;;
 		check-ignore)     : plumbing;;
 		check-ignore)     : plumbing;;
+		check-mailmap)    : plumbing;;
 		check-ref-format) : plumbing;;
 		check-ref-format) : plumbing;;
 		checkout-index)   : plumbing;;
 		checkout-index)   : plumbing;;
 		commit-tree)      : plumbing;;
 		commit-tree)      : plumbing;;
@@ -951,7 +868,6 @@ _git_am ()
 			"
 			"
 		return
 		return
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_apply ()
 _git_apply ()
@@ -971,7 +887,6 @@ _git_apply ()
 			"
 			"
 		return
 		return
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_add ()
 _git_add ()
@@ -1031,7 +946,6 @@ _git_bisect ()
 		__gitcomp_nl "$(__git_refs)"
 		__gitcomp_nl "$(__git_refs)"
 		;;
 		;;
 	*)
 	*)
-		COMPREPLY=()
 		;;
 		;;
 	esac
 	esac
 }
 }
@@ -1124,9 +1038,14 @@ _git_cherry ()
 
 
 _git_cherry_pick ()
 _git_cherry_pick ()
 {
 {
+	local dir="$(__gitdir)"
+	if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
+		__gitcomp "--continue --quit --abort"
+		return
+	fi
 	case "$cur" in
 	case "$cur" in
 	--*)
 	--*)
-		__gitcomp "--edit --no-commit"
+		__gitcomp "--edit --no-commit --signoff --strategy= --mainline"
 		;;
 		;;
 	*)
 	*)
 		__gitcomp_nl "$(__git_refs)"
 		__gitcomp_nl "$(__git_refs)"
@@ -1170,7 +1089,6 @@ _git_clone ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_commit ()
 _git_commit ()
@@ -1182,13 +1100,6 @@ _git_commit ()
 		;;
 		;;
 	esac
 	esac
 
 
-	case "$prev" in
-	-c|-C)
-		__gitcomp_nl "$(__git_refs)" "" "${cur}"
-		return
-		;;
-	esac
-
 	case "$cur" in
 	case "$cur" in
 	--cleanup=*)
 	--cleanup=*)
 		__gitcomp "default strip verbatim whitespace
 		__gitcomp "default strip verbatim whitespace
@@ -1218,7 +1129,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"
@@ -1251,7 +1162,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
 			--no-prefix --src-prefix= --dst-prefix=
 			--no-prefix --src-prefix= --dst-prefix=
 			--inter-hunk-context=
 			--inter-hunk-context=
 			--patience --histogram --minimal
 			--patience --histogram --minimal
-			--raw
+			--raw --word-diff
 			--dirstat --dirstat= --dirstat-by-file
 			--dirstat --dirstat= --dirstat-by-file
 			--dirstat-by-file= --cumulative
 			--dirstat-by-file= --cumulative
 			--diff-algorithm=
 			--diff-algorithm=
@@ -1299,7 +1210,7 @@ _git_difftool ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	__git_complete_file
+	__git_complete_revlist_file
 }
 }
 
 
 __git_fetch_options="
 __git_fetch_options="
@@ -1319,11 +1230,12 @@ _git_fetch ()
 }
 }
 
 
 __git_format_patch_options="
 __git_format_patch_options="
-	--stdout --attach --no-attach --thread --thread= --output-directory
+	--stdout --attach --no-attach --thread --thread= --no-thread
 	--numbered --start-number --numbered-files --keep-subject --signoff
 	--numbered --start-number --numbered-files --keep-subject --signoff
 	--signature --no-signature --in-reply-to= --cc= --full-index --binary
 	--signature --no-signature --in-reply-to= --cc= --full-index --binary
 	--not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
 	--not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
 	--inline --suffix= --ignore-if-in-upstream --subject-prefix=
 	--inline --suffix= --ignore-if-in-upstream --subject-prefix=
+	--output-directory --reroll-count --to= --quiet --notes
 "
 "
 
 
 _git_format_patch ()
 _git_format_patch ()
@@ -1354,7 +1266,6 @@ _git_fsck ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_gc ()
 _git_gc ()
@@ -1365,7 +1276,6 @@ _git_gc ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_gitk ()
 _git_gitk ()
@@ -1442,7 +1352,6 @@ _git_init ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_ls_files ()
 _git_ls_files ()
@@ -1578,7 +1487,6 @@ _git_mergetool ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	COMPREPLY=()
 }
 }
 
 
 _git_merge_base ()
 _git_merge_base ()
@@ -1819,7 +1727,7 @@ __git_config_get_set_variables ()
 _git_config ()
 _git_config ()
 {
 {
 	case "$prev" in
 	case "$prev" in
-	branch.*.remote)
+	branch.*.remote|branch.*.pushremote)
 		__gitcomp_nl "$(__git_remotes)"
 		__gitcomp_nl "$(__git_remotes)"
 		return
 		return
 		;;
 		;;
@@ -1827,11 +1735,19 @@ _git_config ()
 		__gitcomp_nl "$(__git_refs)"
 		__gitcomp_nl "$(__git_refs)"
 		return
 		return
 		;;
 		;;
+	branch.*.rebase)
+		__gitcomp "false true"
+		return
+		;;
+	remote.pushdefault)
+		__gitcomp_nl "$(__git_remotes)"
+		return
+		;;
 	remote.*.fetch)
 	remote.*.fetch)
 		local remote="${prev#remote.}"
 		local remote="${prev#remote.}"
 		remote="${remote%.fetch}"
 		remote="${remote%.fetch}"
 		if [ -z "$cur" ]; then
 		if [ -z "$cur" ]; then
-			COMPREPLY=("refs/heads/")
+			__gitcomp_nl "refs/heads/" "" "" ""
 			return
 			return
 		fi
 		fi
 		__gitcomp_nl "$(__git_refs_remotes "$remote")"
 		__gitcomp_nl "$(__git_refs_remotes "$remote")"
@@ -1866,6 +1782,10 @@ _git_config ()
 			"
 			"
 		return
 		return
 		;;
 		;;
+	diff.submodule)
+		__gitcomp "log short"
+		return
+		;;
 	help.format)
 	help.format)
 		__gitcomp "man info web html"
 		__gitcomp "man info web html"
 		return
 		return
@@ -1891,7 +1811,6 @@ _git_config ()
 		return
 		return
 		;;
 		;;
 	*.*)
 	*.*)
-		COMPREPLY=()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
@@ -1908,7 +1827,7 @@ _git_config ()
 		;;
 		;;
 	branch.*.*)
 	branch.*.*)
 		local pfx="${cur%.*}." cur_="${cur##*.}"
 		local pfx="${cur%.*}." cur_="${cur##*.}"
-		__gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
+		__gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
 		return
 		return
 		;;
 		;;
 	branch.*)
 	branch.*)
@@ -2039,7 +1958,6 @@ _git_config ()
 		core.fileMode
 		core.fileMode
 		core.fsyncobjectfiles
 		core.fsyncobjectfiles
 		core.gitProxy
 		core.gitProxy
-		core.ignoreCygwinFSTricks
 		core.ignoreStat
 		core.ignoreStat
 		core.ignorecase
 		core.ignorecase
 		core.logAllRefUpdates
 		core.logAllRefUpdates
@@ -2061,13 +1979,14 @@ _git_config ()
 		core.whitespace
 		core.whitespace
 		core.worktree
 		core.worktree
 		diff.autorefreshindex
 		diff.autorefreshindex
-		diff.statGraphWidth
 		diff.external
 		diff.external
 		diff.ignoreSubmodules
 		diff.ignoreSubmodules
 		diff.mnemonicprefix
 		diff.mnemonicprefix
 		diff.noprefix
 		diff.noprefix
 		diff.renameLimit
 		diff.renameLimit
 		diff.renames
 		diff.renames
+		diff.statGraphWidth
+		diff.submodule
 		diff.suppressBlankEmpty
 		diff.suppressBlankEmpty
 		diff.tool
 		diff.tool
 		diff.wordRegex
 		diff.wordRegex
@@ -2202,6 +2121,7 @@ _git_config ()
 		receive.fsckObjects
 		receive.fsckObjects
 		receive.unpackLimit
 		receive.unpackLimit
 		receive.updateserverinfo
 		receive.updateserverinfo
+		remote.pushdefault
 		remotes.
 		remotes.
 		repack.usedeltabaseoffset
 		repack.usedeltabaseoffset
 		rerere.autoupdate
 		rerere.autoupdate
@@ -2272,7 +2192,6 @@ _git_remote ()
 		__gitcomp "$c"
 		__gitcomp "$c"
 		;;
 		;;
 	*)
 	*)
-		COMPREPLY=()
 		;;
 		;;
 	esac
 	esac
 }
 }
@@ -2356,7 +2275,7 @@ _git_show ()
 		return
 		return
 		;;
 		;;
 	esac
 	esac
-	__git_complete_file
+	__git_complete_revlist_file
 }
 }
 
 
 _git_show_branch ()
 _git_show_branch ()
@@ -2388,8 +2307,6 @@ _git_stash ()
 		*)
 		*)
 			if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
 			if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
 				__gitcomp "$subcommands"
 				__gitcomp "$subcommands"
-			else
-				COMPREPLY=()
 			fi
 			fi
 			;;
 			;;
 		esac
 		esac
@@ -2402,14 +2319,12 @@ _git_stash ()
 			__gitcomp "--index --quiet"
 			__gitcomp "--index --quiet"
 			;;
 			;;
 		show,--*|drop,--*|branch,--*)
 		show,--*|drop,--*|branch,--*)
-			COMPREPLY=()
 			;;
 			;;
 		show,*|apply,*|drop,*|pop,*|branch,*)
 		show,*|apply,*|drop,*|pop,*|branch,*)
 			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
 			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
 					| sed -n -e 's/:.*//p')"
 					| sed -n -e 's/:.*//p')"
 			;;
 			;;
 		*)
 		*)
-			COMPREPLY=()
 			;;
 			;;
 		esac
 		esac
 	fi
 	fi
@@ -2419,7 +2334,7 @@ _git_submodule ()
 {
 {
 	__git_has_doubledash && return
 	__git_has_doubledash && return
 
 
-	local subcommands="add status init update summary foreach sync"
+	local subcommands="add status init deinit update summary foreach sync"
 	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 		case "$cur" in
 		case "$cur" in
 		--*)
 		--*)
@@ -2451,7 +2366,7 @@ _git_svn ()
 			--no-metadata --use-svm-props --use-svnsync-props
 			--no-metadata --use-svm-props --use-svnsync-props
 			--log-window-size= --no-checkout --quiet
 			--log-window-size= --no-checkout --quiet
 			--repack-flags --use-log-author --localtime
 			--repack-flags --use-log-author --localtime
-			--ignore-paths= $remote_opts
+			--ignore-paths= --include-paths= $remote_opts
 			"
 			"
 		local init_opts="
 		local init_opts="
 			--template= --shared= --trunk= --tags=
 			--template= --shared= --trunk= --tags=
@@ -2526,7 +2441,6 @@ _git_svn ()
 			__gitcomp "--revision= --parent"
 			__gitcomp "--revision= --parent"
 			;;
 			;;
 		*)
 		*)
-			COMPREPLY=()
 			;;
 			;;
 		esac
 		esac
 	fi
 	fi
@@ -2551,13 +2465,10 @@ _git_tag ()
 
 
 	case "$prev" in
 	case "$prev" in
 	-m|-F)
 	-m|-F)
-		COMPREPLY=()
 		;;
 		;;
 	-*|tag)
 	-*|tag)
 		if [ $f = 1 ]; then
 		if [ $f = 1 ]; then
 			__gitcomp_nl "$(__git_tags)"
 			__gitcomp_nl "$(__git_tags)"
-		else
-			COMPREPLY=()
 		fi
 		fi
 		;;
 		;;
 	*)
 	*)
@@ -2579,9 +2490,10 @@ __git_main ()
 		i="${words[c]}"
 		i="${words[c]}"
 		case "$i" in
 		case "$i" in
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+		--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
 		--bare)      __git_dir="." ;;
 		--bare)      __git_dir="." ;;
 		--help) command="help"; break ;;
 		--help) command="help"; break ;;
-		-c) c=$((++c)) ;;
+		-c|--work-tree|--namespace) ((c++)) ;;
 		-*) ;;
 		-*) ;;
 		*) command="$i"; break ;;
 		*) command="$i"; break ;;
 		esac
 		esac
@@ -2599,6 +2511,7 @@ __git_main ()
 			--exec-path
 			--exec-path
 			--exec-path=
 			--exec-path=
 			--html-path
 			--html-path
+			--man-path
 			--info-path
 			--info-path
 			--work-tree=
 			--work-tree=
 			--namespace=
 			--namespace=
@@ -2693,35 +2606,19 @@ if [[ -n ${ZSH_VERSION-} ]]; then
 		compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 		compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 	}
 	}
 
 
-	__git_zsh_helper ()
-	{
-		emulate -L ksh
-		local cur cword prev
-		cur=${words[CURRENT-1]}
-		prev=${words[CURRENT-2]}
-		let cword=CURRENT-1
-		__${service}_main
-	}
-
 	_git ()
 	_git ()
 	{
 	{
-		emulate -L zsh
-		local _ret=1
-		__git_zsh_helper
-		let _ret && _default -S '' && _ret=0
+		local _ret=1 cur cword prev
+		cur=${words[CURRENT]}
+		prev=${words[CURRENT-1]}
+		let cword=CURRENT-1
+		emulate ksh -c __${service}_main
+		let _ret && _default && _ret=0
 		return _ret
 		return _ret
 	}
 	}
 
 
 	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 ()

+ 190 - 144
plugins/gitfast/git-prompt.sh

@@ -3,7 +3,7 @@
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Distributed under the GNU General Public License, version 2.0.
 # 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:
 # To enable:
 #
 #
@@ -13,23 +13,27 @@
 #    3a) Change your PS1 to call __git_ps1 as
 #    3a) Change your PS1 to call __git_ps1 as
 #        command-substitution:
 #        command-substitution:
 #        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #        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.
 #        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.
-#           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.
+#    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, 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
 #        Optionally, you can supply a third argument with a printf
 #        format string to finetune the output of the branch status
 #        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,
 # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 # unstaged (*) and staged (+) changes will be shown next to the branch
 # unstaged (*) and staged (+) changes will be shown next to the branch
@@ -77,31 +81,8 @@
 #
 #
 # If you would like a colored hint about the current dirty state, set
 # 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
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
-
-# __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
-}
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
 
 
 # stores the divergence from upstream in $p
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
 # used by GIT_PS1_SHOWUPSTREAM
@@ -124,7 +105,7 @@ __git_ps1_show_upstream ()
 			fi
 			fi
 			;;
 			;;
 		svn-remote.*.url)
 		svn-remote.*.url)
-			svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+			svn_remote[$((${#svn_remote[@]} + 1))]="$value"
 			svn_url_pattern+="\\|$value"
 			svn_url_pattern+="\\|$value"
 			upstream=svn+git # default upstream is SVN if available, else git
 			upstream=svn+git # default upstream is SVN if available, else git
 			;;
 			;;
@@ -146,10 +127,11 @@ __git_ps1_show_upstream ()
 	svn*)
 	svn*)
 		# get the upstream from the "git-svn-id: ..." in a commit message
 		# get the upstream from the "git-svn-id: ..." in a commit message
 		# (git-svn uses essentially the same procedure internally)
 		# (git-svn uses essentially the same procedure internally)
-		local svn_upstream=($(git log --first-parent -1 \
+		local -a svn_upstream
+		svn_upstream=($(git log --first-parent -1 \
 					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
 					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
 		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
 		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
-			svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+			svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
 			svn_upstream=${svn_upstream%@*}
 			svn_upstream=${svn_upstream%@*}
 			local n_stop="${#svn_remote[@]}"
 			local n_stop="${#svn_remote[@]}"
 			for ((n=1; n <= n_stop; n++)); do
 			for ((n=1; n <= n_stop; n++)); do
@@ -222,6 +204,51 @@ __git_ps1_show_upstream ()
 
 
 }
 }
 
 
+# Helper function that is meant to be called from __git_ps1.  It
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
+__git_ps1_colorize_gitstring ()
+{
+	if [[ -n ${ZSH_VERSION-} ]]; then
+		local c_red='%F{red}'
+		local c_green='%F{green}'
+		local c_lblue='%F{blue}'
+		local c_clear='%f'
+	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 bad_color=$c_red
+	local ok_color=$c_green
+	local flags_color="$c_lblue"
+
+	local branch_color=""
+	if [ $detached = no ]; then
+		branch_color="$ok_color"
+	else
+		branch_color="$bad_color"
+	fi
+	c="$branch_color$c"
+
+	z="$c_clear$z"
+	if [ "$w" = "*" ]; then
+		w="$bad_color$w"
+	fi
+	if [ -n "$i" ]; then
+		i="$ok_color$i"
+	fi
+	if [ -n "$s" ]; then
+		s="$flags_color$s"
+	fi
+	if [ -n "$u" ]; then
+		u="$bad_color$u"
+	fi
+	r="$c_clear$r"
+}
 
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
 # when called from PS1 using command substitution
 # when called from PS1 using command substitution
@@ -254,39 +281,83 @@ __git_ps1 ()
 		;;
 		;;
 	esac
 	esac
 
 
-	local g="$(__gitdir)"
-	if [ -z "$g" ]; then
+	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
 		if [ $pcmode = yes ]; then
 		if [ $pcmode = yes ]; then
 			#In PC mode PS1 always needs to be set
 			#In PC mode PS1 always needs to be set
 			PS1="$ps1pc_start$ps1pc_end"
 			PS1="$ps1pc_start$ps1pc_end"
 		fi
 		fi
-	else
-		local r=""
-		local b=""
+		return
+	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'*}"
+
+	local r=""
+	local b=""
+	local step=""
+	local total=""
+	if [ -d "$g/rebase-merge" ]; then
+		read b 2>/dev/null <"$g/rebase-merge/head-name"
+		read step 2>/dev/null <"$g/rebase-merge/msgnum"
+		read total 2>/dev/null <"$g/rebase-merge/end"
 		if [ -f "$g/rebase-merge/interactive" ]; then
 		if [ -f "$g/rebase-merge/interactive" ]; then
 			r="|REBASE-i"
 			r="|REBASE-i"
-			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -d "$g/rebase-merge" ]; then
+		else
 			r="|REBASE-m"
 			r="|REBASE-m"
-			b="$(cat "$g/rebase-merge/head-name")"
+		fi
+	else
+		if [ -d "$g/rebase-apply" ]; then
+			read step 2>/dev/null <"$g/rebase-apply/next"
+			read total 2>/dev/null <"$g/rebase-apply/last"
+			if [ -f "$g/rebase-apply/rebasing" ]; then
+				read b 2>/dev/null <"$g/rebase-apply/head-name"
+				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"
+		fi
+
+		if [ -n "$b" ]; then
+			:
+		elif [ -h "$g/HEAD" ]; then
+			# symlink symbolic ref
+			b="$(git symbolic-ref HEAD 2>/dev/null)"
 		else
 		else
-			if [ -d "$g/rebase-apply" ]; then
-				if [ -f "$g/rebase-apply/rebasing" ]; then
-					r="|REBASE"
-				elif [ -f "$g/rebase-apply/applying" ]; then
-					r="|AM"
-				else
-					r="|AM/REBASE"
+			local head=""
+			if ! read head 2>/dev/null <"$g/HEAD"; then
+				if [ $pcmode = yes ]; then
+					PS1="$ps1pc_start$ps1pc_end"
 				fi
 				fi
-			elif [ -f "$g/MERGE_HEAD" ]; then
-				r="|MERGING"
-			elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
-				r="|CHERRY-PICKING"
-			elif [ -f "$g/BISECT_LOG" ]; then
-				r="|BISECTING"
+				return
 			fi
 			fi
-
-			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+			# is it a symbolic ref?
+			b="${head#ref: }"
+			if [ "$head" = "$b" ]; then
 				detached=yes
 				detached=yes
 				b="$(
 				b="$(
 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -300,100 +371,75 @@ __git_ps1 ()
 					git describe --tags --exact-match HEAD ;;
 					git describe --tags --exact-match HEAD ;;
 				esac 2>/dev/null)" ||
 				esac 2>/dev/null)" ||
 
 
-				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
-				b="unknown"
+				b="$short_sha..."
 				b="($b)"
 				b="($b)"
-			}
+			fi
 		fi
 		fi
+	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
 			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
+		fi
+		if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+		   [ -r "$g/refs/stash" ]; then
+			s="$"
+		fi
 
 
-			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
-			   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
-			   [ -n "$(git ls-files --others --exclude-standard)" ]
-			then
-				u="%%"
-			fi
+		if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+		   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+		   git ls-files --others --exclude-standard --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
+	fi
 
 
-		local f="$w$i$s$u"
-		if [ $pcmode = yes ]; then
-			local gitstring=
-			if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-				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 z="${GIT_PS1_STATESEPARATOR-" "}"
 
 
-				if [ $detached = no ]; then
-					branch_color="$ok_color"
-				else
-					branch_color="$bad_color"
-				fi
+	# NO color option unless in PROMPT_COMMAND mode
+	if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+		__git_ps1_colorize_gitstring
+	fi
 
 
-				# Setting gitstring directly with \[ and \] around colors
-				# is necessary to prevent wrapping issues!
-				gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
+	local f="$w$i$s$u"
+	local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
 
 
-				if [ -n "$w$i$s$u$r$p" ]; then
-					gitstring="$gitstring "
-				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"
-			else
-				gitstring="$c${b##refs/heads/}${f:+ $f}$r$p"
-			fi
+	if [ $pcmode = yes ]; then
+		if [[ -n ${ZSH_VERSION-} ]]; then
 			gitstring=$(printf -- "$printf_format" "$gitstring")
 			gitstring=$(printf -- "$printf_format" "$gitstring")
-			PS1="$ps1pc_start$gitstring$ps1pc_end"
 		else
 		else
-			# NO color option unless in PROMPT_COMMAND mode
-			printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p"
+			printf -v gitstring -- "$printf_format" "$gitstring"
 		fi
 		fi
+		PS1="$ps1pc_start$gitstring$ps1pc_end"
+	else
+		printf -- "$printf_format" "$gitstring"
 	fi
 	fi
 }
 }