diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | Doc/Zsh/contrib.yo | 506 | ||||
-rw-r--r-- | Functions/VCS_Info/.distfiles | 1 | ||||
-rw-r--r-- | Functions/VCS_Info/Backends/VCS_INFO_detect_hg | 14 | ||||
-rw-r--r-- | Functions/VCS_Info/Backends/VCS_INFO_get_data_git | 35 | ||||
-rw-r--r-- | Functions/VCS_Info/Backends/VCS_INFO_get_data_hg | 295 | ||||
-rw-r--r-- | Functions/VCS_Info/VCS_INFO_formats | 26 | ||||
-rw-r--r-- | Functions/VCS_Info/VCS_INFO_hook | 10 | ||||
-rw-r--r-- | Functions/VCS_Info/VCS_INFO_quilt | 190 | ||||
-rw-r--r-- | Functions/VCS_Info/vcs_info | 30 | ||||
-rw-r--r-- | Misc/.distfiles | 1 | ||||
-rw-r--r-- | Misc/vcs_info-examples | 496 |
12 files changed, 1316 insertions, 302 deletions
diff --git a/ChangeLog b/ChangeLog index cc4c0dea2..cc43bfe29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2010-05-10 Frank Terbeck <ft@bewatermyfriend.org> + + * Seth House, Simon Ruderich and myself: 27948: + Doc/Zsh/contrib.yo, Functions/VCS_Info/.distfiles, Misc/.distfiles, + Misc/vcs_info-examples, Functions/VCS_Info/Backends/VCS_INFO_detect_hg, + Functions/VCS_Info/Backends/VCS_INFO_get_data_git, + Functions/VCS_Info/Backends/VCS_INFO_get_data_hg, + Functions/VCS_Info/VCS_INFO_formats, Functions/VCS_Info/VCS_INFO_hook, + Functions/VCS_Info/VCS_INFO_quilt, Functions/VCS_Info/vcs_info: various + vcs_info changes: mercurial backend improvements, new hooks, quilt + support, documentation improvements and bugfixes + 2010-05-05 Peter Stephenson <pws@csr.com> * users/15031: Completion/Unix/Type/_path_files: fix (still @@ -13105,5 +13117,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.4973 $ +* $Revision: 1.4974 $ ***************************************************** diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 88a052203..10a45e8e1 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -334,16 +334,20 @@ startsitem() sitem(Bazaar (tt(bzr)))(http://bazaar-vcs.org/) sitem(Codeville (tt(cdv)))(http://codeville.org/) sitem(Concurrent Versioning System (tt(cvs)))(http://www.nongnu.org/cvs/) -sitem(tt(darcs))(http://darcs.net/) -sitem(tt(git))(http://git.or.cz/) +sitem(Darcs (tt(darcs)))(http://darcs.net/) +sitem(Git (tt(git)))(http://git-scm.com/) sitem(GNU arch (tt(tla)))(http://www.gnu.org/software/gnu-arch/) -sitem(Mercurial (tt(hg)))(http://selenic.com/mercurial/) +sitem(Mercurial (tt(hg)))(http://mercurial.selenic.com/) sitem(Monotone (tt(mtn)))(http://monotone.ca/) sitem(Perforce (tt(p4)))(http://www.perforce.com/) sitem(Subversion (tt(svn)))(http://subversion.tigris.org/) sitem(SVK (tt(svk)))(http://svk.bestpractical.com/) endsitem() +There is also support for the patch management system tt(quilt) +(http://savannah.nongnu.org/projects/quilt). See tt(Quilt Support) +below for details. + To load var(vcs_info): example(autoload -Uz vcs_info) @@ -364,7 +368,7 @@ zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat '%b%F{1}:%F{3}%r' precmd () { vcs_info } PS1='%F{5}[%F{2}%n%F{5}] %F{3}%3~ ${vcs_info_msg_0_}%f%# ') -Obviously, the last two lines are there for demonstration: You need to +Obviously, the last two lines are there for demonstration. You need to call var(vcs_info) from your var(precmd) function. Once that is done you need a tt(single quoted) var('${vcs_info_msg_0_}') in your prompt. @@ -391,7 +395,10 @@ tla ## are enabled and disabled with their master [git-svn -> git]) ## they *can* be used in contexts: ':vcs_info:git-svn:*'. git-p4 -git-svn) +git-svn +hg-git +hg-hgsubversion +hg-hgsvn) You may not want all of these because there is no point in running the code to detect systems you do not use. So there is a way to disable @@ -409,6 +416,7 @@ var(enable) style - if you used that) marked as disabled by a hash sign. That means the detection of these systems is skipped tt(completely). No wasted time there. + subsect(Configuration) The var(vcs_info) feature can be configured via var(zstyle). @@ -418,9 +426,9 @@ example(:vcs_info:<vcs-string>:<user-context>:<repo-root-name>) startitem() item(tt(<vcs-string>))( -is one of: git, git-svn, git-p4, hg, darcs, bzr, -cdv, mtn, svn, cvs, svk, tla or p4. When hooks are active the hooks name -is added after a `+'. (See tt(Hooks in vcs_info) below.) +is one of: git, git-svn, git-p4, hg, hg-git, hg-hgsubversion, hg-hgsvn, +darcs, bzr, cdv, mtn, svn, cvs, svk, tla or p4. When hooks are active the +hooks name is added after a `+'. (See tt(Hooks in vcs_info) below.) ) item(tt(<user-context>))( is a freely configurable string, assignable by @@ -430,13 +438,13 @@ below). item(tt(<repo-root-name>))( is the name of a repository in which you want a style to match. So, if you want a setting specific to var(/usr/src/zsh), -with that being a cvs checkout, you can set tt(<repo-root-name>) to +with that being a CVS checkout, you can set tt(<repo-root-name>) to var(zsh) to make it so. ) enditem() There are three special values for tt(<vcs-string>): The first is named -var(-init-), that is in effect as long as there was no decision what vcs +var(-init-), that is in effect as long as there was no decision what VCS backend to use. The second is var(-preinit-); it is used tt(before) var(vcs_info) is run, when initializing the data exporting variables. The third special value is var(formats) and is used by the tt(vcs_info_lastmsg) @@ -469,36 +477,28 @@ A list of formats, used when actionformats is not used kindex(actionformats) item(tt(actionformats))( A list of formats, used if a there is a special -action going on in your current repository; (like an interactive rebase or -a merge conflict). +action going on in your current repository; like an interactive rebase or +a merge conflict. ) kindex(branchformat) item(tt(branchformat))( Some backends replace var(%b) in the formats and actionformats styles above, not only by a branch name but also by a -revision number. This style lets you modify how that string should look -like. +revision number. This style lets you modify how that string should look. ) kindex(nvcsformats) item(tt(nvcsformats))( -These "formats" are exported, when we didn't detect -a version control system for the current directory. This is useful, if you +These "formats" are exported when we didn't detect +a version control system for the current directory. This is useful if you want var(vcs_info) to completely take over the generation of your prompt. You would do something like tt(PS1='${vcs_info_msg_0_}') to accomplish that. ) -kindex(stgitformat) -item(tt(stgitformat))( -The tt(git) backend replaces var(%m) in the formats and -actionformats styles with tt(stgit)-specific information for -tt(stgit)-initialized branches. This style lets you modify how that string -should look like. -) kindex(hgrevformat) item(tt(hgrevformat))( tt(hg) uses both a hash and a revision number to reference a specific changeset in a repository. With this style you can format the revision -string (see var(branchformat)) to include either of both. It's only +string (see var(branchformat)) to include either or both. It's only useful when var(get-revision) is true. ) kindex(max-exports) @@ -508,16 +508,15 @@ var(vcs_info_msg_*_) variables var(vcs_info) will export. ) kindex(enable) item(tt(enable))( -A list of backends you want to use. Checked in the -var(-init-) context. If this list contains an item called tt(NONE) no -backend is used at all and var(vcs_info) will do nothing. If this list -contains tt(ALL) var(vcs_info) will use all backends known to it. Only with -tt(ALL) in tt(enable), the tt(disable) style has any effect. tt(ALL) and -tt(NONE) are actually tested case insensitively. +A list of backends you want to use. Checked in the var(-init-) context. If +this list contains an item called tt(NONE) no backend is used at all and +var(vcs_info) will do nothing. If this list contains tt(ALL) var(vcs_info) +will use all known backends. Only with tt(ALL) in tt(enable) will the +tt(disable) style have any effect. tt(ALL) and tt(NONE) are case insensitive. ) kindex(disable) item(tt(disable))( -A list of VCSs, you don't want var(vcs_info) to test for +A list of VCSs you don't want var(vcs_info) to test for repositories (checked in the var(-init-) context, too). Only used if tt(enable) contains tt(ALL). ) @@ -531,19 +530,37 @@ Say, tt(~/.zsh) is a directory under version control, in which you do not want var(vcs_info) to be active, do: example(zstyle ':vcs_info:*' disable-patterns "$HOME/.zsh+LPAR()|/*+RPAR()") ) +kindex(use-quilt) +item(tt(use-quilt))( +If enabled, the tt(quilt) support code is active in `addon' mode. +See tt(Quilt Support) for details. +) +kindex(quilt-standalone) +item(tt(quilt-standalone))( +If enabled, `standalone' mode detection is attempted if no VCS is active +in a given directory. See tt(Quilt Support) for details. +) +kindex(quilt-patch-dir) +item(tt(quilt-patch-dir))( +Overwrite the value of the var($QUILT_PATCHES) environment variable. See +tt(Quilt Support) for details. +) +kindex(quiltcommand) +item(tt(quiltcommand))( +When tt(quilt) itself is called in quilt support the value of this style +is used as the command name. +) kindex(check-for-changes) item(tt(check-for-changes))( -If enabled, this style causes the tt(%c) and tt(%u) format escapes to be filled -with information. The strings filled into these escapes can be controlled via -the var(stagedstr) and var(unstagedstr) styles. The only backends that -currently support this option are tt(git) and tt(hg) (tt(hg) only supports -unstaged). - -Note, that the actions taken if this style is enabled are potentially expensive -(read: they take time, depending on how big the current repository is). -Therefore, it is disabled by default. In order to use this style with -the tt(hg) backend you must also use the var(get-revision) style to avoid -having to start the interpreter more than once. +If enabled, this style causes the tt(%c) and tt(%u) format escapes to show +when the working directory has uncommitted changes. The strings displayed by +these escapes can be controlled via the var(stagedstr) and var(unstagedstr) +styles. The only backends that currently support this option are tt(git) and +tt(hg) (tt(hg) only supports unstaged). + +Note, the actions taken if this style is enabled are potentially expensive +(read: they may be slow, depending on how big the current repository is). +Therefore, it is disabled by default. ) kindex(stagedstr) item(tt(stagedstr))( @@ -552,25 +569,25 @@ the repository. ) kindex(unstagedstr) item(tt(unstagedstr))( -This string will be used in the tt(%u) escape if there are unstaged changes in -the repository. +This string will be used in the tt(%u) escape if there are unstaged changes +in the repository. ) kindex(command) item(tt(command))( This style causes var(vcs_info) to use the supplied string as the command -to use as the vcs's binary. Note, that setting this in ':vcs_info:*' is +to use as the VCS's binary. Note, that setting this in ':vcs_info:*' is not a good idea. If the value of this style is empty (which is the default), the used binary -name is the name of the backend in use (e.g. var(svn) is used in a var(svn) +name is the name of the backend in use (e.g. var(svn) is used in an var(svn) repository). The var(repo-root-name) part in the context is always the default tt(-all-) when this style is looked up. For example, this style can be used to use binaries from non-default -installation directories. Assume, var(git) is installed in /usr/bin, but -your sysadmin installed a newer version in /usr/bin/local. Now, instead of +installation directories. Assume, var(git) is installed in /usr/bin but +your sysadmin installed a newer version in /usr/bin/local. Instead of changing the order of your tt($PATH) parameter, you can do this: example(zstyle ':vcs_info:git:*:-all-' command /usr/local/bin/git) ) @@ -581,8 +598,8 @@ contact the Perforce server to find out if a directory is managed by Perforce. This is the only reliable way of doing this, but runs the risk of a delay if the server name cannot be found. If the server (more specifically, the var(host)tt(:)var(port) pair describing the -server) cannot be contacted its name is put into the associative array -tt(vcs_info_p4_dead_servers) and not contacted again during the session +server) cannot be contacted, its name is put into the associative array +tt(vcs_info_p4_dead_servers) and is not contacted again during the session until it is removed by hand. If you do not set this style, the tt(p4) backend is only usable if you have set the environment variable tt(P4CONFIG) to a file name and have corresponding files in the root @@ -594,8 +611,10 @@ item(tt(use-simple))( If there are two different ways of gathering information, you can select the simpler one by setting this style to true; the default is to use the not-that-simple code, which is potentially a lot -slower but might be more accurate in all possible cases. This style is only -used by the tt(bzr) backend. +slower but might be more accurate in all possible cases. This style is +used by the tt(bzr) and tt(hg) backends. In the case of tt(hg) it will invoke +the external hexdump program to parse the binary dirstate cache file; this +method will not return the local revision number. ) kindex(get-revision) item(tt(get-revision))( @@ -604,15 +623,17 @@ a repository's work tree (currently for the tt(git) and tt(hg) backends, where this kind of information is not always vital). For tt(git), the hash value of the currently checked out commit is available via the tt(%i) expansion. With tt(hg), the local revision number and the corresponding -global hash are available via tt(%i); in addition, the topmost -applied tt(mq) patch and bookmarks are available via tt(%m). -If this style is set in the tt(hg) context, the backend supports the -var(branchformat) style. +global hash are available via tt(%i). +) +kindex(get-mq) +item(tt(get-mq))( +If set to true, the tt(hg) backend will look for a Mercurial Queue (tt(mq)) +patch directory. Information will be available via the `tt(%m)' replacement. ) kindex(get-bookmarks) item(tt(get-bookmarks))( If set to true, the tt(hg) backend will try to get a list of current -bookmarks. They will be available in via the `tt(%m)' replacement. +bookmarks. They will be available via the `tt(%m)' replacement. ) kindex(use-prompt-escapes) item(tt(use-prompt-escapes))( @@ -622,12 +643,12 @@ tt(vcs_info_lastmsg).) ) kindex(debug) item(tt(debug))( -Enable debugging output, to track possible problems. Currently this style -is only used by tt(vcs_info)'s hooks system. +Enable debugging output to track possible problems. Currently this style +is only used by var(vcs_info)'s hooks system. ) kindex(hooks) item(tt(hooks))( -A list style, that defines hook-function names. See tt(Hooks in vcs_info) +A list style that defines hook-function names. See tt(Hooks in vcs_info) below for details. ) enditem() @@ -639,7 +660,6 @@ sitem(tt(formats))(" (%s)-[%b]-") sitem(tt(actionformats))(" (%s)-[%b|%a]-") sitem(tt(branchformat))("%b:%r" (for bzr, svn, svk and hg)) sitem(tt(nvcsformats))("") -sitem(tt(stgitformat))(" %p (%c)") sitem(tt(hgrevformat))("%r:%h") sitem(tt(max-exports))(2) sitem(tt(enable))(ALL) @@ -652,62 +672,70 @@ sitem(tt(command))((empty string)) sitem(tt(use-server))(false) sitem(tt(use-simple))(false) sitem(tt(get-revision))(false) +sitem(tt(get-mq))(true) sitem(tt(get-bookmarks))(false) sitem(tt(use-prompt-escapes))(true) sitem(tt(debug))(false) sitem(tt(hooks))((empty list)) +sitem(tt(use-quilt))(false) +sitem(tt(quilt-standalone))(false) +sitem(tt(quilt-patch-dir))(empty - use var($QUILT_PATCHES)) +sitem(tt(quiltcommand))(quilt) endsitem() -In normal tt(formats) and tt(actionformats), the following replacements are +In normal tt(formats) and tt(actionformats) the following replacements are done: startsitem() -sitem(tt(%s))(The vcs in use (git, hg, svn etc.)) +sitem(tt(%s))(The VCS in use (git, hg, svn, etc.).) sitem(tt(%b))(Information about the current branch.) -sitem(tt(%a))(An identifier, that describes the action. Only makes sense in -actionformats.) +sitem(tt(%a))(An identifier that describes the action. Only makes sense in +var(actionformats).) sitem(tt(%i))(The current revision number or identifier. For tt(hg) the var(hgrevformat) style may be used to customize the output.) sitem(tt(%c))(The string from the var(stagedstr) style if there are staged changes in the repository.) -sitem(tt(%u))(The string from the var(unstagedstr) style if there are unstaged -changes in the repository.) -sitem(tt(%R))(base directory of the repository.) -sitem(tt(%r))(repository name. If tt(%R) is var(/foo/bar/repoXY), tt(%r) is -var(repoXY).) -sitem(tt(%S))(subdirectory within a repository. If tt($PWD) is +sitem(tt(%u))(The string from the var(unstagedstr) style if there are +unstaged changes in the repository.) +sitem(tt(%R))(The base directory of the repository.) +sitem(tt(%r))(The repository name. If tt(%R) is var(/foo/bar/repoXY), tt(%r) +is var(repoXY).) +sitem(tt(%S))(A subdirectory within a repository. If tt($PWD) is var(/foo/bar/repoXY/beer/tasty), tt(%S) is var(beer/tasty).) -sitem(tt(%m))(A "misc" replacement. It is at the discretion of the backend -to decide what this replacement expands to. It is currently used by -the tt(hg) and tt(git) backends. The tt(hg) backend replaces tt(%m) with the -topmost tt(mq) patch applied (qtop) and a list of any current bookmarks. The -tt(git) backend replaces it with the string from the var(stgitformat) -style.) +sitem(tt(%m))(A "misc" replacement. It is at the discretion of the backend to +decide what this replacement expands to. It is currently used by the tt(hg) +and tt(git) backends to display patch information from the tt(mq) and +tt(stgit) extensions.) endsitem() In tt(branchformat) these replacements are done: startsitem() -sitem(tt(%b))(the branch name) -sitem(tt(%r))(the current revision number or the var(hgrevformat) style for tt(hg)) +sitem(tt(%b))(The branch name.) +sitem(tt(%r))(The current revision number or the var(hgrevformat) style for +tt(hg).) endsitem() -In tt(stgitformat) these replacements are done: +In tt(hgrevformat) these replacements are done: startsitem() -sitem(tt(%p))(the name of the patch currently on top of the stack) -sitem(tt(%c))(the number of unapplied patches) +sitem(tt(%r))(The current local revision number.) +sitem(tt(%h))(The current 40-character changeset ID hash identifier.) endsitem() -In tt(hgrevformat) these replacements are done: +In tt(patch-format) and tt(nopatch-format) these replacements are done: startsitem() -sitem(tt(%r))(the current revision number) -sitem(tt(%h))(the hash identifier for the current resivion in short form) +sitem(tt(%p))(The name of the top-most applied patch.) +sitem(tt(%u))(The number of unapplied patches.) +sitem(tt(%n))(The number of applied patches.) +sitem(tt(%c))(The number of unapplied patches.) +sitem(tt(%g))(The names of active tt(mq) guards (tt(hg) backend).) +sitem(tt(%G))(The number of active tt(mq) guards (tt(hg) backend).) endsitem() -Not all vcs backends have to support all replacements. For tt(nvcsformats) -no replacements are performed at all. It is just a string. +Not all VCS backends have to support all replacements. For tt(nvcsformats) +no replacements are performed at all, it is just a string. subsect(Oddities) @@ -720,21 +748,110 @@ cannot be easily avoided. Luckily we do not clash with a lot of prompt expansions and this only needs to be done for those. -subsect(Function descriptions (public API)) +subsect(Quilt Support) + +tt(Quilt) is not a version control system, therefore this is not implemented +as a backend. It can help keeping track of a series of patches. People use it +to keep a set of changes they want to use on top of software packages (which +is tightly integrated into the package build process - the Debian project +does this for a large number of packages). Quilt can also help individual +developers keep track of their own patches on top of real version control +systems. + +The var(vcs_info) integration tries to support both ways of using quilt by +having two slightly different modes of operation: `addon' mode and +`standalone' mode). + +For `addon' mode to become active var(vcs_info) must have already detected a +real version control system controlling the directory. If that is the case, +a directory that holds quilt's patches needs to be found. That directory is +configurable via the var(`QUILT_PATCHES') environment variable. If that +variable exists its value is used, otherwise the value tt(`patches') is +assumed. The value from var($QUILT_PATCHES) can be overwritten using the +tt(`quilt-patches') style. (Note: you can use var(vcs_info) to keep the value +of var($QUILT_PATCHES) correct all the time via the tt(post-quilt) hook). + +When the directory in question is found, quilt is assumed to be active. To +gather more information, var(vcs_info) looks for a directory called `.pc'; +Quilt uses that directory to track its current state. If this directory does +not exist we know that quilt has not done anything to the working directory +(read: no patches have been applied yet). + +If patches are applied, var(vcs_info) will try to find out which. If you want +to know which patches of a series are not yet applied, you need to activate +the tt(get-unapplied) style in the appropriate context. + +var(vcs_info) allows for very detailed control over how the gathered +information is presented (see the below sections, tt(Styles) and tt(Hooks in +vcs_info)), all of which are documented below. Note there are a number of +other patch tracking systems that work on top of a certain version control +system (like tt(stgit) for tt(git), or tt(mq) for tt(hg)); the configuration +for systems like that are generally configured the same way as the tt(quilt) +support. + +If the tt(quilt) support is working in `addon' mode, the produced string is +available as a simple format replacement (var(%Q) to be precise), which can +be used in tt(formats) and tt(actionformats); see below for details). + +If, on the other hand, the support code is working in `standalone' mode, +var(vcs_info) will pretend as if tt(quilt) were an actual version control +system. That means that the version control system identifier (which +otherwise would be something like `svn' or `cvs') will be set to +`tt(-quilt-)'. This has implications on the used style context where this +identifier is the second element. var(vcs_info) will have filled in a proper +value for the "repository's" root directory and the string containing the +information about quilt's state will be available as the `misc' replacement +(and var(%Q) for compatibility with `addon' mode. + +What is left to discuss is how `standalone' mode is detected. The detection +itself is a series of searches for directories. You can have this detection +enabled all the time in every directory that is not otherwise under version +control. If you know there is only a limited set of trees where you would +like var(vcs_info) to try and look for Quilt in `standalone' mode to minimise +the amount of searching on every call to var(vcs_info), there are a number of +ways to do that: + +Essentially, `standalone' mode detection is controlled by a style called +`tt(quilt-standalone)'. It is a string style and its value can have different +effects. The simplest values are: `tt(always)' to run detection every time +var(vcs_info) is run, and `tt(never)' to turn the detection off entirely. + +If the value of tt(quilt-standalone) is something else, it is interpreted +differently. If the value is the name of a scalar variable the value of that +variable is checked and that value is used in the same `always'/`never' way +as described above. + +If the value of tt(quilt-standalone) is an array, the elements of that array +are used as directory names under which you want the detection to be active. + +If tt(quilt-standalone) is an associative array, the keys are taken as +directory names under which you want the detection to be active, but only if +the corresponding value is the string `tt(true)'. + +Last, but not least, if the value of tt(quilt-standalone) is the name of a +function, the function is called without arguments and the return value +decides whether detection should be active. A `0' return value is true; a +non-zero return value is interpreted as false. + +Note, if there is both a function and a variable by the name of +tt(quilt-standalone), the function will take precedence. + + +subsect(Function Descriptions (Public API)) startitem() findex(vcs_info) item(tt(vcs_info) [var(user-context)])( -The main function, that runs all -backends and assembles all data into var(${vcs_info_msg_*_}). This is the -function you want to call from tt(precmd) if you want to include up-to-date -information in your prompt (see Variable description below). If an argument -is given, that string will be used instead of tt(default) in the -user-context field of the style context. +The main function, that runs all backends and assembles all data into +var(${vcs_info_msg_*_}). This is the function you want to call from +tt(precmd) if you want to include up-to-date information in your prompt (see +tt(Variable description) below). If an argument is given, that string will be +used instead of tt(default) in the tt(user-context) field of the style +context. ) item(tt(vcs_info_lastmsg))( Outputs the last var(${vcs_info_msg_*_}) value. -Takes into account the value of the use-prompt-escapes style in +Takes into account the value of the tt(use-prompt-escapes) style in var(':vcs_info:formats:command:-all-'). It also only prints tt(max-exports) values. ) @@ -753,14 +870,14 @@ enditem() All functions named VCS_INFO_* are for internal use only. -subsect(Variable description) +subsect(Variable Description) startitem() item(tt(${vcs_info_msg_N_}) (Note the trailing underscore)) ( -Where var(N) is an integer, eg: var(vcs_info_msg_0_) These variables +Where var(N) is an integer, e.g., var(vcs_info_msg_0_). These variables are the storage for the informational message the last var(vcs_info) call -has assembled. These are strongly connected to the formats, +has assembled. These are strongly connected to the tt(formats), tt(actionformats) and tt(nvcsformats) styles described above. Those styles are lists. The first member of that list gets expanded into var(${vcs_info_msg_0_}), the second into var(${vcs_info_msg_1_}) @@ -773,7 +890,7 @@ All variables named VCS_INFO_* are for internal use only. subsect(Hooks in vcs_info) -Hooks are places in tt(vcs_info) where you can run your own code. That +Hooks are places in var(vcs_info) where you can run your own code. That code can communicate with the code that called it and through that, change the system's behaviour. @@ -799,114 +916,152 @@ When you register more than one function to a hook, all functions are executed one after another until one function returns non-zero or until all functions have been called. -There are a number of variables, that are special in hook contexts: +You may pass data between functions via an associative array, tt(user_data). +For example: +example( ++vi-git-myfirsthook+LPAR()RPAR(){ + user_data[myval]=$myval +} ++vi-git-mysecondhook+LPAR()RPAR(){ + # do something with ${user_data[myval]} +}) + +There are a number of variables that are special in hook contexts: startitem() item(tt(ret))( -The return value, that the hooks system will return to the caller. The +The return value that the hooks system will return to the caller. The default is an integer `zero'. If and how a changed tt(ret) value changes -the execution of the caller depends on the specific hook. See the hook's +the execution of the caller depends on the specific hook. See the hook documentation below for details. ) item(tt(hook_com))( -An associated array, which is used for bidirectional communication from +An associated array which is used for bidirectional communication from the caller to hook functions. The used keys depend on the specific hook. ) item(tt(context))( The active context of the hook. Functions that wish to change this variable should make it local scope first. ) +item(tt(vcs))( +The current VCS after it was detected. The same values as in the +enable/disable style are used. Available in all hooks except tt(start-up). +) enditem() Finally, the full list of currently available hooks: startitem() +item(tt(start-up))( +Called after starting var(vcs_info) but before the VCS in this directory is +determined. It can be used to deactivate var(vcs_info) temporarily if +necessary. When tt(ret) is set to var(1), var(vcs_info) aborts and does +nothing; when set to var(2), var(vcs_info) sets up everything as if no +version control were active and exits. +) +item(tt(pre-get-data))( +Same as tt(start-up) but after the VCS was detected. +) item(tt(gen-hg-bookmark-string))( -Called in the Mercurial backend (the tt(get-revision) and tt(get-bookmarks) -styles must be active) when a bookmark string is generated. +Called in the Mercurial backend when a bookmark string is generated; the +tt(get-revision) and tt(get-bookmarks) styles must be true. -This hook gets the names of the Mercurial bookmarks, that -tt(vcs_info) collected from `hg'. +This hook gets the names of the Mercurial bookmarks that +var(vcs_info) collected from `hg'. When setting tt(ret) to non-zero, the string in -tt(${hook_com[hg-bookmark-string]}) will be used as the -`tt(misc1)' replacement in the variables set by tt(vcs_info). +tt(${hook_com[hg-bookmark-string]}) will be used in the var(%m) escape in +tt(formats) and tt(actionformats) and will be availabe in the global +var(backend_misc) array as tt(${backend_misc[bookmarks]}). ) -item(tt(gen-mq-patch-string))( -Called in the Mercurial backend when a mq-patch string is generated. That -only happens if a tt(.hg/patches) directory exists in the repository. +item(tt(gen-applied-string))( +Called in the tt(git) (with tt(stgit)), and tt(hg) (with tt(mq)) backends +and in tt(quilt) support when the var(applied-string) is generated; the +tt(use-quilt) zstyle must be true for tt(quilt) (the tt(mq) and tt(stgit) +backends are active by default). -This hook gets the names of all applied mq patches which tt(vcs_info) -collected so far in the opposite order, which mean that the first argument -is the top-most patch and so forth. +This hook gets the names of all applied patches which var(vcs_info) collected +so far in the opposite order, which means that the first argument is the +top-most patch and so forth. When setting tt(ret) to non-zero, the string in -tt(${hook_com[hg-mqpatch-string]}) will be used as the -`tt(misc0)' replacement in the variables set by tt(vcs_info). +tt(${hook_com[applied-string]}) will be used in the var(%m) escape in +tt(formats) and tt(actionformats); it will be available in the global +var(backend_misc) array as tt($backend_misc[patches]}); and it will be +available as var(%p) in the tt(patch-format) and tt(nopatch-format) styles. ) -item(tt(gen-stgit-patch-string))( -Called in the git backend when a stgit-patch string is generated. That -only happens if stgit is in use in the repository. +item(tt(gen-unapplied-string))( +Called in the tt(git) (with tt(stgit)), and tt(hg) (with tt(mq)) backend +and in tt(quilt) support when the var(unapplied-string) is generated; the +tt(get-unapplied) style must be true. -This hook gets the names of all applied stgit patches which tt(vcs_info) -collected so far in the opposite order, which mean that the first argument -is the top-most patch and so forth. +This hook gets the names of all unapplied patches which var(vcs_info) +collected so far in the opposite order, which mean that the first argument is +the patch next-in-line to be applied and so forth. When setting tt(ret) to non-zero, the string in -tt(${hook_com[stgit-patch-string]}) will be used as the -`tt(misc0)' replacement in the variables set by tt(vcs_info). +tt(${hook_com[unapplied-string]}) will be available as var(%u) in the +tt(patch-format) and tt(nopatch-format) styles. ) -item(tt(gen-stgit-unapplied-string))( -Called in the git backend when a stgit-unapplied string is generated. That -only happens if stgit is in use in the repository. +item(tt(gen-mqguards-string))( +Called in the tt(hg) backend when tt(guards-string) is generated; the +tt(get-mq) style must be true (default). -This hook gets the names of all unapplied stgit patches which tt(vcs_info) -collected so far. +This hook gets the names of any active tt(mq) guards. When setting tt(ret) to non-zero, the string in -tt(${hook_com[stgit-unapplied-string]}) will be used as the -`tt(misc0)' replacement in the variables set by tt(vcs_info). +tt(${hook_com[guards-string]}) will be used in the var(%g) escape in the +tt(patch-format) and tt(nopatch-format) styles. +) +item(tt(post-quilt))( +Called after the tt(quilt) support is done. The following information +is passed as arguments to the hook: 1. the quilt-support mode (`addon' or +`standalone'); 2. the directory that contains the patch series; 3. the +directory that holds quilt's status information (the `.pc' directory) or +the string tt("-nopc-") if that directory wasn't found. + +The `hook_com' parameter is not used. ) item(tt(set-branch-format))( -Called before a `tt(branchformat)' is set. The only argument to the +Called before `tt(branchformat)' is set. The only argument to the hook is the format that is configured at this point. The `tt(hook_com)' keys considered are `tt(branch)' and `tt(revision)'. -They are set to the values figured out so far by tt(vcs_info) and any +They are set to the values figured out so far by var(vcs_info) and any change will be used directly when the actual replacement is done. If tt(ret) is set to to non-zero, the string in tt(${hook_com[branch-replace]}) will be used unchanged as the -`tt(%b)' replacement in the variables set by tt(vcs_info). +`tt(%b)' replacement in the variables set by var(vcs_info). ) item(tt(set-hgrev-format))( Called before a `tt(hgrevformat)' is set. The only argument to the hook is the format that is configured at this point. -The `tt(hook_com)' keys considered are `tt(hash)' and `tt(localref)'. -They are set to the values figured out so far by tt(vcs_info) and any +The `tt(hook_com)' keys considered are `tt(hash)' and `tt(localrev)'. +They are set to the values figured out so far by var(vcs_info) and any change will be used directly when the actual replacement is done. If tt(ret) is set to to non-zero, the string in tt(${hook_com[rev-replace]}) will be used unchanged as the -`tt(%i)' replacement in the variables set by tt(vcs_info). +`tt(%i)' replacement in the variables set by var(vcs_info). ) item(tt(set-message))( Called each time before a `tt(vcs_info_msg_N_)' message is set. It takes two arguments; the first being the `N' in the message -variable name, the second is the currently configured format or -actionformat. +variable name, the second is the currently configured tt(formats) or +tt(actionformats). There are a number of `tt(hook_com)' keys, that are used here: `tt(action)', `tt(branch)', `tt(base)', `tt(base-name)', `tt(subdir)', `tt(staged)', `tt(unstaged)', `tt(revision)', `tt(misc)', `tt(vcs)' and one `tt(miscN)' entry for each backend-specific data field (tt(N) starting at zero). They are set to the values figured out so far by -tt(vcs_info) and any change will be used directly when the actual +var(vcs_info) and any change will be used directly when the actual replacement is done. Since this hook is triggered multiple times (once for each configured -format or actionformat), each of the `tt(hook_com)' keys mentioned +tt(formats) or tt(actionformats)), each of the `tt(hook_com)' keys mentioned above (except for the tt(miscN) entries) has an `tt(_orig)' counterpart, so even if you changed a value to your liking you can still get the original value in the next run. Changing the `tt(_orig)' values is @@ -914,28 +1069,16 @@ probably not a good idea. If tt(ret) is set to to non-zero, the string in tt(${hook_com[message]}) will be used unchanged as the message by -tt(vcs_info). -) -item(tt(set-stgit-format))( -Called before a `tt(stgitformat)' is set. The only argument to the -hook is the format that is configured at this point. - -The `tt(hook_com)' keys considered are `tt(patch)' and `tt(unapplied)'. -They are set to the values figured out so far by tt(vcs_info) and any -change will be used directly when the actual replacement is done. - -If tt(ret) is set to to non-zero, the string in -tt(${hook_com[stgit-replace]}) will be used unchanged as the -`tt(misc0)' replacement in the variables set by tt(vcs_info). +var(vcs_info). ) -enditem() If all of this sounds rather confusing, take a look at the tt(Examples) -section below. It contains some explanatory code. +section below and also in the Misc/vcs_info-examples file in the Zsh source. +They contain some explanatory code. subsect(Examples) -Don't use tt(vcs_info) at all (even though it's in your prompt): +Don't use var(vcs_info) at all (even though it's in your prompt): example(zstyle ':vcs_info:*' enable NONE) Disable the backends for tt(bzr) and tt(svk): @@ -948,6 +1091,17 @@ Provide a special formats for tt(git): example(zstyle ':vcs_info:git:*' formats ' GIT, BABY! [%b]' zstyle ':vcs_info:git:*' actionformats ' GIT ACTION! [%b|%a]') +All tt(%x) expansion in all sorts of formats ("formats", "actionformats", +branchformat, you name it) are done using the `tt(zformat)' builtin from +the `tt(zsh/zutil)' module. That means you can do everything with these +tt(%x) items what zformat supports. In particular, if you want something +that is really long to have a fixed width, like a hash in a mercurial +branchformat, you can do this: tt(%12.12i). That'll shrink the 40 character +hash to its 12 leading characters. The form is actually +`tt(%)var(min)tt(.)var(max)tt(x)'. More is possible. +See ifzman(the section `The zsh/zutil Module' in zmanref(zshmodules))\ +ifnzman(noderef(The zsh/zutil Module)) for details. + Use the quicker tt(bzr) backend example(zstyle ':vcs_info:bzr:*' use-simple true) @@ -956,10 +1110,10 @@ If you do use tt(use-simple), please report if it does `the-right-thing[tm]'. Display the revision number in yellow for tt(bzr) and tt(svn): example(zstyle ':vcs_info:(svn|bzr):*' branchformat '%b%{'${fg[yellow]}'%}:%r') -If you want colors, make sure you enclose the color codes in tt(%{...%}), -if you want to use the string provided by tt(vcs_info) in prompts. +If you want colors, make sure you enclose the color codes in tt(%{...%}) +if you want to use the string provided by var(vcs_info) in prompts. -Here is how to print the vcs information as a command (not in a prompt): +Here is how to print the VCS information as a command (not in a prompt): example(alias vcsi='vcs_info command; vcs_info_lastmsg') This way, you can even define different formats for output via @@ -967,15 +1121,15 @@ tt(vcs_info_lastmsg) in the ':vcs_info:*:command:*' namespace. Now as promised, some code that uses hooks: say, you'd like to replace the string `svn' by `subversion' in -tt(vcs_info)'s tt(%s) format-replacement. +var(vcs_info)'s tt(%s) tt(formats) replacement. -First, we will tell tt(vcs_info) to call a function when populating +First, we will tell var(vcs_info) to call a function when populating the message variables with the gathered information: example(zstyle ':vcs_info:*+set-message:*' hooks svn2subversion) -Nothing happens. Which is reasonable, since there we didn't define -the actual function yet. To see what the hooks subsystem is trying to -do, enable the `tt(debug)' style: +Nothing happens. Which is reasonable, since we didn't define the actual +function yet. To see what the hooks subsystem is trying to do, enable the +`tt(debug)' style: example(zstyle ':vcs_info:*+*:*' debug true) That should give you an idea what is going on. Specifically, the function @@ -1010,7 +1164,7 @@ And then we define the `tt(+vi-hgbookmarks) function: example( function +vi-hgbookmarks+LPAR()RPAR() { # The default is to connect all bookmark names by - # semicolons. This mixes things up a little. + # commas. This mixes things up a little. # Imagine, there's one type of bookmarks that is # special to you. Say, because it's *your* work. # Those bookmarks look always like this: "sh/*" @@ -1018,8 +1172,7 @@ function +vi-hgbookmarks+LPAR()RPAR() { # This makes the bookmarks string use only those # bookmarks. If there's more than one, it # concatenates them using commas. - local s i) -example( + local s i # The bookmarks returned by `hg' are available in # the functions positional parameters. (( $# == 0 )) && return 0 @@ -1028,22 +1181,25 @@ example( [[ -n $s ]] && s=$s, s=${s}$i fi - done) -example( + done # Now, the communication with the code that calls # the hook functions is done via the hook_com[] # hash. The key, at which the `gen-hg-bookmark-string' # hook looks at is `hg-bookmark-string'. So: - hook_com[hg-bookmark-string]=$s) -example( + hook_com[hg-bookmark-string]=$s # And to signal, that we want to use the sting we # just generated, set the special variable `ret' to # something other than the default zero: ret=1 return 0 -}) +} +) + +Some longer examples and code snippets which might be useful are available in +the examples file located at Misc/vcs_info-examples in the Zsh source +directory. -This concludes our guided tour through zsh's tt(vcs_info). +This concludes our guided tour through zsh's var(vcs_info). texinode(Prompt Themes)(ZLE Functions)(Version Control Information)(User Contributions) 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 diff --git a/Misc/.distfiles b/Misc/.distfiles index 4e6a00aac..d40656c0d 100644 --- a/Misc/.distfiles +++ b/Misc/.distfiles @@ -3,4 +3,5 @@ DISTFILES_SRC=' bash2zshprompt c2z compctl-examples globtests globtests.ksh job-control-tests lete2ctl make-zsh-urls + vcs_info-examples ' diff --git a/Misc/vcs_info-examples b/Misc/vcs_info-examples new file mode 100644 index 000000000..6060f2306 --- /dev/null +++ b/Misc/vcs_info-examples @@ -0,0 +1,496 @@ +# A collection of vcs_info usage examples + +### Running vcs_info ######################################################### + +# As always, there's more than one way to skin a cat. Running vcs_info is +# exception. Here is a rundown of three common ways to get it into action. +# +# All three ways need vcs_info to be marked for autoloading first, so you'd +# do this somewhere in your setup: + +autoload -Uz vcs_info + +# Episode I: "The prompt_subst way" +# Also known as the Quick-Start-Way. Probably the simplest way to add +# vcs_info functionality to existing setups. You just drop a vcs_info call +# to your `precmd' (or into a `precmd_functions[]' entry) and include a +# single-quoted ${vcs_info_msg_0_} in your PS1 definition: + +precmd() { vcs_info } +# This needs prompt_subst set, hence the name. So: +setopt prompt_subst +PS1='%!-%3~ ${vcs_info_msg_0_}%# ' + +# Episode II: "The way of the psvar" +# With $psvar you got a simple way to get user defined things into your +# prompt without having to set `prompt_subst', which requires extra +# attention to quoting if you like characters like ` in your prompt... +# As described in <http://xana.scru.org/xana2/quanks/vcsinfoprompt/>: + +precmd() { + psvar=() + + vcs_info + [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_" +} + +# You can now use `%1v' to drop the $vcs_info_msg_0_ contents in your prompt; +# like this: + +PS1="%m%(1v.%F{red}%1v%f.)%# " + +# Episode III: "The justsetitinprecmd way" +# This is the way I prefer. When you see it, you may think "Setting that +# variable in precmd() each time? What a waste..."; but let me assure you, +# you're running vcs_info already, setting one variable is not an issue. +# +# You're getting the benefit of being able to programmatically setting your +# prompt, which is nice especially when you're going to do weird things in +# there anyway. Here goes: + +precmd() { + # As always first run the system so everything is setup correctly. + vcs_info + # And then just set PS1, RPS1 and whatever you want to. This $PS1 + # is (as with the other examples above too) just an example of a very + # basic single-line prompt. See "man zshmisc" for details on how to + # make this less readable. :-) + if [[ -n ${vcs_info_msg_0_} ]]; then + # Oh hey, nothing from vcs_info, so we got more space. + # Let's print a longer part of $PWD... + PS1="%5~%# " + else + # vcs_info found something, that needs space. So a shorter $PWD + # makes sense. + PS1="%3~${vcs_info_msg_0_}%# " + fi +} + + +### Hooks #################################################################### + +# A number of examples in this file revolve around the concept of `hooks' +# in vcs_info. Hooks are places in vcs_info where you may put in your +# own code to achieve something "totally awesome"[tm]. +# +# Hooks can be confusing. It's hard to keep track of what's going on. +# In order to help you with that vcs_info can output some debugging +# information when it processes hooks. This will tell you which hooks +# are being run and which functions are attempted to run (and if the +# functions in question were found or not). +# +# If you feel like you need to see what's attempted and where, I suggest +# you use the following line and see for yourself. +zstyle ':vcs_info:*+*:*' debug true + +# You can just comment it out (or disable it) again when you've seen enough. +# Debugging is off by default - of course. +zstyle ':vcs_info:*+*:*' debug false + +# Further down, every example that uses a function named `+vi-*' uses a hook. + + +### check-for-changes just in some places #################################### + +# Some backends (git and mercurial at the time of writing) can tell you +# whether there are local changes in the current repository. While that's +# nice, the actions needed to obtain the information can be potentially +# expensive. So if you're working on something the size of the linux kernel +# or some corporate code monstrosity you may want to think twice about +# enabling the `check-for-changes' style unconditionally. +# +# Zsh's zstyle configuration system let's you do some magic to enable styles +# only depending on some code you're running. +# +# So, what I'm doing is this: I'm keeping my own projects in `~/src/code'. +# There are the projects I want the information for most. They are also +# a lot smaller than the linux kernel so the information can be retrieved +# instantaneously - even on my old laptop at 600MHz. And the following code +# enables `check-for-changes' only in that subtree: + +zstyle -e ':vcs_info:git:*' \ + check-for-changes 'estyle-cfc && reply=( true ) || reply=( false )' + +function estyle-cfc() { + local d + local -a cfc_dirs + cfc_dirs=( + ${HOME}/src/code/*(/) + ) + + for d in ${cfc_dirs}; do + d=${d%/##} + [[ $PWD == $d(|/*) ]] && return 0 + done + return 1 +} + + +### Mercurial Tips ######################################################### + +### Truncate Long Hashes #################################################### + +### Truncate a long hash to 12 characters (which is usually unique enough) +# NOTE: On Mercurial this will hide the second parent hash during a merge +# (see an example in the Mercurial section below on how to retain both parents) +# Use zformat syntax (remember %i is the hash): %12.12i + +# First, remove the hash from the default 'branchformat': +zstyle ':vcs_info:hg:*' branchformat '%b' +# Then add the hash to 'formats' as %i and truncate it to 12 chars: +zstyle ':vcs_info:hg:*' formats ' (%s)-[%12.12i %b]-' + + +### Truncate long hash to 12-chars but also allow for multiple parents +# Hashes are joined with a + to mirror the output of `hg id`. +zstyle ':vcs_info:hg+set-hgrev-format:*' hooks hg-shorthash +function +vi-hg-shorthash() { + local -a parents + + parents=( ${(s:+:)hook_com[hash]} ) + parents=( ${(@r:12:)parents} ) + hook_com[rev-replace]=${(j:+:)parents} + + ret=1 +} + +### Show marker when the working directory is not on a branch head +# This may indicate that running `hg up` will do something +# NOTE: the branchheads.cache file is not updated with every Mercurial +# operation, so it will sometimes give false positives. Think of this more as a +# hint that you might not be on a branch head instead of the final word. +zstyle ':vcs_info:hg+set-hgrev-format:*' hooks hg-storerev +zstyle ':vcs_info:hg+set-message:*' hooks hg-branchhead + +# The hash is availabe in the hgrev-format hook, store a copy of it in the +# user_data array so we can access it in the second function +function +vi-hg-storerev() { + user_data[hash]=${hook_com[hash]} +} + +function +vi-hg-branchhead() { + local branchheadsfile i_tiphash i_branchname + local -a branchheads + + local branchheadsfile=${hook_com[base]}/.hg/branchheads.cache + + # Bail out if any mq patches are applied + [[ -s ${hook_com[base]}/.hg/patches/status ]] && return 0 + + if [[ -r ${branchheadsfile} ]] ; then + while read -r i_tiphash i_branchname ; do + branchheads+=( $i_tiphash ) + done < ${branchheadsfile} + + if [[ ! ${branchheads[(i)${user_data[hash]}]} -le ${#branchheads} ]] ; then + hook_com[revision]="^ ${hook_com[revision]}" + fi + fi +} + + +### Run vcs_info selectively to increase speed in large repos ################# + +# The following example shows a possible setup for vcs_info which displays +# staged and unstaged changes in the vcs_info prompt and prevents running +# it too often for speed reasons. + + +# Allow substitutions and expansions in the prompt, necessary for +# using a single-quoted $vcs_info_msg_0_ in PS1, RPOMPT (as used here) and +# similar. Other ways of using the information are described above. +setopt promptsubst +# Load vcs_info to display information about version control repositories. +autoload -Uz vcs_info + +# Check the repository for changes so they can be used in %u/%c (see +# below). This comes with a speed penalty for bigger repositories. +zstyle ':vcs_info:*' check-for-changes true +zstyle ':vcs_info:*' get-revision true + +# Improve default formats/actionformats to display staged (%c) and +# unstaged (%u) changes. You can change the displayed string with the +# 'unstagedstr' and 'stagedstr' settings. +zstyle ':vcs_info:*' formats " (%s)-[%b]%u%c-" +zstyle ':vcs_info:*' actionformats " (%s)-[%b|%a]%u%c-" + + +# Default to running vcs_info. If possible we prevent running it later for +# speed reasons. If set to a non empty value vcs_info is run. +FORCE_RUN_VCS_INFO=1 + +# Only run vcs_info when necessary to speed up the prompt and make using +# check-for-changes bearable in bigger repositories. This setup was +# inspired by Bart Trojanowski +# (http://jukie.net/~bart/blog/pimping-out-zsh-prompt). +# +# This setup is by no means perfect. It can only detect changes done +# through the VCS's commands run by the current shell. If you use your +# editor to commit changes to the VCS or if you run them in another shell +# this setup won't detect them. To fix this just run "cd ." which causes +# vcs_info to run and update the information. If you use aliases to run +# the VCS commands update the case check below. +zstyle ':vcs_info:*+pre-get-data:*' hooks pre-get-data ++vi-pre-get-data() { + # Only Git and Mercurial support and need caching. Abort if any other + # VCS is used. + [[ "$vcs" != git && "$vcs" != hg ]] && return + + # If the shell just started up or we changed directories (or for other + # custom reasons) we must run vcs_info. + if [[ -n $FORCE_RUN_VCS_INFO ]]; then + FORCE_RUN_VCS_INFO= + return + fi + + # If we got to this point, running vcs_info was not forced, so now we + # default to not running it and selectively choose when we want to run + # it (ret=1 means run it, ret=0 means don't). + ret=1 + # If a git/hg command was run then run vcs_info as the status might + # need to be updated. + case "$(fc -ln $(($HISTCMD-1)))" in + git*) + ret=0 + ;; + hg*) + ret=0 + ;; + esac +} + +# Call vcs_info as precmd before every prompt. +prompt_precmd() { + vcs_info +} +add-zsh-hook precmd prompt_precmd + +# Must run vcs_info when changing directories. +prompt_chpwd() { + FORCE_RUN_VCS_INFO=1 +} +add-zsh-hook chpwd prompt_chpwd + +# Display the VCS information in the right prompt. The {..:- } is a +# workaround for Zsh below 4.3.9. +RPROMPT='${vcs_info_msg_0_:- }' + + +### Quilt support ############################################################ + +# Vcs_info does its best to support the patch management system quilt +# <http://savannah.nongnu.org/projects/quilt>. The information gathered by +# the quilt support always (and I'm saying always, because there are two +# ways quilt support can be active - see "man zshcontrib" for details) +# ends up in the `%Q' replacement in formats. +# +# Quilt support is also disabled by default. To turn its `addon' mode +# on for all backends, do: +zstyle ':vcs_info:*' use-quilt true + +# Then use `%Q' somewhere in the `formats' and `actionformats' styles: +zstyle ':vcs_info:*' formats " (%s)-[%b%Q]-" +zstyle ':vcs_info:*' actionformats " (%s)-[%b%Q|%a]-" + +# In the quilt support code, the zstyle context changes a little, it's now: +# :vcs_info:<vcs>.quilt-<quiltmode>:*:* +# "<vcs>" is the version-control-system string and "<quiltmode>" is either +# `addon' or `standalone'. So, if you'd use quilt on top of CVS, the +# context becomes ":vcs_info:cvs.quilt-addon:*:*". + +# That's almost all you need to know. Almost. +# +# Quilt support has a standalone mode. Even though quilt is not really +# a version control system, it keeps track of patches. It can work on top +# of a real VCS (like subversion or CVS - which is covered by addon mode) +# or apply patches to a normal directory tree that's not under version +# control. The debian project does this for a large number of packages, +# during their automatic build process. +# The `use-quilt' style only enables# addon-mode, because for standalone +# mode we'd have to try to detect whether quilt is "active" in a directory. +# You can fine-tune that "detection" using the `quilt-standalone' style. +# If the value of that style is a function name, that function is executed +# without arguments to determine whether quilt-detection should be attempted. +# It's the most powerful way of doing this and we'll give a simple +# example later. + +# First let's assume you want standalone mode to be active only in +# /usr/src/debian, ~/src/debian and their subdirectories. That's simple: + +typeset -a foobar +foobar=( + /usr/src/debian + ~/src/debian +) +zstyle ':vcs_info:*' quilt-standalone foobar + +# As mentioned earlier, using a function in this style is more powerful: +function foobar() { + # You can do any sort of wicked wizardry here. This example just + # checks if we're in "/usr/src/debian" or a subdirectory and if so + # enables standalone detection. + [[ $PWD == /usr/src/debian(|/*) ]] && return 0 + + # Returning non-zero means false, which means don't enable the + # "detection". + return 1 +} + +# In standalone-mode, vcs_info pretends as if quilt actually was a VCS. +# Well, kind of. The vcs string is set to '-quilt-'. So let's define a +# format just for that mode: +zstyle ':vcs_info:-quilt-:*' formats " [%s%Q]-" + +# As with other format insertions, you got total control over what is being +# inserted. The `%Q' insertion is controlled by the `quiltformat' and +# `quilt-nopatch-format' styles. + +# quiltformat (default: "%p (%n applied)") +# The `%p' replacement tells you which patches are applied. `%n' tells you +# How many patches are applied. `%u' and `%N' do the same for unapplied patches. +# +# Now you might say, that's way too much. That'll eat up my entire screen if I +# all my 1002 patches applied. Well, true. +# By default, `%p' contains the top-most applied patch. `%u' says contains the +# number of unapplied patches and is therefore the same as `%c'. +# There are two hooks you can use to setup what these contain. Those would be +# `gen-applied-string' and `gen-unapplied-string'. We'll go with the default +# here... ...no need to go into every insane detail. +zstyle ':vcs_info:*' quiltformat '#%p [%n|%c]' + +# quilt-nopatch-format (default: "no patch applied") +zstyle ':vcs_info:*' quilt-nopatch-format '#cleeaaaaan!1!!' + +# To retrieve inforamation about unapplied patches, vcs_info invokes `quilt' +# itself. Even though that's pretty quick, it's not needed for the default +# behaviour. If we want to have `%c' and `%u' to contain meaningful data, +# we have to enable retrieval of unapplied data: +zstyle ':vcs_info:*' quilt-get-unapplied true + +# With quilt, the location of its patches are configurable. It's either +# $QUILT_PATCHES or `patches' if that's unset. Let's assume we're a debian +# developer and want $QUILT_PATCHES to always be `debian/patches' in stand- +# alone mode: +zstyle ':vcs_info:-quilt-.quilt-standalone:*:*' quilt-patch-dir debian/patches + +# Since we're a debian developer, we also have some packages of our own, +# and so we want addon mode to also use a $QUILT_PATCHES value of +# `debian/patches' in some directories. In the other directories we never +# want the default `patches' though but a dedicated place for them. +# Say `~/patches/<repository-name>'. Now we'll use some evaluated-style +# magic to achieve all that: + +zstyle -e ':vcs_info:*.quilt-addon:*:*' quilt-patch-dir 'my-patches-func' + +# That runs something called `my-patches-func', and the value of $reply is +# used as the value for the `quilt-patch-dir' style. We'll define the thing +# as a function - as the name suggests: + +function my-patches-func() { + local p + # As the tidy debian developer we are, we're keeping our packages + # in VCSs and they are located in one place `~/src/mypkgs/' + if [[ $PWD == ${HOME}/src/mypkgs(|/*) ]]; then + reply=( debian/patches ) + return 0 + fi + + # Now the part about the dedicated directory is a little trickier. + # It requires some knowledge of vcs_info's internals. Not much though. + # Everything about this is described in the manual because this + # variable (plus a few others) may be of interest in hooks, where + # they are available, too. + # + # The variable in question here is `$rrn' which is an abbreviation + # of repository-root-name. if you're in + # /usr/src/zsh/Functions + # and the repository being + # /usr/src/zsh + # then the value of `$rrn' is `zsh'. Now in case the variable is + # empty (it shouldn't at this point, but you never know), let's + # drop back to quilt's default "patches". + if [[ -z ${rrn} ]]; then + reply=( patches ) + return 0 + fi + + # If we're here, there's something in $rrn, so: + p="${HOME}/patches/${rrn}" + if [[ ! -d $p ]]; then + # ...and while we're at it, make sure it exists... + mkdir -p "$p" + fi + reply=( $p ) +} + +# And finally, let's use the `post-quilt' hook to let vcs_info help us +# with setting the $QUILT_PATCHES variable. Since vcs_info already knows +# which $QUILT_PATCHES value is correct, it should just export that variable +# for us. No need to configure something twice when it can work +# automatically. :-) + +# Register the hook: +zstyle ':vcs_info:*+post-quilt:*:*' hooks set-quilt-patches + +# Define the corresponding function: +function +vi-set-quilt-patches() { + # The `post-quilt' hook functions are called with three arguments: + # $1 the mode (`addon' vs. `standalone'). + # $2 the path-name of the detected patches directory. + # $3 the path-name of the `.pc' directory (or "-nopc-" if it + # could not be found). + # So, what we're after is in $2 already, which makes this function + # rather trivial: + export QUILT_PATCHES="$2" + return 0 +} + +# This would take care of all the dedicated-patches-directory-in-${HOME} +# from earlier examples, too. + + +### Using vcs_info from CVS ################################################## + +# You've decided you desperately need a newer feature of vcs_info than +# there is in your installed version of zsh. That's possible, but be aware +# that you are choosing not only the newest features but potentially also +# the newest bugs of vcs_info. Also note, that vcs_info from CVS *may* rely +# on features of zsh that are only available in a newer version than you +# got installed on your system. +# +# So, now that the warnings are out of the way - let's cut to the chase: +# First you'll need to decide where to put the files from CVS. Many people +# keep a directory for personal function files such as `~/.zfuncs' or +# similar. That's what we'll use here. +# +# Step one: "get the thing from CVS" +# % mkdir -p ~/.zfuncs +# % cd ~/.zfuncs +# % cvs -z3 -d:pserver:anonymous@zsh.cvs.sourceforge.net:/cvsroot/zsh \ +# co -d VCS_Info -PA zsh/Functions/VCS_Info +# +# There, now you got a `~/.zfuncs/VCS_Info' directory that has all the files +# you need. Whenever you feel like updating the checkout, you can do: +# % cd ~/.zfuncs/VCS_Info; cvs up; cd - +# +# Step two: "Tell zsh to use the checkout" +# Zsh looks for function files in the directories listed in $fpath. If +# you're already using `~/.zfuncs' you probably have something like this +# in your setup: + +fpath=( ~/.zfuncs $fpath ) + +# Note, that the private directory is added in *front* of the default +# value, so that files from that directory supersede the ones from system +# directories. To add the VCS_Info subtree (excluding the CVS directories) +# in front, change that line to this: + +fpath=( ~/.zfuncs ~/.zfuncs/VCS_Info/**/*~*/(CVS)#(/) $fpath ) + +# The weirdly looking pattern requires the `extended_glob' option to be +# active, so make sure it is. +# +# Step three: "Restart Z shell" +# A simple +# % exec zsh +# gets you there. You should be all set now. Have fun. |