about summary refs log tree commit diff
path: root/Functions/VCS_Info
diff options
context:
space:
mode:
authorFrank Terbeck <bewater@users.sourceforge.net>2010-05-10 10:46:48 +0000
committerFrank Terbeck <bewater@users.sourceforge.net>2010-05-10 10:46:48 +0000
commit0501efc54af2d194f952c2968a7aeeb5bac8fdf4 (patch)
tree9d632c46950c434594cc46263e35558018912b20 /Functions/VCS_Info
parent144a06bf958b4e573fd3c78eab30536349d09cff (diff)
downloadzsh-0501efc54af2d194f952c2968a7aeeb5bac8fdf4.tar.gz
zsh-0501efc54af2d194f952c2968a7aeeb5bac8fdf4.tar.xz
zsh-0501efc54af2d194f952c2968a7aeeb5bac8fdf4.zip
Seth House, Simon Ruderich and myself: 27948: various vcs_info changes
Here's a diff-stat:

 Doc/Zsh/contrib.yo                                |  506 ++++++++++++++-------
 Functions/VCS_Info/.distfiles                     |    1 +
 Functions/VCS_Info/Backends/VCS_INFO_detect_hg    |   14 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_git |   35 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_hg  |  295 +++++++++----
 Functions/VCS_Info/VCS_INFO_formats               |   26 +-
 Functions/VCS_Info/VCS_INFO_hook                  |   10 +-
 Functions/VCS_Info/VCS_INFO_quilt                 |  190 ++++++++
 Functions/VCS_Info/vcs_info                       |   30 +-
 Misc/.distfiles                                   |    1 +
 Misc/vcs_info-examples                            |  496 ++++++++++++++++++++
 11 files changed, 1303 insertions(+), 301 deletions(-)

The major changes are vast improvements for the mercurial (hg) backend
(which was done almost entirely by Seth); improved documentation (mostly
done by Simon and again Seth); quilt support (as an addon and stand
alone, see the manual for details); a number of new hooks and a fair
share of bugfixes.
Diffstat (limited to 'Functions/VCS_Info')
-rw-r--r--Functions/VCS_Info/.distfiles1
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_detect_hg14
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_git35
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_hg295
-rw-r--r--Functions/VCS_Info/VCS_INFO_formats26
-rw-r--r--Functions/VCS_Info/VCS_INFO_hook10
-rw-r--r--Functions/VCS_Info/VCS_INFO_quilt190
-rw-r--r--Functions/VCS_Info/vcs_info30
8 files changed, 475 insertions, 126 deletions
diff --git a/Functions/VCS_Info/.distfiles b/Functions/VCS_Info/.distfiles
index cde11b26c..988e7ada4 100644
--- a/Functions/VCS_Info/.distfiles
+++ b/Functions/VCS_Info/.distfiles
@@ -11,6 +11,7 @@ vcs_info_lastmsg
 VCS_INFO_maxexports
 VCS_INFO_nvcsformats
 vcs_info_printsys
+VCS_INFO_quilt
 VCS_INFO_realpath
 VCS_INFO_reposub
 VCS_INFO_set
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_detect_hg b/Functions/VCS_Info/Backends/VCS_INFO_detect_hg
index 36078b7a7..e2866afd5 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_detect_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_detect_hg
@@ -4,9 +4,17 @@
 
 setopt localoptions NO_shwordsplit
 
-[[ $1 == '--flavours' ]] && return 1
+[[ $1 == '--flavours' ]] && { print -l hg-git hg-hgsubversion hg-hgsvn; return 0 }
 
 VCS_INFO_check_com ${vcs_comm[cmd]} || return 1
 vcs_comm[detect_need_file]=store
-VCS_INFO_bydir_detect '.hg'
-return $?
+VCS_INFO_bydir_detect '.hg' || return 1
+
+if [[ -d ${vcs_comm[basedir]}/.hg/svn ]] ; then
+    vcs_comm[overwrite_name]='hg-hgsubversion'
+elif [[ -d ${vcs_comm[basedir]}/.hgsvn ]] ; then
+    vcs_comm[overwrite_name]='hg-hgsvn'
+elif [[ -e ${vcs_comm[basedir]}/.hg/git-mapfile ]] ; then
+    vcs_comm[overwrite_name]='hg-git'
+fi
+return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
index 4018b5d92..778d0610b 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
@@ -3,7 +3,7 @@
 ## Distributed under the same BSD-ish license as zsh itself.
 
 setopt localoptions extendedglob NO_shwordsplit
