]> git.madduck.net Git - etc/zsh.git/blob - .zsh/zshrc/60_vcsprompt

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

07ea29388fdeba3ac45de18284ec9aa8eab6ef94
[etc/zsh.git] / .zsh / zshrc / 60_vcsprompt
1 # zshrc/60_vcsprompt
2 #
3 # Make git information available to the prompt
4 #
5 # Copyright © 1994–2008 martin f. krafft <madduck@madduck.net>
6 # Released under the terms of the Artistic Licence 2.0
7 #
8 # Source repository: git://git.madduck.net/etc/zsh.git
9 #
10 # Shamelessly based on http://glandium.org/blog/?p=170
11 #
12
13 __git_get_reporoot()
14 {
15   # return the full path to the root of the current git repository
16   [ -d "$GIT_DIR" ] && echo "$GIT_DIR" && return 0
17   local relroot
18   relroot="$(git rev-parse --show-cdup 2>/dev/null)" || return 1
19   if [ -n "$relroot" ]; then
20     readlink -f "$relroot"
21   else
22     echo $PWD
23   fi
24 }
25
26 __git_get_branch()
27 {
28   # return the name of the git branch we're on
29   local ref
30   ref=$(git --git-dir="$PWD" symbolic-ref -q HEAD 2>/dev/null \
31      || git --git-dir="$PWD" name-rev --name-only HEAD 2>/dev/null) || return 1
32   echo "${ref#refs/heads/}"
33 }
34
35 __git_print_preprompt()
36 {
37   [ "$(git config --get core.bare)" = false ] || return
38
39   local output
40   output=(${(f):-"$(git diff --stat --relative 2>/dev/null)"})
41   if [[ ${#output} -gt 1 ]]; then
42     echo changes on filesystem:
43     print "${${(F)output[1,-2]}//\.\.\./…}"
44   fi
45   output=(${(f):-"$(git diff --cached --stat --relative 2>/dev/null)"})
46   if [[ ${#output} -gt 1 ]]; then
47     echo cached/staged changes:
48     print "${${(F)output[1,-2]}//\.\.\./…}"
49   fi
50 }
51
52 __hg_get_reporoot()
53 {
54   hg root
55 }
56
57 __hg_get_branch()
58 {
59   echo "hg:$(hg branch)"
60 }
61
62 __bzr_get_reporoot()
63 {
64   local reporoot
65   reporoot="$(bzr info | sed -rne 's, *branch root: ,,p')"
66   case "$reporoot" in
67     .) echo "$PWD";;
68     *) echo "$reporoot";;
69   esac
70 }
71
72 __bzr_get_branch()
73 {
74   local branch revno
75   bzr version-info | while read i j; do
76       case "$i" in
77         revno:) revno="$j";;
78         branch-nick:) branch="$j";;
79       esac
80     done
81   echo "bzr:${branch}@$revno"
82 }
83
84 __vcs_get_repo_type()
85 {
86   # return the type of the closest repository in the path hierarchy
87   local dir
88   while true; do
89     [ -d ${dir}.git ] && echo git && break
90     [ -d "$GIT_DIR" ] && echo git && break
91     [ -d ${dir}.bzr ] && echo bzr && break
92     [ -d ${dir}.hg ] && echo hg && break
93     [ "$(readlink -f ${dir:-.})" = / ] && echo NONE && break
94     dir="../$dir"
95   done
96 }
97
98 __vcs_get_prompt_path_components()
99 {
100   # return formatted path components (prefix branch postfix) given
101   # the repository root and the branch.
102
103   # shortcut: if there are no arguments, return a default prompt
104   if [ -z "${1:-}" ]; then
105     pwdnamed="${(%):-%${_PROMPT_PATH_MAXLEN}<…<%~%<<}"
106     echo "$pwdnamed"
107     return
108   fi
109
110   local reporoot branch
111   reporoot="${1%%/}"
112   branch="$2"
113
114   # replace named directories in the PWD, we need thi for the proper component
115   # count later
116   local pwdnamed
117   pwdnamed="${(%):-%~}"
118
119   # store paths in arrays for component count calculation
120   typeset -la apwd apwdnamed areporoot
121   apwd=(${(s:/:)PWD})
122   apwdnamed=(${(s:/:)pwdnamed})
123   areporoot=(${(s:/:)reporoot})
124
125   # get the number of leading and trailing path components. Since we're using
126   # %~ later and then /home/madduck suddenly becomes ~, which is 1, not
127   # 2 components, we calculate the leading component count by using the named
128   # path and the number of post components
129   local precomps postcomps
130   postcomps=$(($#apwd - $#areporoot))
131   precomps=$(($#apwdnamed - $postcomps))
132
133   local postfix
134   (( $postcomps > 0 )) && postfix="${(%):-%${postcomps}~}"
135
136   # we don't want the prompt to get too long, so keep the total prompt length
137   # under $_PROMPT_PATH_MAXLEN (25), but ensure that the prefix is not shorter
138   # than $_PROMPT_PATH_MINLEN (10), no matter what
139   local prelen minlen prefix
140   prelen=$((${_PROMPT_PATH_MAXLEN:-25} - $#branch - $#postfix))
141   minlen=${_PROMPT_PATH_MINLEN:-10}
142   (( $prelen < $minlen )) && prelen=$minlen
143   prefix="${(%):-%${prelen}<…<%-${precomps}~%<<}"
144
145   echo "'$prefix'" "'$branch'" "'$postfix'"
146 }
147
148 __vcs_set_prompt_variables()
149 {
150   # set psvar[1..3] depending on repo type, or just psvar[1] if no repo found
151   local reporoot branch repotype
152   repotype="${1:-$(__vcs_get_repo_type)}"
153
154   case "$repotype" in
155     git)
156       reporoot="$(__git_get_reporoot)" ||
157         { error "could not determine git repository root"; return 1 }
158       branch="$(__git_get_branch)" ||
159         { error "could not determine git branch"; return 1 }
160       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
161       if [ -d "$GIT_DIR" ]; then
162         # poor man's replace until I find out how to do named dirs properly
163         # here:
164         local _D="${GIT_DIR/$HOME/~}"
165         set -- "$_D" "$2" "${${1#$_D}%/}"
166       fi
167       ;;
168     hg)
169       reporoot="$(__hg_get_reporoot)" ||
170         { error "could not determine hg repository root"; return 1 }
171       branch="$(__hg_get_branch)" ||
172         { error "could not determine hg branch"; return 1 }
173       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
174       ;;
175     bzr)
176       reporoot="$(__bzr_get_reporoot)" ||
177         { error "could not determine bzr repository root"; return 1 }
178       branch="$(__bzr_get_branch)" ||
179         { error "could not determine bzr branch"; return 1 }
180       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
181       ;;
182     *)
183       case "$repotype" in
184         NONE) :;;
185         *) warn "$repotype repositories not (yet) supported in the prompt";;
186       esac
187       local p="%${MAXLEN}<…<%~%<<"
188       #TODO find a better way so we don't have to nuke $psvar, but since the
189       #     %(nv.true.false) check for prompts checks element count, not
190       #     content, that's all we get for now
191       psvar=("${(%)p}")
192       return
193   esac
194
195   psvar[1,3]=($1 $2 $3)
196 }
197
198 __vcs_print_preprompt()
199 {
200   local reporoot
201   repotype="${1:-$(__vcs_get_repo_type)}"
202
203   case "$repotype" in
204     git)
205       __git_print_preprompt
206       ;;
207   esac
208 }
209
210 if ! is_root; then
211   # too dangerous to be run as root
212
213   _update_vcs_prompt_vars_if_vcs_ran() {
214     local vcs="$(__vcs_get_repo_type)"
215     case "$(history $(($HISTCMD - 1)))" in
216       # $vcs appeared in last command, so be sure to update
217       *${vcs}*) __vcs_set_prompt_variables "$vcs"
218     esac
219   }
220   precmd_functions+=_update_vcs_prompt_vars_if_vcs_ran
221
222   _update_vcs_prompt_vars() {
223     __vcs_set_prompt_variables
224   }
225   chpwd_functions+=_update_vcs_prompt_vars
226
227   _print_preprompt() {
228     [[ $? -eq 0 ]] && __vcs_print_preprompt
229   }
230   precmd_functions+=_print_preprompt
231
232   # call it once
233   _update_vcs_prompt_vars
234 fi
235
236 # vim:ft=zsh