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

simplify variable expansions
[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 ${dir}.bzr ] && echo bzr && break
74     [ -d ${dir}.hg ] && echo hg && break
75     [ "$(readlink -f ${dir:-.})" = / ] && echo NONE && break
76     dir="../$dir"
77   done
78 }
79
80 __vcs_get_prompt_path_components()
81 {
82   # return formatted path components (prefix branch postfix) given
83   # the repository root and the branch.
84
85   # shortcut: if there are no arguments, return a default prompt
86   if [ -z "${1:-}" ]; then
87     pwdnamed="${(%):-'%${_PROMPT_PATH_MAXLEN}<..<%~%<<'}"
88     echo "$pwdnamed"
89     return
90   fi
91
92   local reporoot branch
93   reporoot="${1%%/}"
94   branch="$2"
95
96   # replace named directories in the PWD, we need thi for the proper component
97   # count later
98   local pwdnamed
99   pwdnamed="${(%):-%~}"
100
101   # store paths in arrays for component count calculation
102   typeset -la apwd apwdnamed areporoot
103   apwd=(${(s:/:)PWD})
104   apwdnamed=(${(s:/:)pwdnamed})
105   areporoot=(${(s:/:)reporoot})
106
107   # get the number of leading and trailing path components. Since we're using
108   # %~ later and then /home/madduck suddenly becomes ~, which is 1, not
109   # 2 components, we calculate the leading component count by using the named
110   # path and the number of post components
111   local precomps postcomps
112   postcomps=$(($#apwd - $#areporoot))
113   precomps=$(($#apwdnamed - $postcomps))
114
115   local postfix
116   (( $postcomps > 0 )) && postfix="${(%):-'%${postcomps}~'}"
117
118   # we don't want the prompt to get too long, so keep the total prompt length
119   # under $_PROMPT_PATH_MAXLEN (25), but ensure that the prefix is not shorter
120   # than $_PROMPT_PATH_MINLEN (10), no matter what
121   local prelen minlen prefix
122   prelen=$((${_PROMPT_PATH_MAXLEN:-25} - $#branch - $#postfix))
123   minlen=${_PROMPT_PATH_MINLEN:-10}
124   (( $prelen < $minlen )) && prelen=$minlen
125   prefix="${(%):-'%${prelen}<..<%-${precomps}~%<<'}"
126
127   echo "'$prefix'" "'$branch'" "'$postfix'"
128 }
129
130 __vcs_set_prompt_variables()
131 {
132   # set psvar[1..3] depending on repo type, or just psvar[1] if no repo found
133   local reporoot branch repotype
134   repotype="${1:-$(__vcs_get_repo_type)}"
135
136   case "$repotype" in
137     git)
138       reporoot="$(__git_get_reporoot)" ||
139         { error "could not determine git repository root"; return 1 }
140       branch="$(__git_get_branch)" ||
141         { error "could not determine git branch"; return 1 }
142       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
143       [ -d "$GIT_DIR" ] && set -- "$GIT_DIR" "$2" "$1"
144       ;;
145     hg)
146       reporoot="$(__hg_get_reporoot)" ||
147         { error "could not determine hg repository root"; return 1 }
148       branch="$(__hg_get_branch)" ||
149         { error "could not determine hg branch"; return 1 }
150       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
151       ;;
152     bzr)
153       reporoot="$(__bzr_get_reporoot)" ||
154         { error "could not determine bzr repository root"; return 1 }
155       branch="$(__bzr_get_branch)" ||
156         { error "could not determine bzr branch"; return 1 }
157       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
158       ;;
159     *)
160       case "$repotype" in
161         NONE) :;;
162         *) warn "$repotype repositories not (yet) supported in the prompt";;
163       esac
164       local p="%${MAXLEN}<..<%~%<<"
165       #TODO find a better way so we don't have to nuke $psvar, but since the
166       #     %(nv.true.false) check for prompts checks element count, not
167       #     content, that's all we get for now
168       psvar=("${(%)p}")
169       return
170   esac
171
172   psvar[1,3]=($1 $2 $3)
173 }
174
175 if ! is_root; then
176   # too dangerous to be run as root
177
178   _update_vcs_prompt_vars_if_vcs_ran() {
179     local vcs="$(__vcs_get_repo_type)"
180     case "$(history $(($HISTCMD - 1)))" in
181       # $vcs appeared in last command, so be sure to update
182       *${vcs}*) __vcs_set_prompt_variables "$vcs"
183     esac
184   }
185   precmd_functions+=_update_vcs_prompt_vars_if_vcs_ran
186
187   _update_vcs_prompt_vars() {
188     __vcs_set_prompt_variables
189   }
190   chpwd_functions+=_update_vcs_prompt_vars
191
192   # call it once
193   _update_vcs_prompt_vars
194 fi
195
196 # vim:ft=zsh