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

improve vcsh prompt integration
[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 [ -n "$VCSH_REPO_NAME" ]; then
163         set -- "vcsh/$VCSH_REPO_NAME" "$2" "$3"
164       elif [ -d "$GIT_DIR" ]; then
165         # poor man's replace until I find out how to do named dirs properly
166         # here:
167         local _D="${GIT_DIR/$HOME/~}"
168         set -- "$_D" "$2" "${${1#$_D}%/}"
169       fi
170       ;;
171     hg)
172       reporoot="$(__hg_get_reporoot)" ||
173         { error "could not determine hg repository root"; return 1 }
174       branch="$(__hg_get_branch)" ||
175         { error "could not determine hg branch"; return 1 }
176       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
177       ;;
178     bzr)
179       reporoot="$(__bzr_get_reporoot)" ||
180         { error "could not determine bzr repository root"; return 1 }
181       branch="$(__bzr_get_branch)" ||
182         { error "could not determine bzr branch"; return 1 }
183       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
184       ;;
185     *)
186       case "$repotype" in
187         NONE) :;;
188         *) warn "$repotype repositories not (yet) supported in the prompt";;
189       esac
190       local p="%${MAXLEN}<…<%~%<<"
191       #TODO find a better way so we don't have to nuke $psvar, but since the
192       #     %(nv.true.false) check for prompts checks element count, not
193       #     content, that's all we get for now
194       psvar=("${(%)p}")
195       return
196   esac
197
198   psvar[1,3]=($1 $2 $3)
199 }
200
201 __vcs_print_preprompt()
202 {
203   local reporoot
204   repotype="${1:-$(__vcs_get_repo_type)}"
205
206   case "$repotype" in
207     git)
208       __git_print_preprompt
209       ;;
210   esac
211 }
212
213 if ! is_root; then
214   # too dangerous to be run as root
215
216   _update_vcs_prompt_vars_if_vcs_ran() {
217     local vcs="$(__vcs_get_repo_type)"
218     case "$(history $(($HISTCMD - 1)))" in
219       # $vcs appeared in last command, so be sure to update
220       *${vcs}*) __vcs_set_prompt_variables "$vcs"
221     esac
222   }
223   precmd_functions+=_update_vcs_prompt_vars_if_vcs_ran
224
225   _update_vcs_prompt_vars() {
226     __vcs_set_prompt_variables
227   }
228   chpwd_functions+=_update_vcs_prompt_vars
229
230   _print_preprompt() {
231     [[ $? -eq 0 ]] && __vcs_print_preprompt
232   }
233   precmd_functions+=_print_preprompt
234
235   # call it once
236   _update_vcs_prompt_vars
237 fi
238
239 # vim:ft=zsh