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

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