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

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