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

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