1## vim:ft=zsh
2## mercurial support by: Frank Terbeck <ft@bewatermyfriend.org>
3## with large contributions by Seth House <seth@eseth.com>
4## Distributed under the same BSD-ish license as zsh itself.
5
6setopt localoptions extendedglob NO_shwordsplit
7
8local hgbase bmfile branchfile rebasefile dirstatefile mqseriesfile \
9    mqstatusfile mqguardsfile patchdir mergedir \
10    r_csetid r_lrev r_branch i_bmhash i_bmname \
11    revformat branchformat hgactionstring hgchanges \
12    hgbmstring hgmqstring applied_string unapplied_string guards_string
13
14local -a hgid_args defrevformat defbranchformat \
15    hgbmarks mqpatches mqseries mqguards mqunapplied hgmisc \
16    i_patchguards i_negguards i_posguards
17
18local -xA hook_com
19
20hgbase=${vcs_comm[basedir]}
21rrn=${hgbase:t}
22r_csetid='' # changeset id (long hash)
23r_lrev='' # local revision
24patchdir="${hgbase}/.hg/patches"
25mergedir="${hgbase}/.hg/merge/"
26bmfile="${hgbase}/.hg/bookmarks"
27branchfile="${hgbase}/.hg/branch"
28rebasefile="${hgbase}/.hg/rebasestate"
29dirstatefile="${hgbase}/.hg/dirstate"
30mqstatusfile="${patchdir}/status" # currently applied patches
31mqseriesfile="${patchdir}/series" # all patches
32mqguardsfile="${patchdir}/guards"
33
34# Look for any --flavours
35VCS_INFO_adjust
36
37# Calling the 'hg' program is quite a bit too slow for prompts.
38# Disabled by default anyway, so no harm done.
39if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
40    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple \
41            && ( VCS_INFO_check_com hexdump ) && [[ -r ${dirstatefile} ]] ; then
42        # Calling hexdump is (much) faster than hg but doesn't get the local rev
43        r_csetid=$(hexdump -n 20 -e '1/1 "%02x"' ${dirstatefile})
44    else
45        # Settling for a short (but unique!) hash because getting the full
46        # 40-char hash in addition to all the other info we want isn't
47        # available in a single hg invocation
48        hgid_args=( id -i -n -b )
49
50        # Looking for changes is a tad bit slower since the dirstate cache must
51        # first be refreshed before being read
52        zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \
53            "check-for-changes" || hgid_args+=( -r. )
54
55        local HGRCPATH
56        HGRCPATH="/dev/null" ${vcs_comm[cmd]} ${(z)hgid_args} 2> /dev/null \
57            | read -r r_csetid r_lrev r_branch
58    fi
59fi
60
61# If the user doesn't opt to invoke hg we can still get the current branch
62if [[ -z ${r_branch} && -r ${branchfile} ]] ; then
63    r_branch=$(< ${branchfile})
64fi
65
66# If we still don't know the branch it's safe to assume default
67[[ -n ${r_branch} ]] || r_branch="default"
68
69# The working dir has uncommitted-changes if the revision ends with a +
70if [[ $r_lrev[-1] == + ]] ; then
71    hgchanges=1
72
73    r_lrev=${r_lrev%+}
74    r_csetid=${r_csetid%+}
75fi
76
77# This directory only exists during a merge
78[[ -d $mergedir ]] && hgactionstring="merging"
79
80# This file only exists during a rebase
81[[ -e $rebasefile ]] && hgactionstring="rebasing"
82
83
84### Build the current revision display
85[[ -n ${r_csetid} ]] && defrevformat+=( "%h" )
86[[ -n ${r_lrev} ]] && defrevformat+=( "%r" )
87
88zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" \
89    "hgrevformat" revformat || revformat=${(j/:/)defrevformat}
90
91hook_com=( localrev "${r_lrev}" "hash" "${r_csetid}" )
92
93if VCS_INFO_hook 'set-hgrev-format' "${revformat}"; then
94    zformat -f r_lrev "${revformat}" \
95        "r:${hook_com[localrev]}" "h:${hook_com[hash]}"
96else
97    r_lrev=${hook_com[rev-replace]}
98fi
99
100hook_com=()
101
102### Build the branch display
103[[ -n ${r_branch} ]] && defbranchformat+=( "%b" )
104[[ -n ${r_lrev} ]] && defbranchformat+=( "%r" )
105
106zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" \
107    branchformat branchformat || branchformat=${(j/:/)defbranchformat}
108
109hook_com=( branch "${r_branch}" revision "${r_lrev}" )
110
111if VCS_INFO_hook 'set-branch-format' "${branchformat}"; then
112    zformat -f branchformat "${branchformat}" \
113        "b:${hook_com[branch]}" "r:${hook_com[revision]}"
114else
115    branchformat=${hook_com[branch-replace]}
116fi
117
118hook_com=()
119
120### Look for current Bookmarks (this requires knowing the changeset id)
121if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-bookmarks \
122        && [[ -r "${bmfile}" ]] && [[ -n "$r_csetid" ]] ; then
123    while read -r i_bmhash i_bmname ; do
124        # Compare hash in bookmarks file with changeset id
125        [[ $i_bmhash == $r_csetid* ]] && hgbmarks+=( $i_bmname )
126    done < ${bmfile}
127
128    if VCS_INFO_hook 'gen-hg-bookmark-string' "${hgbmarks[@]}"; then
129        hgbmstring=${(j:, :)hgbmarks}
130    else
131        hgbmstring=${hook_com[hg-bookmark-string]}
132    fi
133
134    hook_com=()
135fi
136
137### Look for any applied Mercurial Queue patches
138if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
139        && [[ -d $patchdir ]] ; then
140    if [[ -e $mqstatusfile ]]; then
141        mqpatches=( ${${(f)"$(< "${patchdir}/status")"}/(#s)[a-f0-9]##:/} )
142        mqpatches=( ${(Oa)mqpatches} )
143    fi
144
145    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied \
146            && [[ -r ${mqseriesfile} ]]; then
147        # Okay, here's a little something that assembles a list of unapplied
148        # patches that takes into account if mq-guards are active or not.
149
150        # Collect active guards
151        if [[ -r ${mqguardsfile} ]]; then
152            mqguards=( ${(f)"$(< "${mqguardsfile}")"} )
153            mqguards=( ${(oa)mqguards} )
154        fi
155
156        while read -r i_patch i_patchguards ; do
157            # Skip commented lines
158            [[ ${i_patch} == [[:space:]]#"#"* ]] && continue
159
160            # Keep list of all patches
161            mqseries+=( $i_patch )
162
163            # Separate negative and positive guards to more easily find the
164            # intersection of active guards with patch guards
165            i_patchguards=( ${(s: :)i_patchguards} )
166            i_negguards=( ${${(M)i_patchguards:#*"#-"*}/(#s)\#-/} )
167            i_posguards=( ${${(M)i_patchguards:#*"#+"*}/(#s)\#+/} )
168
169            # Patch with any negative guards is never pushed if guard is active
170            if [[ ${#i_negguards} -gt 0
171                    && ${#${(@M)mqguards:#${(~j,|,)i_negguards}}} -gt 0 ]] ; then
172                continue
173            fi
174
175            # Patch with positive guards is only pushed if guard is active
176            if [[ ${#i_posguards} -gt 0 ]] ; then
177                if [[ ${#${(@M)mqguards:#${(~j,|,)i_posguards}}} -gt 0 ]] ; then
178                    mqunapplied+=( $i_patch )
179                fi
180                continue
181            fi
182
183            # If we made it this far the patch isn't guarded and should be pushed
184            mqunapplied+=( $i_patch )
185        done < ${mqseriesfile}
186    fi
187
188    if VCS_INFO_hook 'gen-applied-string' "${mqpatches[@]}"; then
189        (( ${#mqpatches} )) && applied_string=${mqpatches[1]}
190    else
191        applied_string=${hook_com[applied-string]}
192    fi
193
194    hook_com=()
195
196    if VCS_INFO_hook 'gen-unapplied-string' "${mqunapplied[@]}"; then
197        unapplied_string=${#mqunapplied}
198    else
199        unapplied_string=${hook_com[unapplied-string]}
200    fi
201
202    hook_com=()
203
204    if VCS_INFO_hook 'gen-mqguards-string' "${mqguards[@]}"; then
205        guards_string=${(j:,:)mqguards}
206    else
207        guards_string=${hook_com[guards-string]}
208    fi
209
210    if (( ${#mqpatches} )); then
211        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format \
212            hgmqstring || hgmqstring="%p (%n applied)"
213    else
214        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format \
215            hgmqstring || hgmqstring="no patch applied"
216    fi
217
218    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
219               applied-n ${#mqpatches}     unapplied-n ${#mqunapplied}     all-n ${#mqseries}
220               guards "${guards_string}"   guards-n ${#mqguards} )
221
222    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
223        zformat -f hgmqstring "${hgmqstring}" \
224            "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
225            "n:${#mqpatches}" "c:${#mqunapplied}" "a:${#mqseries}" \
226            "g:${hook_com[guards]}" "G:${#mqguards}"
227    else
228        hgmqstring=${hook_com[patch-replace]}
229    fi
230
231    hook_com=()
232fi
233
234
235### Build the misc string
236hgmisc+=( ${hgmqstring} )
237hgmisc+=( ${hgbmstring} )
238
239backend_misc[patches]="${hgmqstring}"
240backend_misc[bookmarks]="${hgbmstring}"
241
242VCS_INFO_formats "${hgactionstring}" "${branchformat}" "${hgbase}" '' "${hgchanges}" "${r_lrev}" "${(j:;:)hgmisc}"
243return 0
244