]> 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:

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