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

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