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

turn on extended globbing
[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     pwdnamed="${(%)pwdnamed}"
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="${(%)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   if (( $postcomps > 0 )); then
118     postfix="%${postcomps}~"
119     postfix="${(%)postfix}"
120   fi
121
122   # we don't want the prompt to get too long, so keep the total prompt length
123   # under $_PROMPT_PATH_MAXLEN (25), but ensure that the prefix is not shorter
124   # than $_PROMPT_PATH_MINLEN (10), no matter what
125   local prelen minlen prefix
126   prelen=$((${_PROMPT_PATH_MAXLEN:-25} - $#branch - $#postfix))
127   minlen=${_PROMPT_PATH_MINLEN:-10}
128   (( $prelen < $minlen )) && prelen=$minlen
129   prefix="%${prelen}<..<%-${precomps}~%<<"
130   prefix="${(%)prefix}"
131
132   echo "'$prefix'" "'$branch'" "'$postfix'"
133 }
134
135 __vcs_set_prompt_variables()
136 {
137   # set psvar[1..3] depending on repo type, or just psvar[1] if no repo found
138   local reporoot branch repotype
139   repotype="${1:-$(__vcs_get_repo_type)}"
140
141   case "$repotype" in
142     git)
143       reporoot="$(__git_get_reporoot)" ||
144         { error "could not determine git repository root"; return 1 }
145       branch="$(__git_get_branch)" ||
146         { error "could not determine git branch"; return 1 }
147       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
148       [ -d "$GIT_DIR" ] && set -- "$GIT_DIR" "$2" "$1"
149       ;;
150     hg)
151       reporoot="$(__hg_get_reporoot)" ||
152         { error "could not determine hg repository root"; return 1 }
153       branch="$(__hg_get_branch)" ||
154         { error "could not determine hg branch"; return 1 }
155       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
156       ;;
157     bzr)
158       reporoot="$(__bzr_get_reporoot)" ||
159         { error "could not determine bzr repository root"; return 1 }
160       branch="$(__bzr_get_branch)" ||
161         { error "could not determine bzr branch"; return 1 }
162       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
163       ;;
164     *)
165       case "$repotype" in
166         NONE) :;;
167         *) warn "$repotype repositories not (yet) supported in the prompt";;
168       esac
169       local p="%${MAXLEN}<..<%~%<<"
170       #TODO find a better way so we don't have to nuke $psvar, but since the
171       #     %(nv.true.false) check for prompts checks element count, not
172       #     content, that's all we get for now
173       psvar=("${(%)p}")
174       return
175   esac
176
177   psvar[1]="$1"
178   psvar[2]="$2"
179   psvar[3]="$3"
180 }
181
182 if ! is_root; then
183   # too dangerous to be run as root
184
185   _update_vcs_prompt_vars_if_vcs_ran() {
186     local vcs="$(__vcs_get_repo_type)"
187     case "$(history $(($HISTCMD - 1)))" in
188       # $vcs appeared in last command, so be sure to update
189       *${vcs}*) __vcs_set_prompt_variables "$vcs"
190     esac
191   }
192   precmd_functions+=_update_vcs_prompt_vars_if_vcs_ran
193
194   _update_vcs_prompt_vars() {
195     __vcs_set_prompt_variables
196   }
197   chpwd_functions+=_update_vcs_prompt_vars
198
199   # call it once
200   _update_vcs_prompt_vars
201 fi
202
203 # vim:ft=zsh