-local gitdir gitbase gitbranch gitaction gitunstaged gitstaged gitsha1 gitmisc
+local gitdir gitbase gitbranch gitaction gitunstaged gitstaged gitsha1
 local stgitpatch stgitunapplied
 local -xA hook_com
 
@@ -139,33 +139,40 @@ if [[ -d $patchdir ]] ; then
     stgit_unapplied=(${(f)"$(< "${patchdir}/unapplied")"})
     stgit_unapplied=( ${(oa)stgit_applied} )
 
-    if VCS_INFO_hook 'gen-stgit-patch-string' "${stgit_applied[@]}"; then
+    if VCS_INFO_hook 'gen-applied-string' "${stgit_applied[@]}"; then
         if (( ${#stgit_applied} )); then
             stgitpatch=${stgit_applied[1]}
         else
-            stgitpatch="no patch applied"
+            stgitpatch=""
         fi
     else
-        stgitpatch=${hook_com[stgit-patch-string]}
+        stgitpatch=${hook_com[patch-string]}
     fi
-    if VCS_INFO_hook 'gen-stgit-unapplied-string' "${stgit_unapplied[@]}"; then
+    hook_com=()
+    if VCS_INFO_hook 'gen-unapplied-string' "${stgit_unapplied[@]}"; then
         stgitunapplied=${#stgit_unapplied}
     else
-        stgitunapplied=${hook_com[stgit-unapplied-string]}
+        stgitunapplied=${hook_com[unapplied-string]}
     fi
 
-    zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" stgitformat stgitmsg || stgitmsg=" %p (%c)"
-    hook_com=( patch "${stgitpatch}" unapplied "${stgitunapplied}" )
-    if VCS_INFO_hook 'set-stgit-format' "${stgitformat}"; then
-        zformat -f stgitmsg "${stgitmsg}" "p:${hook_com[patch]}" "c:${hook_com[unapplied]}"
-        gitmisc=${stgitmsg}
+    if (( ${#stgit_applied} )); then
+        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format stgitmsg || stgitmsg="%p (%n applied)"
+    else
+        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format stgitmsg || stgitmsg="no patch applied"
+    fi
+    hook_com=( applied "${stgitpatch}"     unapplied "${stgitunapplied}"
+               applied-n ${#stgit_applied} unapplied-n ${#stgit_unapplied} )
+    if VCS_INFO_hook 'set-patch-format' "${stgitmsg}"; then
+        zformat -f stgitmsg "${stgitmsg}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
+                                          "n:${#stgit_applied}" "c:${#stgit_unapplied}"
     else
-        gitmisc=${hook_com[stgit-replace]}
+        stgitmsg=${hook_com[patch-replace]}
     fi
     hook_com=()
 else
-    gitmisc=''
+    stgitmsg=''
 fi
 
-VCS_INFO_formats "${gitaction}" "${gitbranch}" "${gitbase}" "${gitstaged}" "${gitunstaged}" "${gitsha1}" "${gitmisc}"
+backend_misc[patches]="${stgitmsg}"
+VCS_INFO_formats "${gitaction}" "${gitbranch}" "${gitbase}" "${gitstaged}" "${gitunstaged}" "${gitsha1}" "${stgitmsg}"
 return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index 1c103a541..2324bc809 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -1,128 +1,239 @@
 ## vim:ft=zsh
 ## mercurial support by: Frank Terbeck <ft@bewatermyfriend.org>
+## with large contributions by Seth House <seth@eseth.com>
 ## Distributed under the same BSD-ish license as zsh itself.
 
-setopt localoptions NO_shwordsplit
-local file hgbranch hgbranch_name hgbase hghash hglrev hgmqstring \
-    r_branch hgchanges revformat bookmarks r_bmhash r_bmname hgbmstring
-local -i getbookmarks
-local -a hgbm mqpatches hgmisc_args
+setopt localoptions extendedglob NO_shwordsplit
+
+local hgbase bmfile branchfile rebasefile dirstatefile mqseriesfile \
+    mqstatusfile mqguardsfile patchdir mergedir \
+    r_csetid r_lrev r_branch i_bmhash i_bmname \
+    revformat branchformat hgactionstring hgchanges \
+    hgbmstring hgmqstring applied_string unapplied_string guards_string
+
+local -a hgid_args defrevformat defbranchformat \
+    hgbmarks mqpatches mqseries mqguards mqunapplied hgmisc \
+    i_patchguards i_negguards i_posguards
+
 local -xA hook_com
 
 hgbase=${vcs_comm[basedir]}
 rrn=${hgbase:t}
+r_csetid='' # changeset id (long hash)
+r_lrev='' # local revision
+patchdir="${hgbase}/.hg/patches"
+mergedir="${hgbase}/.hg/merge/"
+bmfile="${hgbase}/.hg/bookmarks"
+branchfile="${hgbase}/.hg/branch"
+rebasefile="${hgbase}/.hg/rebasestate"
+dirstatefile="${hgbase}/.hg/dirstate"
+mqstatusfile="${patchdir}/status" # currently applied patches
+mqseriesfile="${patchdir}/series" # all patches
+mqguardsfile="${patchdir}/guards"
+
+# Look for any --flavours
+VCS_INFO_adjust
+
+# Calling the 'hg' program is quite a bit too slow for prompts.
+# Disabled by default anyway, so no harm done.
+if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
+    # Calling hexdump is (much) faster than hg but doesn't get the local rev
+    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple \
+            && ( VCS_INFO_check_com hexdump ) && [[ -r ${dirstatefile} ]] ; then
+        r_csetid=$(hexdump -n 20 -e '1/1 "%02x"' ${dirstatefile})
+    else
+        hgid_args=( --debug id -i -n -b )
+
+        # Looking for changes is a tad bit slower since the dirstate cache must
+        # first be refreshed before being read
+        zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \
+            "check-for-changes" || hgid_args+=( -r. )
+
+        local HGRCPATH
+        HGRCPATH="/dev/null" ${vcs_comm[cmd]} ${(z)hgid_args} \
+            | read -r r_csetid r_lrev r_branch
+    fi
+fi
 
-file="${hgbase}/.hg/branch"
-if [[ -r ${file} ]] ; then
-    hgbranch_name=$(< ${file})
+# If the user doesn't opt to invoke hg we can still get the current branch
+if [[ -z ${r_branch} && -r ${branchfile} ]] ; then
+    r_branch=$(< ${branchfile})
 else
-    hgbranch_name="default"
+    r_branch="default"
 fi
 
-hghash=''
-hglrev=''
-hgbm=()
-bookmarks="${hgbase}/.hg/bookmarks"
-if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
-    # Calling the 'hg' program is quite a bit too slow for prompts.
-    # If there's a way around that, I'd be interested.
-    # Disabled by default anyway, so no harm done.
-    local HGRCPATH
+# The working dir has uncommitted-changes if the revision ends with a +
+if [[ $r_lrev[-1] == + ]] ; then
+    hgchanges=1
 
-    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \
-            "check-for-changes" ; then
+    r_lrev=${r_lrev%+}
+    r_csetid=${r_csetid%+}
+fi
 
-        HGRCPATH="/dev/null" ${vcs_comm[cmd]} id --debug -i -n -b \
-        | read -r hghash hglrev r_branch
+# This directory only exists during a merge
+[[ -d $mergedir ]] && hgactionstring="merging"
 
-        # Are there uncommitted-changes?
-        if [[ $hglrev[-1] == + ]] ; then
-            hgchanges=1
-        fi
+# This file only exists during a rebase
+[[ -e $rebasefile ]] && hgactionstring="rebasing"
+
+
+### Build the current revision display
+[[ -n ${r_csetid} ]] && defrevformat+=( "%h" )
+[[ -n ${r_lrev} ]] && defrevformat+=( "%r" )
+
+zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" \
+    "hgrevformat" revformat || revformat=${(j/:/)defrevformat}
+
+hook_com=( localrev "${r_lrev}" "hash" "${r_csetid}" )
 
-        # Remove uncommitted-changes marker, if any
-        hglrev=${hglrev/+/}
-        hghash=${hghash/+/}
+if VCS_INFO_hook 'set-hgrev-format' "${revformat}"; then
+    zformat -f r_lrev "${revformat}" \
+        "r:${hook_com[localrev]}" "h:${hook_com[hash]}"
+else
+    r_lrev=${hook_com[rev-replace]}
+fi
+
+hook_com=()
+
+### Build the branch display
+[[ -n ${r_branch} ]] && defbranchformat+=( "%b" )
+[[ -n ${r_lrev} ]] && defbranchformat+=( "%r" )
+
+zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" \
+    branchformat branchformat || branchformat=${(j/:/)defbranchformat}
+
+hook_com=( branch "${r_branch}" revision "${r_lrev}" )
+
+if VCS_INFO_hook 'set-branch-format' "${branchformat}"; then
+    zformat -f branchformat "${branchformat}" \
+        "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+else
+    branchformat=${hook_com[branch-replace]}
+fi
+
+hook_com=()
+
+### Look for current Bookmarks (this requires knowing the changeset id)
+if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-bookmarks \
+        && [[ -r "${bmfile}" ]] && [[ -n "$r_csetid" ]] ; then
+    while read -r i_bmhash i_bmname ; do
+        # Compare hash in bookmarks file with changeset id
+        [[ $r_csetid == $i_bmhash ]] && hgbmarks+=( $i_bmname )
+    done < ${bmfile}
+
+    if VCS_INFO_hook 'gen-hg-bookmark-string' "${hgbmarks[@]}"; then
+        hgbmstring=${(j:, :)hgbmarks}
     else
-        HGRCPATH="/dev/null" ${vcs_comm[cmd]} \
-        parents --template="{node} {rev} {branches}\n" \
-        | read -r hghash hglrev r_branch
+        hgbmstring=${hook_com[hg-bookmark-string]}
     fi
 
-    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" "get-bookmarks" \
-            && getbookmarks=1 || getbookmarks=0
+    hook_com=()
+fi
 
-    if (( getbookmarks )) && [[ -r "${bookmarks}" ]] ; then
-        while read -r r_bmhash r_bmname ; do
-            if [[ $hghash == $r_bmhash ]] ; then
-                hgbm=( "$r_bmname" ${hgbm} )
-            fi
-        done < ${bookmarks}
+### Look for any applied Mercurial Queue patches
+if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
+        && [[ -d $patchdir ]] ; then
+    if [[ -e $mqstatusfile ]]; then
+        mqpatches=( ${${(f)"$(< "${patchdir}/status")"}/(#s)[a-f0-9]##:/} )
+        mqpatches=( ${(Oa)mqpatches} )
     fi
 
-    if [[ -n ${hglrev} ]] ; then
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" hgrevformat revformat || revformat="%r:%h"
-        hook_com=( localrev "${hglrev}" "hash" "${hghash}" )
-        if VCS_INFO_hook 'set-hgrev-format' "${revformat}"; then
-            zformat -f hglrev "${revformat}" "r:${hook_com[localrev]}" "h:${hook_com[hash]}"
-        else
-            hglrev=${hook_com[rev-replace]}
+    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied \
+            && [[ -r ${mqseriesfile} ]]; then
+        # Okay, here's a little something that assembles a list of unapplied
+        # patches that takes into account if mq-guards are active or not.
+
+        # Collect active guards
+        if [[ -r ${mqguardsfile} ]]; then
+            mqguards=( ${(f)"$(< "${mqguardsfile}")"} )
+            mqguards=( ${(oa)mqguards} )
         fi
-        hook_com=()
-        if (( getbookmarks )) ; then
-            if VCS_INFO_hook 'gen-hg-bookmark-string' "${hgbm[@]}"; then
-                hgbmstring=${(j.;.)hgbm}
-            else
-                hgbmstring=${hook_com[hg-bookmark-string]}
+
+        while read -r i_patch i_patchguards ; do
+            # Skip commented lines
+            [[ ${i_patch} == [[:space:]]#"#"* ]] && continue
+
+            # Keep list of all patches
+            mqseries+=( $i_patch )
+
+            # Separate negative and positive guards to more easily find the
+            # intersection of active guards with patch guards
+            i_patchguards=( ${(s: :)i_patchguards} )
+            i_negguards=( ${${(M)i_patchguards:#*"#-"*}/(#s)\#-/} )
+            i_posguards=( ${${(M)i_patchguards:#*"#+"*}/(#s)\#+/} )
+
+            # Patch with any negative guards is never pushed if guard is active
+            if [[ ${#i_negguards} -gt 0
+                    && ${#${(@M)mqguards:#${(~j,|,)i_negguards}}} -gt 0 ]] ; then
+                continue
             fi
-            hook_com=()
-        fi
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat hgbranch || hgbranch="%b:%r"
-        hook_com=( branch "${hgbranch_name}" revision "${hglrev}" )
-        if VCS_INFO_hook 'set-branch-format' "${hgbranch}"; then
-            zformat -f hgbranch "${hgbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
-        else
-            hgbranch=${hook_com[branch-replace]}
-        fi
-        hook_com=()
+
+            # Patch with positive guards is only pushed if guard is active
+            if [[ ${#i_posguards} -gt 0 ]] ; then
+                if [[ ${#${(@M)mqguards:#${(~j,|,)i_posguards}}} -gt 0 ]] ; then
+                    mqunapplied+=( $i_patch )
+                fi
+                continue
+            fi
+
+            # If we made it this far the patch isn't guarded and should be pushed
+            mqunapplied+=( $i_patch )
+        done < ${mqseriesfile}
     fi
-else
-    hgbranch="${hgbranch_name}"
-fi
 
-local patchdir=${hgbase}/.hg/patches/
+    if VCS_INFO_hook 'gen-applied-string' "${mqpatches[@]}"; then
+        (( ${#mqpatches} )) && applied_string=${mqpatches[1]}
+    else
+        applied_string=${hook_com[applied-string]}
+    fi
 
-if [[ -d $patchdir ]] ; then
-    local -a mqpatches
-    if [[ -e "${patchdir}/status" ]]; then
-        mqpatches=( ${${(f)"$(< "${patchdir}/status")"}/(#s)[a-f0-9]##:/} )
-        mqpatches=( ${(Oa)mqpatches} )
+    hook_com=()
+
+    if VCS_INFO_hook 'gen-unapplied-string' "${mqunapplied[@]}"; then
+        unapplied_string=${#mqunapplied}
     else
-        mqpatches=( )
+        unapplied_string=${hook_com[unapplied-string]}
     fi
 
-    if VCS_INFO_hook 'gen-mq-patch-string' "${mqpatches[@]}"; then
-        if (( ${#mqpatches} )); then
-            hgmqstring=${mqpatches[1]}
-        else
-            hgmqstring="no patch applied"
-        fi
+    hook_com=()
+
+    if VCS_INFO_hook 'gen-mqguards-string' "${mqguards[@]}"; then
+        guards_string=${(j:,:)mqguards}
     else
-        hgbmstring=${hook_com[hg-mqpatch-string]}
+        guards_string=${hook_com[guards-string]}
     fi
+
+    if (( ${#mqpatches} )); then
+        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format \
+            hgmqstring || hgmqstring="%p (%n applied)"
+    else
+        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format \
+            hgmqstring || hgmqstring="no patch applied"
+    fi
+
+    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
+               applied-n ${#mqpatches}     unapplied-n ${#mqunapplied}
+               guards "${guards_string}"   guards-n ${#mqguards} )
+
+    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
+        zformat -f hgmqstring "${hgmqstring}" \
+            "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
+            "n:${#mqpatches}" "c:${#mqunapplied}" \
+            "g:${hook_com[guards]}" "G:${#mqguards}"
+    else
+        hgmqstring=${hook_com[patch-replace]}
+    fi
+
     hook_com=()
-else
-    hgmqstring=''
 fi
 
-if [[ -z "${hgmqstring}" ]] && [[ -z "${hgbmstring}" ]]; then
-    hgmisc_args=( '' ) # make sure there's at least *one* misc argument
-elif [[ -z "${hgmqstring}" ]]; then
-    hgmisc_args=( "${hgmqstring}" )
-elif [[ -z "${hgbmstring}" ]]; then
-    hgmisc_args=( "${hgbmstring}" )
-else
-    hgmisc_args=( "${hgmqstring}" "${hgbmstring}" )
-fi
-VCS_INFO_formats '' "${hgbranch}" "${hgbase}" '' "${hgchanges}" "${hglrev}" "${hgmisc_args[@]}"
+
+### Build the misc string
+hgmisc+=( ${hgmqstring} )
+hgmisc+=( ${hgbmstring} )
+
+backend_misc[patches]="${hgmqstring}"
+backend_misc[bookmarks]="${hgbmstring}"
+
+VCS_INFO_formats "${hgactionstring}" "${branchformat}" "${hgbase}" '' "${hgchanges}" "${r_lrev}" "${(j:;:)hgmisc}"
 return 0
diff --git a/Functions/VCS_Info/VCS_INFO_formats b/Functions/VCS_Info/VCS_INFO_formats
index db7a8dd48..469efa7d4 100644
--- a/Functions/VCS_Info/VCS_INFO_formats
+++ b/Functions/VCS_Info/VCS_INFO_formats
@@ -22,21 +22,18 @@ hook_com=(
     unstaged_orig "$5"
     revision      "$6"
     revision_orig "$6"
+    misc          "$7"
+    misc_orig     "$7"
     vcs           "${vcs}"
     vcs_orig      "${vcs}"
 )
-shift 6
-i=0
-for tmp in "$@"; do
-    hook_com[misc$((i++))]="${tmp}"
-done
-hook_com[misc]=${(j:,:)argv}
-hook_com[misc_orig]=${hook_com[misc]}
 hook_com[base-name]="${${hook_com[base]}:t}"
 hook_com[base-name_orig]="${hook_com[base_name]}"
 hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
 hook_com[subdir_orig]="${hook_com[subdir]}"
 
+VCS_INFO_hook 'post-backend'
+
 ## description:
 #   action:   a string that signals a certain non-default condition in the
 #             repository (like 'rebase-i' in git). If this in non-empty,
@@ -46,8 +43,7 @@ hook_com[subdir_orig]="${hook_com[subdir]}"
 #   staged:   non-empty if the repository contains staged changes.
 #   unstaged: non-empty if the repository contains unstaged changes.
 #   revision: an identifier of the currently checked out revision.
-#   misc0..N: a set of strings that may contain anything the author likes.
-#             the backends should document what they put in it and when.
+#   misc:     a string that may contain anything the backend author likes.
 #
 # If an argument has no valid value for a given backend, an empty value
 # should be provided. eg:
@@ -71,6 +67,15 @@ if [[ -n ${hook_com[unstaged]} ]] ; then
     [[ -z ${tmp} ]] && hook_com[unstaged]='U' || hook_com[unstaged]=${tmp}
 fi
 
+if [[ ${quiltmode} != 'standalone' ]] && VCS_INFO_hook "pre-addon-quilt"; then
+    local -x REPLY
+    VCS_INFO_quilt addon
+    hook_com[quilt]="${REPLY}"
+    unset REPLY
+elif [[ ${quiltmode} == 'standalone' ]]; then
+    hook_com[quilt]=${hook_com[misc]}
+fi
+
 (( ${#msgs} > maxexports )) && msgs[$(( maxexports + 1 )),-1]=()
 for i in {1..${#msgs}} ; do
     if VCS_INFO_hook "set-message" $(( $i - 1 )) "${msgs[$i]}"; then
@@ -83,6 +88,7 @@ for i in {1..${#msgs}} ; do
                         r:${hook_com[base-name]}        \
                         s:${hook_com[vcs]}              \
                         u:${hook_com[unstaged]}         \
+                        Q:${hook_com[quilt]}            \
                         R:${hook_com[base]}             \
                         S:${hook_com[subdir]}
         msgs[$i]=${msg}
@@ -90,4 +96,6 @@ for i in {1..${#msgs}} ; do
         msgs[$i]=${hook_com[message]}
     fi
 done
+hook_com=()
+backend_misc=()
 return 0
diff --git a/Functions/VCS_Info/VCS_INFO_hook b/Functions/VCS_Info/VCS_INFO_hook
index 71845d808..7274d726f 100644
--- a/Functions/VCS_Info/VCS_INFO_hook
+++ b/Functions/VCS_Info/VCS_INFO_hook
@@ -20,8 +20,11 @@ if (( debug )); then
 fi
 
 zstyle -a "${context}" hooks hooks || return 0
-# protect some internal variables in hooks
-typeset -r vcs rrn usercontext maxexports msgs vcs_comm
+# Protect some internal variables in hooks. The `-g' parameter to
+# typeset does *not* make the parameters global here (they are already
+# "*-local-export). It prevents typeset from creating *new* *local*
+# parameters in this function's scope.
+typeset -g -r vcs rrn usercontext maxexports msgs vcs_comm
 for hook in ${hooks} ; do
     func="+vi-${hook}"
     if (( ${+functions[$func]} == 0 )); then
@@ -29,6 +32,7 @@ for hook in ${hooks} ; do
         continue
     fi
     (( debug )) && printf '  + Running function: "%s"\n' "${func}"
+    true
     ${func} "$@"
     case $? in
         (0)
@@ -38,5 +42,5 @@ for hook in ${hooks} ; do
             ;;
     esac
 done
-typeset +r vcs rrn usercontext maxexports msgs vcs_comm
+typeset -g +r vcs rrn usercontext maxexports msgs vcs_comm
 return $ret
diff --git a/Functions/VCS_Info/VCS_INFO_quilt b/Functions/VCS_Info/VCS_INFO_quilt
new file mode 100644
index 000000000..fc127c23b
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_quilt
@@ -0,0 +1,190 @@
+## vim:ft=zsh:foldmethod=marker
+
+function VCS_INFO_quilt-match() {
+    emulate -L zsh
+    setopt extendedglob
+    local d mode="$1" param="$2"
+    local -a list
+
+    case ${mode} in
+        (assoc) list=( ${(kOP)param} );;
+        (array) : "${foo[@]}" ${(t)foo}; list=( ${(OP)param} );;
+        (*) return 1;;
+    esac
+    for d in "${list[@]}"; do
+        if [[ ${PWD} == ${d%/##}(|/*) ]]; then
+            print "$d"
+            return 0
+        fi
+    done
+    return 1
+}
+
+function VCS_INFO_quilt-standalone-detect() {
+    emulate -L zsh
+    setopt extendedglob
+    local param
+    local -i ret
+
+    zstyle -s "${context}" quilt-standalone param || return 1
+    [[ "${param}" == 'never' ]] && return 1
+    [[ "${param}" == 'always' ]] && return 0
+
+    if (( ${+functions[$param]} )); then
+        ${param}
+        return $?
+    fi
+
+    case ${(Pt)param} in
+    *association*)
+        local m
+        local -A A
+        m="$(VCS_INFO_quilt-match assoc ${param})"
+        A=(${(kvP)param})
+        (( $? == 0 )) && [[ ${A[$m]} == "true" ]] && return 0
+        return 1
+        ;;
+    *array*)
+        typeset -gU ${param}
+        VCS_INFO_quilt-match array ${param} > /dev/null
+        return $?
+        ;;
+    *scalar*)
+        [[ "${(P)param}" == 'always' ]] && return 0
+        [[ "${(P)param}" == 'never' ]] && return 1
+        ;;
+    esac
+    # If nothing hit yet, it just wasn't meant to be.
+    return 1
+}
+
+function VCS_INFO_quilt-dirfind() {
+    # This is a wrapper around VCS_INFO_bydir_detect(). It makes sure
+    # that $vcs_comm[] is unchanged. Currently, changing anything in it
+    # should not be an issue, but this makes sure the code can safely
+    # be called elsewhere, too - if needed.
+    emulate -L zsh
+    setopt extendedglob
+    local dir="$1" file="$2"; shift $#
+    local ret oldfile olddir
+
+    olddir=${vcs_comm[basedir]}
+    vcs_comm[basedir]=''
+    if [[ -n "${file}" ]]; then
+        oldfile=${vcs_comm[detect_need_file]}
+        vcs_comm[detect_need_file]=${file}
+    fi
+    VCS_INFO_bydir_detect ${dir}
+    ret=$?
+    [[ -n "${file}" ]] && vcs_comm[detect_need_file]=${oldfile}
+    printf '%s' ${vcs_comm[basedir]}
+    vcs_comm[basedir]="${olddir}"
+    return ${ret}
+}
+
+function VCS_INFO_quilt() {
+    emulate -L zsh
+    setopt extendedglob
+    local mode="$1"
+    local patches pc tmp qstring root
+    local -i ret
+    local -x context
+    local -a applied unapplied applied_string unapplied_string quiltcommand
+    local -Ax hook_com
+
+    context=":vcs_info:${vcs}.quilt-${mode}:${usercontext}:${rrn}"
+    zstyle -t "${context}" use-quilt || return 1
+
+    case ${mode} in
+    (standalone)
+        VCS_INFO_quilt-standalone-detect || return 1
+        ;;
+    (addon)
+        ;;
+    (*)
+        printf 'Invalid mode: `%s'\''\n' "$1"
+        return 2
+        ;;
+    esac
+
+    zstyle -s "${context}" quilt-patch-dir patches || patches="${QUILT_PATCHES}"
+    if [[ "${patches}" != /* ]]; then
+        tmp=${patches:-patches}
+        patches="$(VCS_INFO_quilt-dirfind "${tmp}")"
+        ret=$?
+        (( ret )) && return ${ret}
+        patches=${patches}/${tmp}
+    else
+        [[ -d ${patches} ]] || return 1
+    fi
+
+    pc="$(VCS_INFO_quilt-dirfind .pc .version)"
+    ret=$?
+    if (( ret == 0 )); then
+        [[ ${quiltmode} == 'standalone' ]] && root=${pc}
+        pc=${pc}/.pc
+        if [[ -e ${pc}/applied-patches ]]; then
+            applied=( ${(f)"$(<$pc/applied-patches)"} )
+            # throw away empty entries
+            applied=( ${applied:#} )
+            applied=( ${(Oa)applied} )
+        else
+            applied=()
+        fi
+    fi
+    if zstyle -t "${context}" get-unapplied; then
+        # This zstyle call needs to be moved further up if `quilt' needs
+        # to be run in more places than this one.
+        zstyle -s "${context}" quiltcommand quiltcommand || quiltcommand='quilt'
+        unapplied=( ${(f)"$(QUILT_PATCHES=$patches $quiltcommand --quiltrc /dev/null unapplied 2> /dev/null)"} )
+        unapplied=( ${unapplied:#} )
+    else
+        unapplied=()
+    fi
+
+    if VCS_INFO_hook 'gen-applied-string' "${applied[@]}"; then
+        if (( ${#applied} )); then
+            applied_string=${applied[1]}
+        else
+            applied_string=""
+        fi
+    else
+        applied_string=${hook_com[applied-string]}
+    fi
+    hook_com=()
+    if VCS_INFO_hook 'gen-unapplied-string' "${unapplied[@]}"; then
+        unapplied_string="${#unapplied}"
+    else
+        unapplied_string=${hook_com[unapplied-string]}
+    fi
+
+    if (( ${#applied} )); then
+        zstyle -s "${context}" patch-format qstring || qstring="%p (%n applied)"
+    else
+        zstyle -s "${context}" nopatch-format qstring || qstring="no patch applied"
+    fi
+    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
+               applied-n ${#applied}       unapplied-n ${#unapplied} )
+    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
+        zformat -f qstring "${qstring}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
+                                        "n:${#applied}" "c:${#unapplied}"
+    else
+        qstring=${hook_com[patch-replace]}
+    fi
+    hook_com=()
+
+    case ${mode} in
+    (standalone)
+        VCS_INFO_formats '' '' "${root}" '' '' '' "${qstring}"
+        VCS_INFO_set
+        ;;
+    (addon)
+        # When VCS_INFO_quilt() is called with "addon" a "local -x REPLY" variable
+        # should be in place. That variable can be unset after it's being used.
+        REPLY="${qstring}"
+        ;;
+    esac
+
+    VCS_INFO_hook 'post-quilt' ${mode} ${patches} ${pc:-\\-nopc-}
+}
+VCS_INFO_quilt "$@"
diff --git a/Functions/VCS_Info/vcs_info b/Functions/VCS_Info/vcs_info
index 7ae11230e..6ce1cd702 100644
--- a/Functions/VCS_Info/vcs_info
+++ b/Functions/VCS_Info/vcs_info
@@ -21,6 +21,7 @@ static_functions=(
     VCS_INFO_hook
     VCS_INFO_maxexports
     VCS_INFO_nvcsformats
+    VCS_INFO_quilt
     VCS_INFO_realpath
     VCS_INFO_reposub
     VCS_INFO_set
@@ -45,12 +46,12 @@ vcs_info () {
     [[ -r . ]] || return 0
 
     local pat
-    local -i found
+    local -i found retval
     local -a enabled disabled dps
-    local -x usercontext vcs rrn LC_MESSAGES
+    local -x usercontext vcs rrn quiltmode LC_MESSAGES
     local -ix maxexports
     local -ax msgs
-    local -Ax vcs_comm
+    local -Ax vcs_comm hook_com backend_misc user_data
 
     LC_MESSAGES=C
     if [[ -n ${LC_ALL} ]]; then
@@ -58,9 +59,18 @@ vcs_info () {
         LANG=${LC_ALL}
         local -x LC_ALL
     fi
-    vcs='-init-'; rrn='-all-'
+    vcs='-init-'; rrn='-all-'; quiltmode='addon'
     usercontext=${1:-default}
 
+    VCS_INFO_hook "start-up"
+    retval=$?
+    if (( retval == 1 )); then
+        return 0
+    elif (( retval == 2 )); then
+        VCS_INFO_set --nvcs
+        return 0
+    fi
+
     zstyle -a ":vcs_info:${vcs}:${usercontext}:${rrn}" "enable" enabled
     (( ${#enabled} == 0 )) && enabled=( all )
 
@@ -99,10 +109,20 @@ vcs_info () {
     done
 
     (( found == 0 )) && {
-        VCS_INFO_set --nvcs
+        vcs='-quilt-'; quiltmode='standalone'
+        VCS_INFO_quilt standalone || VCS_INFO_set --nvcs
         return 0
     }
 
+    VCS_INFO_hook "pre-get-data"
+    retval=$?
+    if (( retval == 1 )); then
+        return 0
+    elif (( retval == 2 )); then
+        VCS_INFO_set --nvcs
+        return 0
+    fi
+
     VCS_INFO_get_data_${vcs} || {
         VCS_INFO_set --nvcs
         return 1