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

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