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

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