Mercurial > hg
changeset 8916:3df8dbf706b0
merged with crew
author | Martin Geisler <mg@lazybytes.net> |
---|---|
date | Sun, 21 Jun 2009 19:06:57 +0200 |
parents | c57336de5c12 (current diff) d0a3eadfbdb3 (diff) |
children | 41ef9ea0ba66 |
files | hgext/interhg.py mercurial/dispatch.py mercurial/localrepo.py |
diffstat | 59 files changed, 731 insertions(+), 487 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/buildrpm Sat Jun 20 18:58:34 2009 +0200 +++ b/contrib/buildrpm Sun Jun 21 19:06:57 2009 +0200 @@ -1,19 +1,19 @@ #!/bin/sh # # Build a Mercurial RPM in place. -# Known to work on: -# - Fedora 9 -# - Fedora 10 # # Bryan O'Sullivan <bos@serpentine.com> +# +# Tested on +# - Fedora 10 +# - Fedora 11 +# - Centos 5.3 (with Fedora EPEL repo for asciidoc) -if hg --version > /dev/null 2>&1; then : -else - echo 'hg command not available!' 1>&2 - exit 1 -fi +HG="`dirname $0`/../hg" +PYTHONPATH="`dirname $0`/../mercurial/pure" +export PYTHONPATH -root="`hg root 2>/dev/null`" +root="`$HG root 2>/dev/null`" specfile=contrib/mercurial.spec if [ -z "$root" ]; then @@ -26,7 +26,7 @@ cd "$root" rm -rf $rpmdir mkdir -p $rpmdir/RPMS -hg clone "$root" $rpmdir/BUILD +$HG clone "$root" $rpmdir/BUILD if [ ! -f $specfile ]; then echo "Cannot find $specfile!" 1>&2 @@ -35,11 +35,11 @@ tmpspec=/tmp/`basename "$specfile"`.$$ # FIXME: Insecure /tmp handling # Use the most recent tag as the version. -version=`hg tags | perl -e 'while(<STDIN>){if(/^(\d\S+)/){print$1;exit}}'` +version=`$HG tags | python -c 'import sys; print [l for l in sys.stdin.readlines() if l[0].isdigit()][0].split()[0]'` # Compute the release number as the difference in revision numbers # between the tip and the most recent tag. -release=`hg tags | perl -e 'while(<STDIN>){($tag,$id)=/^(\S+)\s+(\d+)/;if($tag eq "tip"){$tip = $id}elsif($tag=~/^\d/){print $tip-$id+1;exit}}'` -tip=`hg -q tip` +release=`$HG tags | python -c 'import sys; l = sys.stdin.readlines(); print int(l[0].split()[1].split(":")[0]) - int([x for x in l if x[0].isdigit()][0].split()[1].split(":")[0])'` +tip=`$HG -q tip` # Beat up the spec file sed -e 's,^Source:.*,Source: /dev/null,' \ @@ -51,11 +51,11 @@ cat <<EOF >> $tmpspec %changelog -* `date +'%a %b %d %Y'` `hg showconfig ui.username` $version-$release +* `date +'%a %b %d %Y'` `$HG showconfig ui.username` $version-$release - Automatically built via $0 EOF -hg log \ +$HG log \ --template '* {date|rfc822date} {author}\n- {desc|firstline}\n\n' \ .hgtags \ | sed -e 's/^\(\* [MTWFS][a-z][a-z]\), \([0-3][0-9]\) \([A-Z][a-z][a-z]\) /\1 \3 \2 /' \
--- a/contrib/mercurial.spec Sat Jun 20 18:58:34 2009 +0200 +++ b/contrib/mercurial.spec Sun Jun 21 19:06:57 2009 +0200 @@ -71,6 +71,8 @@ %{_bindir}/hg-viz %{_bindir}/git-rev-tree %{_bindir}/mercurial-convert-repo -%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py2.5.egg-info +%if "%{?pythonver}" != "2.4" +%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info +%endif %{pythonlib} %{hgext}
--- a/contrib/perf.py Sat Jun 20 18:58:34 2009 +0200 +++ b/contrib/perf.py Sun Jun 21 19:06:57 2009 +0200 @@ -1,4 +1,5 @@ # perf.py - performance test routines +'''helper extension to measure performance''' from mercurial import cmdutil, match, commands import time, os, sys
--- a/doc/hgrc.5.txt Sat Jun 20 18:58:34 2009 +0200 +++ b/doc/hgrc.5.txt Sun Jun 21 19:06:57 2009 +0200 @@ -142,6 +142,11 @@ foo.password = bar foo.schemes = http https + bar.prefix = secure.example.org + bar.key = path/to/file.key + bar.cert = path/to/file.cert + bar.schemes = https + Supported arguments: prefix;; @@ -152,10 +157,17 @@ against the URI with its scheme stripped as well, and the schemes argument, q.v., is then subsequently consulted. username;; - Username to authenticate with. + Optional. Username to authenticate with. If not given, and the + remote site requires basic or digest authentication, the user + will be prompted for it. password;; - Optional. Password to authenticate with. If not given the user + Optional. Password to authenticate with. If not given, and the + remote site requires basic or digest authentication, the user will be prompted for it. + key;; + Optional. PEM encoded client certificate key file. + cert;; + Optional. PEM encoded client certificate chain file. schemes;; Optional. Space separated list of URI schemes to use this authentication entry with. Only used if the prefix doesn't include
--- a/hgext/acl.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/acl.py Sun Jun 21 19:06:57 2009 +0200 @@ -5,49 +5,49 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. # -# this hook allows to allow or deny access to parts of a repo when -# taking incoming changesets. -# -# authorization is against local user name on system where hook is -# run, not committer of original changeset (since that is easy to -# spoof). -# -# acl hook is best to use if you use hgsh to set up restricted shells -# for authenticated users to only push to / pull from. not safe if -# user has interactive shell access, because they can disable hook. -# also not safe if remote users share one local account, because then -# no way to tell remote users apart. -# -# to use, configure acl extension in hgrc like this: -# -# [extensions] -# hgext.acl = -# -# [hooks] -# pretxnchangegroup.acl = python:hgext.acl.hook -# -# [acl] -# sources = serve # check if source of incoming changes in this list -# # ("serve" == ssh or http, "push", "pull", "bundle") -# -# allow and deny lists have subtree pattern (default syntax is glob) -# on left, user names on right. deny list checked before allow list. -# -# [acl.allow] -# # if acl.allow not present, all users allowed by default -# # empty acl.allow = no users allowed -# docs/** = doc_writer -# .hgtags = release_engineer -# -# [acl.deny] -# # if acl.deny not present, no users denied by default -# # empty acl.deny = all users allowed -# glob pattern = user4, user5 -# ** = user6 + +'''provide simple hooks for access control + +Authorization is against local user name on system where hook is run, not +committer of original changeset (since that is easy to spoof). + +The acl hook is best to use if you use hgsh to set up restricted shells for +authenticated users to only push to / pull from. It's not safe if user has +interactive shell access, because they can disable the hook. It's also not +safe if remote users share one local account, because then there's no way to +tell remote users apart. + +To use, configure the acl extension in hgrc like this: + + [extensions] + hgext.acl = + + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook + + [acl] + sources = serve # check if source of incoming changes in this list + # ("serve" == ssh or http, "push", "pull", "bundle") + +Allow and deny lists have a subtree pattern (default syntax is glob) on the +left and user names on right. The deny list is checked before the allow list. + + [acl.allow] + # if acl.allow not present, all users allowed by default + # empty acl.allow = no users allowed + docs/** = doc_writer + .hgtags = release_engineer + + [acl.deny] + # if acl.deny not present, no users denied by default + # empty acl.deny = all users allowed + glob pattern = user4, user5 + ** = user6 +''' from mercurial.i18n import _ from mercurial import util, match -import getpass +import getpass, urllib def buildmatch(ui, repo, user, key): '''return tuple of (match function, list enabled).''' @@ -72,7 +72,15 @@ ui.debug(_('acl: changes have source "%s" - skipping\n') % source) return - user = getpass.getuser() + user = None + if source == 'serve' and 'url' in kwargs: + url = kwargs['url'].split(':') + if url[0] == 'remote' and url[1].startswith('http'): + user = urllib.unquote(url[2]) + + if user is None: + user = getpass.getuser() + cfg = ui.config('acl', 'config') if cfg: ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
--- a/hgext/bookmarks.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/bookmarks.py Sun Jun 21 19:06:57 2009 +0200 @@ -64,10 +64,14 @@ util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks')) if current(repo) not in refs: setcurrent(repo, None) - file = repo.opener('bookmarks', 'w+') - for refspec, node in refs.iteritems(): - file.write("%s %s\n" % (hex(node), refspec)) - file.close() + wlock = repo.wlock() + try: + file = repo.opener('bookmarks', 'w', atomictemp=True) + for refspec, node in refs.iteritems(): + file.write("%s %s\n" % (hex(node), refspec)) + file.rename() + finally: + wlock.release() def current(repo): '''Get the current bookmark @@ -106,9 +110,13 @@ return if mark not in refs: mark = '' - file = repo.opener('bookmarks.current', 'w+') - file.write(mark) - file.close() + wlock = repo.wlock() + try: + file = repo.opener('bookmarks.current', 'w', atomictemp=True) + file.write(mark) + file.rename() + finally: + wlock.release() repo._bookmarkcurrent = mark def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None): @@ -242,26 +250,30 @@ def commit(self, *k, **kw): """Add a revision to the repository and move the bookmark""" - node = super(bookmark_repo, self).commit(*k, **kw) - if node is None: - return None - parents = repo.changelog.parents(node) - if parents[1] == nullid: - parents = (parents[0],) - marks = parse(repo) - update = False - for mark, n in marks.items(): - if ui.configbool('bookmarks', 'track.current'): - if mark == current(repo) and n in parents: - marks[mark] = node - update = True - else: - if n in parents: - marks[mark] = node - update = True - if update: - write(repo, marks) - return node + wlock = self.wlock() # do both commit and bookmark with lock held + try: + node = super(bookmark_repo, self).commit(*k, **kw) + if node is None: + return None + parents = repo.changelog.parents(node) + if parents[1] == nullid: + parents = (parents[0],) + marks = parse(repo) + update = False + for mark, n in marks.items(): + if ui.configbool('bookmarks', 'track.current'): + if mark == current(repo) and n in parents: + marks[mark] = node + update = True + else: + if n in parents: + marks[mark] = node + update = True + if update: + write(repo, marks) + return node + finally: + wlock.release() def addchangegroup(self, source, srctype, url, emptyok=False): parents = repo.dirstate.parents()
--- a/hgext/children.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/children.py Sun Jun 21 19:06:57 2009 +0200 @@ -8,6 +8,8 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. +'''provides children command to show children changesets''' + from mercurial import cmdutil from mercurial.commands import templateopts from mercurial.i18n import _
--- a/hgext/churn.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/churn.py Sun Jun 21 19:06:57 2009 +0200 @@ -120,7 +120,7 @@ It is possible to map alternate email addresses to a main address by providing a file using the following format: - + <alias email> <actual email> Such a file may be specified with the --aliases option, otherwise a
--- a/hgext/color.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/color.py Sun Jun 21 19:06:57 2009 +0200 @@ -29,10 +29,6 @@ function (aka ANSI escape codes). This module also provides the render_text function, which can be used to add effects to any text. -To enable this extension, add this to your .hgrc file: -[extensions] -color = - Default effects may be overridden from the .hgrc file: [color]
--- a/hgext/convert/convcmd.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/convert/convcmd.py Sun Jun 21 19:06:57 2009 +0200 @@ -80,7 +80,7 @@ self.authorfile = None # Record converted revisions persistently: maps source revision - # ID to target revision ID (both strings). (This is how + # ID to target revision ID (both strings). (This is how # incremental conversions work.) self.map = mapfile(ui, revmapfile) @@ -297,7 +297,7 @@ parents = [self.map.get(p, p) for p in parents] except KeyError: parents = [b[0] for b in pbranches] - newnode = self.dest.putcommit(files, copies, parents, commit, + newnode = self.dest.putcommit(files, copies, parents, commit, self.source, self.map) self.source.converted(rev, newnode) self.map[rev] = newnode
--- a/hgext/convert/p4.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/convert/p4.py Sun Jun 21 19:06:57 2009 +0200 @@ -159,7 +159,7 @@ if code == "error": raise IOError(d["generic"], data) - + elif code == "stat": p4type = self.re_type.match(d["type"]) if p4type: @@ -173,7 +173,7 @@ keywords = self.re_keywords_old elif "k" in flags: keywords = self.re_keywords - + elif code == "text" or code == "binary": contents += data
--- a/hgext/convert/subversion.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/convert/subversion.py Sun Jun 21 19:06:57 2009 +0200 @@ -472,7 +472,7 @@ # Here/tags/tag.1 discarded as well as its children. # It happens with tools like cvs2svn. Such tags cannot # be represented in mercurial. - addeds = dict((p, e.copyfrom_path) for p,e + addeds = dict((p, e.copyfrom_path) for p, e in origpaths.iteritems() if e.action == 'A') badroots = set() for destroot in addeds: @@ -484,7 +484,7 @@ break for badroot in badroots: - pendings = [p for p in pendings if p[2] != badroot + pendings = [p for p in pendings if p[2] != badroot and not p[2].startswith(badroot + '/')] # Tell tag renamings from tag creations @@ -497,7 +497,7 @@ if tagname in tags: # Keep the latest tag value continue - # From revision may be fake, get one with changes + # From revision may be fake, get one with changes try: tagid = self.latest(source, sourcerev) if tagid and tagname not in tags:
--- a/hgext/extdiff.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/extdiff.py Sun Jun 21 19:06:57 2009 +0200 @@ -5,18 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. -''' +'''allow external programs to compare revisions + The `extdiff' Mercurial extension allows you to use external programs to compare revisions, or revision with working directory. The external diff programs are called with a configurable set of options and two non-option arguments: paths to directories containing snapshots of files to compare. -To enable this extension: - - [extensions] - hgext.extdiff = - The `extdiff' extension also allows to configure new diff commands, so you do not need to type "hg extdiff -p kdiff3" always.
--- a/hgext/gpg.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/gpg.py Sun Jun 21 19:06:57 2009 +0200 @@ -1,10 +1,10 @@ -# GnuPG signing extension for Mercurial -# # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. +'''GnuPG signing extension for Mercurial''' + import os, tempfile, binascii from mercurial import util, commands, match from mercurial import node as hgnode
--- a/hgext/graphlog.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/graphlog.py Sun Jun 21 19:06:57 2009 +0200 @@ -12,59 +12,32 @@ revision graph is also shown. ''' -import os +import os, sys from mercurial.cmdutil import revrange, show_changeset from mercurial.commands import templateopts from mercurial.i18n import _ from mercurial.node import nullrev from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions -from mercurial import hg, url, util - -def revisions(repo, start, stop): - """cset DAG generator yielding (rev, node, [parents]) tuples +from mercurial import hg, url, util, graphmod - This generator function walks through the revision history from revision - start to revision stop (which must be less than or equal to start). - """ - assert start >= stop - cur = start - while cur >= stop: - ctx = repo[cur] - parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev] - parents.sort() - yield (ctx, parents) - cur -= 1 - -def filerevs(repo, path, start, stop): - """file cset DAG generator yielding (rev, node, [parents]) tuples +ASCIIDATA = 'ASC' - This generator function walks through the revision history of a single - file from revision start to revision stop (which must be less than or - equal to start). - """ - assert start >= stop - filerev = len(repo.file(path)) - 1 - while filerev >= 0: - fctx = repo.filectx(path, fileid=filerev) - parents = [f.linkrev() for f in fctx.parents() if f.path() == path] - parents.sort() - if fctx.rev() <= start: - yield (fctx, parents) - if fctx.rev() <= stop: - break - filerev -= 1 +def asciiformat(ui, repo, revdag, opts): + """formats a changelog DAG walk for ASCII output""" + showparents = [ctx.node() for ctx in repo[None].parents()] + displayer = show_changeset(ui, repo, opts, buffered=True) + for (id, type, ctx, parentids) in revdag: + if type != graphmod.CHANGESET: + continue + displayer.show(ctx) + lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1] + char = ctx.node() in showparents and '@' or 'o' + yield (id, ASCIIDATA, (char, lines), parentids) -def grapher(nodes): - """grapher for asciigraph on a list of nodes and their parents - - nodes must generate tuples (node, parents, char, lines) where - - parents must generate the parents of node, in sorted order, - and max length 2, - - char is the char to print as the node symbol, and - - lines are the lines to display next to the node. - """ +def asciiedges(nodes): + """adds edge info to changelog DAG walk suitable for ascii()""" seen = [] - for node, parents, char, lines in nodes: + for node, type, data, parents in nodes: if node not in seen: seen.append(node) nodeidx = seen.index(node) @@ -88,7 +61,7 @@ edges.append((nodeidx, nodeidx + 1)) nmorecols = len(nextseen) - ncols seen = nextseen - yield (char, lines, nodeidx, edges, ncols, nmorecols) + yield (nodeidx, type, data, edges, ncols, nmorecols) def fix_long_right_edges(edges): for (i, (start, end)) in enumerate(edges): @@ -142,14 +115,16 @@ line.extend(["|", " "] * (n_columns - ni - 1)) return line -def ascii(ui, grapher): - """prints an ASCII graph of the DAG returned by the grapher +def ascii(ui, dag): + """prints an ASCII graph of the DAG + + dag is a generator that emits tuples with the following elements: - grapher is a generator that emits tuples with the following elements: - - - Character to use as node's symbol. - - List of lines to display as the node's text. - Column of the current node in the set of ongoing edges. + - Type indicator of node data == ASCIIDATA. + - Payload: (char, lines): + - Character to use as node's symbol. + - List of lines to display as the node's text. - Edges; a list of (col, next_col) indicating the edges between the current node and its parents. - Number of columns (ongoing edges) in the current revision. @@ -160,7 +135,7 @@ """ prev_n_columns_diff = 0 prev_node_index = 0 - for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher: + for (node_index, type, (node_ch, node_lines), edges, n_columns, n_columns_diff) in dag: assert -2 < n_columns_diff < 2 if n_columns_diff == -1: @@ -278,34 +253,19 @@ if path: path = util.canonpath(repo.root, os.getcwd(), path) if path: # could be reset in canonpath - revdag = filerevs(repo, path, start, stop) + revdag = graphmod.filerevs(repo, path, start, stop) else: - revdag = revisions(repo, start, stop) + revdag = graphmod.revisions(repo, start, stop) - graphdag = graphabledag(ui, repo, revdag, opts) - ascii(ui, grapher(graphdag)) + fmtdag = asciiformat(ui, repo, revdag, opts) + ascii(ui, asciiedges(fmtdag)) def graphrevs(repo, nodes, opts): - include = set(nodes) limit = cmdutil.loglimit(opts) - count = 0 - for node in reversed(nodes): - if count >= limit: - break - ctx = repo[node] - parents = [p.rev() for p in ctx.parents() if p.node() in include] - parents.sort() - yield (ctx, parents) - count += 1 - -def graphabledag(ui, repo, revdag, opts): - showparents = [ctx.node() for ctx in repo[None].parents()] - displayer = show_changeset(ui, repo, opts, buffered=True) - for (ctx, parents) in revdag: - displayer.show(ctx) - lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1] - char = ctx.node() in showparents and '@' or 'o' - yield (ctx.rev(), parents, char, lines) + nodes.reverse() + if limit < sys.maxint: + nodes = nodes[:limit] + return graphmod.nodes(repo, nodes) def goutgoing(ui, repo, dest=None, **opts): """show the outgoing changesets alongside an ASCII revision graph @@ -332,8 +292,8 @@ o = repo.changelog.nodesbetween(o, revs)[0] revdag = graphrevs(repo, o, opts) - graphdag = graphabledag(ui, repo, revdag, opts) - ascii(ui, grapher(graphdag)) + fmtdag = asciiformat(ui, repo, revdag, opts) + ascii(ui, asciiedges(fmtdag)) def gincoming(ui, repo, source="default", **opts): """show the incoming changesets alongside an ASCII revision graph @@ -381,8 +341,8 @@ chlist = other.changelog.nodesbetween(incoming, revs)[0] revdag = graphrevs(other, chlist, opts) - graphdag = graphabledag(ui, repo, revdag, opts) - ascii(ui, grapher(graphdag)) + fmtdag = asciiformat(ui, repo, revdag, opts) + ascii(ui, asciiedges(fmtdag)) finally: if hasattr(other, 'close'):
--- a/hgext/hgk.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/hgk.py Sun Jun 21 19:06:57 2009 +0200 @@ -14,20 +14,8 @@ hgk consists of two parts: a Tcl script that does the displaying and querying of information, and an extension to Mercurial named hgk.py, which provides hooks for hgk to get information. hgk can be found in -the contrib directory, and hgk.py can be found in the hgext directory. - -To load the hgext.py extension, add it to your .hgrc file (you have to -use your global $HOME/.hgrc file, not one in a repository). You can -specify an absolute path: - - [extensions] - hgk=/usr/local/lib/hgk.py - -Mercurial can also scan the default python library path for a file -named 'hgk.py' if you set hgk empty: - - [extensions] - hgk= +the contrib directory, and the extension is shipped in the hgext +repository, and needs to be enabled. The hg view command will launch the hgk Tcl script. For this command to work, hgk must be in your search path. Alternately, you can specify
--- a/hgext/highlight/__init__.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/highlight/__init__.py Sun Jun 21 19:06:57 2009 +0200 @@ -13,11 +13,6 @@ It depends on the Pygments syntax highlighting library: http://pygments.org/ -To enable the extension add this to hgrc: - -[extensions] -hgext.highlight = - There is a single configuration option: [web] @@ -30,10 +25,10 @@ import highlight from mercurial.hgweb import webcommands, webutil, common -from mercurial import extensions +from mercurial import extensions, encoding def filerevision_highlight(orig, web, tmpl, fctx): - mt = ''.join(tmpl('mimetype', encoding=web.encoding)) + mt = ''.join(tmpl('mimetype', encoding=encoding.encoding)) # only pygmentize for mimetype containing 'html' so we both match # 'text/html' and possibly 'application/xhtml+xml' in the future # so that we don't have to touch the extension when the mimetype @@ -47,7 +42,7 @@ return orig(web, tmpl, fctx) def annotate_highlight(orig, web, req, tmpl): - mt = ''.join(tmpl('mimetype', encoding=web.encoding)) + mt = ''.join(tmpl('mimetype', encoding=encoding.encoding)) if 'html' in mt: fctx = webutil.filectx(web.repo, req) style = web.config('web', 'pygments_style', 'colorful')
--- a/hgext/interhg.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/interhg.py Sun Jun 21 19:06:57 2009 +0200 @@ -14,12 +14,8 @@ which will be automatically expanded into links or any other arbitrary expression, much like InterWiki does. -To enable this extension, add the following lines to your hgrc: - - [extensions] - interhg = - -A few example patterns (link to bug tracking, etc.): +A few example patterns (link to bug tracking, etc.) that may +be used in your hgrc: [interhg] issues = s!issue(\\d+)!<a href="http://bts/issue\\1">issue\\1</a>!
--- a/hgext/keyword.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/keyword.py Sun Jun 21 19:06:57 2009 +0200 @@ -21,12 +21,6 @@ # # Binary files are not touched. # -# Setup in hgrc: -# -# [extensions] -# # enable extension -# hgext.keyword = -# # Files to act upon/ignore are specified in the [keyword] section. # Customized keyword template mappings in the [keywordmaps] section. #
--- a/hgext/mq.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/mq.py Sun Jun 21 19:06:57 2009 +0200 @@ -543,6 +543,8 @@ def _apply(self, repo, series, list=False, update_status=True, strict=False, patchdir=None, merge=None, all_files={}): + '''returns (error, hash) + error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz''' # TODO unify with commands.py if not patchdir: patchdir = self.path @@ -559,7 +561,7 @@ try: ph = patchheader(self.join(patchname)) except: - self.ui.warn(_("Unable to read %s\n") % patchname) + self.ui.warn(_("unable to read %s\n") % patchname) err = 1 break @@ -607,46 +609,60 @@ if patcherr: self.ui.warn(_("patch failed, rejects left in working dir\n")) - err = 1 + err = 2 break if fuzz and strict: self.ui.warn(_("fuzz found when applying patch, stopping\n")) - err = 1 + err = 3 break return (err, n) - def _clean_series(self, patches): + def _cleanup(self, patches, numrevs, keep=False): + if not keep: + r = self.qrepo() + if r: + r.remove(patches, True) + else: + for p in patches: + os.unlink(self.join(p)) + + if numrevs: + del self.applied[:numrevs] + self.applied_dirty = 1 + for i in sorted([self.find_series(p) for p in patches], reverse=True): del self.full_series[i] self.parse_series() self.series_dirty = 1 - def finish(self, repo, revs): + def _revpatches(self, repo, revs): firstrev = repo[self.applied[0].rev].rev() - appliedbase = 0 patches = [] - for rev in sorted(revs): + for i, rev in enumerate(revs): + if rev < firstrev: raise util.Abort(_('revision %d is not managed') % rev) - base = bin(self.applied[appliedbase].rev) - node = repo.changelog.node(rev) - if node != base: - raise util.Abort(_('cannot delete revision %d above ' - 'applied patches') % rev) - patches.append(self.applied[appliedbase].name) - appliedbase += 1 + + ctx = repo[rev] + base = bin(self.applied[i].rev) + if ctx.node() != base: + msg = _('cannot delete revision %d above applied patches') + raise util.Abort(msg % rev) - r = self.qrepo() - if r: - r.remove(patches, True) - else: - for p in patches: - os.unlink(self.join(p)) + patch = self.applied[i].name + for fmt in ('[mq]: %s', 'imported patch %s'): + if ctx.description() == fmt % patch: + msg = _('patch %s finalized without changeset message\n') + repo.ui.status(msg % patch) + break - del self.applied[:appliedbase] - self.applied_dirty = 1 - self._clean_series(patches) + patches.append(patch) + return patches + + def finish(self, repo, revs): + patches = self._revpatches(repo, sorted(revs)) + self._cleanup(patches, len(patches)) def delete(self, repo, patches, opts): if not patches and not opts.get('rev'): @@ -663,37 +679,18 @@ raise util.Abort(_("patch %s not in series file") % patch) realpatches.append(patch) - appliedbase = 0 + numrevs = 0 if opts.get('rev'): if not self.applied: raise util.Abort(_('no patches applied')) revs = cmdutil.revrange(repo, opts['rev']) if len(revs) > 1 and revs[0] > revs[1]: revs.reverse() - for rev in revs: - if appliedbase >= len(self.applied): - raise util.Abort(_("revision %d is not managed") % rev) - - base = bin(self.applied[appliedbase].rev) - node = repo.changelog.node(rev) - if node != base: - raise util.Abort(_("cannot delete revision %d above " - "applied patches") % rev) - realpatches.append(self.applied[appliedbase].name) - appliedbase += 1 + revpatches = self._revpatches(repo, revs) + realpatches += revpatches + numrevs = len(revpatches) - if not opts.get('keep'): - r = self.qrepo() - if r: - r.remove(realpatches, True) - else: - for p in realpatches: - os.unlink(self.join(p)) - - if appliedbase: - del self.applied[:appliedbase] - self.applied_dirty = 1 - self._clean_series(realpatches) + self._cleanup(realpatches, numrevs, opts.get('keep')) def check_toppatch(self, repo): if len(self.applied) > 0: @@ -958,6 +955,7 @@ end = start + 1 else: end = self.series.index(patch, start) + 1 + s = self.series[start:end] all_files = {} try: @@ -977,13 +975,15 @@ util.unlink(repo.wjoin(f)) self.ui.warn(_('done\n')) raise + top = self.applied[-1].name - if ret[0]: - self.ui.write(_("errors during apply, please fix and " - "refresh %s\n") % top) + if ret[0] and ret[0] > 1: + msg = _("errors during apply, please fix and refresh %s\n") + self.ui.write(msg % top) else: self.ui.write(_("now at: %s\n") % top) return ret[0] + finally: wlock.release()
--- a/hgext/parentrevspec.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/parentrevspec.py Sun Jun 21 19:06:57 2009 +0200 @@ -5,8 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. -'''\ -use suffixes to refer to ancestor revisions +'''use suffixes to refer to ancestor revisions This extension allows you to use git-style suffixes to refer to the ancestors of a specific revision.
--- a/hgext/patchbomb.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/patchbomb.py Sun Jun 21 19:06:57 2009 +0200 @@ -28,11 +28,6 @@ with a diffstat summary and the changeset summary, so you can be sure you are sending the right changes. -To enable this extension: - - [extensions] - hgext.patchbomb = - To configure other defaults, add a section like this to your hgrc file:
--- a/hgext/purge.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/purge.py Sun Jun 21 19:06:57 2009 +0200 @@ -6,10 +6,6 @@ # This program was inspired by the "cvspurge" script contained in CVS utilities # (http://www.red-bean.com/cvsutils/). # -# To enable the "purge" extension put these lines in your ~/.hgrc: -# [extensions] -# hgext.purge = -# # For help on the usage of "hg purge" use: # hg help purge # @@ -27,6 +23,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +'''enable removing untracked files only''' + from mercurial import util, commands, cmdutil from mercurial.i18n import _ import os, stat
--- a/hgext/share.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/share.py Sun Jun 21 19:06:57 2009 +0200 @@ -1,10 +1,10 @@ -# Mercurial extension to provide the 'hg share' command -# # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. +'''provides the hg share command''' + import os from mercurial.i18n import _ from mercurial import hg, commands
--- a/hgext/win32mbcs.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/win32mbcs.py Sun Jun 21 19:06:57 2009 +0200 @@ -33,11 +33,6 @@ * You should set same encoding for the repository by locale or HGENCODING. -To use this extension, enable the extension in .hg/hgrc or ~/.hgrc: - - [extensions] - hgext.win32mbcs = - Path encoding conversion are done between Unicode and encoding.encoding which is decided by Mercurial from current locale setting or HGENCODING.
--- a/hgext/win32text.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/win32text.py Sun Jun 21 19:06:57 2009 +0200 @@ -4,31 +4,34 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. -# -# To perform automatic newline conversion, use: -# -# [extensions] -# hgext.win32text = -# [encode] -# ** = cleverencode: -# # or ** = macencode: -# [decode] -# ** = cleverdecode: -# # or ** = macdecode: -# -# If not doing conversion, to make sure you do not commit CRLF/CR by -# accident: -# -# [hooks] -# pretxncommit.crlf = python:hgext.win32text.forbidcrlf -# # or pretxncommit.cr = python:hgext.win32text.forbidcr -# -# To do the same check on a server to prevent CRLF/CR from being -# pushed or pulled: -# -# [hooks] -# pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf -# # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr + +'''LF <-> CRLF/CR translation utilities + +To perform automatic newline conversion, use: + +[extensions] +hgext.win32text = +[encode] +** = cleverencode: +# or ** = macencode: + +[decode] +** = cleverdecode: +# or ** = macdecode: + +If not doing conversion, to make sure you do not commit CRLF/CR by accident: + +[hooks] +pretxncommit.crlf = python:hgext.win32text.forbidcrlf +# or pretxncommit.cr = python:hgext.win32text.forbidcr + +To do the same check on a server to prevent CRLF/CR from being +pushed or pulled: + +[hooks] +pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf +# or pretxnchangegroup.cr = python:hgext.win32text.forbidcr +''' from mercurial.i18n import _ from mercurial.node import short
--- a/hgext/zeroconf/__init__.py Sat Jun 20 18:58:34 2009 +0200 +++ b/hgext/zeroconf/__init__.py Sun Jun 21 19:06:57 2009 +0200 @@ -11,12 +11,6 @@ the need to configure a server or a service. They can be discovered without knowing their actual IP address. -To use the zeroconf extension add the following entry to your hgrc -file: - -[extensions] -hgext.zeroconf = - To allow other people to discover your repository using run "hg serve" in your repository.
--- a/mercurial/bdiff.c Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/bdiff.c Sun Jun 21 19:06:57 2009 +0200 @@ -18,6 +18,10 @@ # define inline #endif +#ifdef __linux +# define inline __inline +#endif + #ifdef _WIN32 #ifdef _MSC_VER #define inline __inline
--- a/mercurial/commands.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/commands.py Sun Jun 21 19:06:57 2009 +0200 @@ -1230,16 +1230,14 @@ for i in xrange(blo, bhi): yield ('+', b[i]) - prev = {} - def display(fn, rev, states, prevstates): + def display(fn, r, pstates, states): datefunc = ui.quiet and util.shortdate or util.datestr found = False filerevmatches = {} - r = prev.get(fn, -1) if opts.get('all'): - iter = difflinestates(states, prevstates) + iter = difflinestates(pstates, states) else: - iter = [('', l) for l in prevstates] + iter = [('', l) for l in states] for change, l in iter: cols = [fn, str(r)] if opts.get('line_number'): @@ -1261,8 +1259,8 @@ found = True return found - fstate = {} skip = {} + revfiles = {} get = util.cachefunc(lambda r: repo[r].changeset()) changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) found = False @@ -1270,46 +1268,58 @@ for st, rev, fns in changeiter: if st == 'window': matches.clear() + revfiles.clear() elif st == 'add': ctx = repo[rev] - matches[rev] = {} + pctx = ctx.parents()[0] + parent = pctx.rev() + matches.setdefault(rev, {}) + matches.setdefault(parent, {}) + files = revfiles.setdefault(rev, []) for fn in fns: - if fn in skip: - continue + flog = getfile(fn) try: - grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn))) - fstate.setdefault(fn, []) - if follow: - copied = getfile(fn).renamed(ctx.filenode(fn)) - if copied: - copies.setdefault(rev, {})[fn] = copied[0] + fnode = ctx.filenode(fn) except error.LookupError: - pass + continue + + copied = flog.renamed(fnode) + copy = follow and copied and copied[0] + if copy: + copies.setdefault(rev, {})[fn] = copy + if fn in skip: + if copy: + skip[copy] = True + continue + files.append(fn) + + if not matches[rev].has_key(fn): + grepbody(fn, rev, flog.read(fnode)) + + pfn = copy or fn + if not matches[parent].has_key(pfn): + try: + fnode = pctx.filenode(pfn) + grepbody(pfn, parent, flog.read(fnode)) + except error.LookupError: + pass elif st == 'iter': - for fn, m in sorted(matches[rev].items()): + parent = repo[rev].parents()[0].rev() + for fn in sorted(revfiles.get(rev, [])): + states = matches[rev][fn] copy = copies.get(rev, {}).get(fn) if fn in skip: if copy: skip[copy] = True continue - if fn in prev or fstate[fn]: - r = display(fn, rev, m, fstate[fn]) + pstates = matches.get(parent, {}).get(copy or fn, []) + if pstates or states: + r = display(fn, rev, pstates, states) found = found or r if r and not opts.get('all'): skip[fn] = True if copy: skip[copy] = True - fstate[fn] = m - if copy: - fstate[copy] = m - prev[fn] = rev - - for fn, state in sorted(fstate.items()): - if fn in skip: - continue - if fn not in copies.get(prev[fn], {}): - found = display(fn, rev, {}, state) or found - return (not found and 1) or 0 def heads(ui, repo, *branchrevs, **opts): """show current repository heads or show branch heads @@ -1473,18 +1483,9 @@ else: ui.write(' %-*s %s\n' % (m, f, h[f])) - exts = list(extensions.extensions()) - if exts and name != 'shortlist': - ui.write(_('\nenabled extensions:\n\n')) - maxlength = 0 - exthelps = [] - for ename, ext in exts: - doc = (gettext(ext.__doc__) or _('(no help text available)')) - ename = ename.split('.')[-1] - maxlength = max(len(ename), maxlength) - exthelps.append((ename, doc.splitlines(0)[0].strip())) - for ename, text in exthelps: - ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text)) + if name != 'shortlist': + exts, maxlength = extensions.enabled() + ui.write(help.listexts(_('enabled extensions:'), exts, maxlength)) if not ui.quiet: addglobalopts(True) @@ -2108,7 +2109,7 @@ 'use "hg update" or merge with an explicit rev')) node = parent == bheads[0] and bheads[-1] or bheads[0] - if opts.get('show'): + if opts.get('preview'): p1 = repo['.'] p2 = repo[node] common = p1.ancestor(p2) @@ -2670,7 +2671,8 @@ This command should be used with care. There is only one level of rollback, and there is no way to undo a rollback. It will also restore the dirstate at the time of the last transaction, losing - any dirstate changes since that time. + any dirstate changes since that time. This command does not alter + the working directory. Transactions are used to encapsulate the effects of all commands that create new changesets or propagate existing changesets into a @@ -2718,9 +2720,9 @@ baseui = repo and repo.baseui or ui optlist = ("name templates style address port prefix ipv6" - " accesslog errorlog webdir_conf certificate") + " accesslog errorlog webdir_conf certificate encoding") for o in optlist.split(): - if opts[o]: + if opts.get(o, None): baseui.setconfig("web", o, str(opts[o])) if (repo is not None) and (repo.ui != baseui): repo.ui.setconfig("web", o, str(opts[o])) @@ -2965,7 +2967,7 @@ return postincoming(ui, repo, modheads, opts.get('update'), None) -def update(ui, repo, node=None, rev=None, clean=False, date=None): +def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False): """update working directory Update the repository's working directory to the specified @@ -2981,7 +2983,8 @@ When there are uncommitted changes, use option -C/--clean to discard them, forcibly replacing the state of the working - directory with the requested revision. + directory with the requested revision. Alternately, use -c/--check + to abort. When there are uncommitted changes and option -C/--clean is not used, and the parent revision and requested revision are on the @@ -3001,6 +3004,12 @@ if not rev: rev = node + if not clean and check: + # we could use dirty() but we can ignore merge and branch trivia + c = repo[None] + if c.modified() or c.added() or c.removed(): + raise util.Abort(_("uncommitted local changes")) + if date: if rev: raise util.Abort(_("you can't specify a revision and a date")) @@ -3358,7 +3367,7 @@ (merge, [('f', 'force', None, _('force a merge with outstanding changes')), ('r', 'rev', '', _('revision to merge')), - ('S', 'show', None, + ('P', 'preview', None, _('review revisions to merge (no merge is performed)'))], _('[-f] [[-r] REV]')), "outgoing|out": @@ -3492,6 +3501,7 @@ "^update|up|checkout|co": (update, [('C', 'clean', None, _('overwrite locally modified files (no backup)')), + ('c', 'check', None, _('check for uncommitted changes')), ('d', 'date', '', _('tipmost revision matching date')), ('r', 'rev', '', _('revision'))], _('[-C] [-d DATE] [[-r] REV]')),
--- a/mercurial/dispatch.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/dispatch.py Sun Jun 21 19:06:57 2009 +0200 @@ -224,7 +224,6 @@ # but only if they have been defined prior to the current definition. for alias, definition in ui.configitems('alias'): aliasdef = cmdalias(alias, definition, cmdtable) - cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help) if aliasdef.norepo: commands.norepo += ' %s' % alias
--- a/mercurial/extensions.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/extensions.py Sun Jun 21 19:06:57 2009 +0200 @@ -5,9 +5,9 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. -import imp, os -import util, cmdutil -from i18n import _ +import imp, os, sys +import util, cmdutil, help +from i18n import _, gettext _extensions = {} _order = [] @@ -117,3 +117,61 @@ origfn = getattr(container, funcname) setattr(container, funcname, wrap) return origfn + +def disabled(): + '''find disabled extensions from hgext + returns a dict of {name: desc}, and the max name length''' + + import hgext + extpath = os.path.dirname(os.path.abspath(hgext.__file__)) + + exts = {} + maxlength = 0 + for e in os.listdir(extpath): + + if e.endswith('.py'): + name = e.rsplit('.', 1)[0] + path = os.path.join(extpath, e) + else: + name = e + path = os.path.join(extpath, e, '__init__.py') + if not os.path.exists(path): + continue + + if name in exts or name in _order or name == '__init__': + continue + + try: + file = open(path) + except IOError: + continue + else: + doc = help.moduledoc(file) + file.close() + + if doc: # extracting localized synopsis + exts[name] = gettext(doc).splitlines()[0] + else: + exts[name] = _('(no help text available)') + + if len(name) > maxlength: + maxlength = len(name) + + return exts, maxlength + +def enabled(): + '''return a dict of {name: desc} of extensions, and the max name length''' + + if not enabled: + return {}, 0 + + exts = {} + maxlength = 0 + exthelps = [] + for ename, ext in extensions(): + doc = (gettext(ext.__doc__) or _('(no help text available)')) + ename = ename.split('.')[-1] + maxlength = max(len(ename), maxlength) + exts[ename] = doc.splitlines(0)[0].strip() + + return exts, maxlength
--- a/mercurial/filemerge.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/filemerge.py Sun Jun 21 19:06:57 2009 +0200 @@ -195,8 +195,8 @@ elif tool == 'internal:dump': a = repo.wjoin(fd) util.copyfile(a, a + ".local") - repo.wwrite(a + ".other", fco.data(), fco.flags()) - repo.wwrite(a + ".base", fca.data(), fca.flags()) + repo.wwrite(fd + ".other", fco.data(), fco.flags()) + repo.wwrite(fd + ".base", fca.data(), fca.flags()) return 1 # unresolved else: args = _toolstr(ui, tool, "args", '$local $base $other')
--- a/mercurial/graphmod.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/graphmod.py Sun Jun 21 19:06:57 2009 +0200 @@ -6,70 +6,114 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2, incorporated herein by reference. -from node import nullrev +"""supports walking the history as DAGs suitable for graphical output -def graph(repo, start_rev, stop_rev): - """incremental revision grapher +The most basic format we use is that of:: + + (id, type, data, [parentids]) - This generator function walks through the revision history from - revision start_rev to revision stop_rev (which must be less than - or equal to start_rev) and for each revision emits tuples with the - following elements: +The node and parent ids are arbitrary integers which identify a node in the +context of the graph returned. Type is a constant specifying the node type. +Data depends on type. +""" + +from mercurial.node import nullrev + +CHANGESET = 'C' - - Current node - - Column and color for the current node - - Edges; a list of (col, next_col, color) indicating the edges between - the current node and its parents. - - First line of the changeset description - - The changeset author - - The changeset date/time +def revisions(repo, start, stop): + """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples + + This generator function walks through the revision history from revision + start to revision stop (which must be less than or equal to start). It + returns a tuple for each node. The node and parent ids are arbitrary + integers which identify a node in the context of the graph returned. """ + cur = start + while cur >= stop: + ctx = repo[cur] + parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev] + yield (cur, CHANGESET, ctx, sorted(parents)) + cur -= 1 - if start_rev == nullrev and not stop_rev: - return +def filerevs(repo, path, start, stop): + """file cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples - assert start_rev >= stop_rev - assert stop_rev >= 0 - curr_rev = start_rev - revs = [] - cl = repo.changelog - colors = {} - new_color = 1 + This generator function walks through the revision history of a single + file from revision start down to revision stop. + """ + filerev = len(repo.file(path)) - 1 + while filerev >= 0: + fctx = repo.filectx(path, fileid=filerev) + parents = [f.linkrev() for f in fctx.parents() if f.path() == path] + rev = fctx.rev() + if rev <= start: + yield (rev, CHANGESET, fctx, sorted(parents)) + if rev <= stop: + break + filerev -= 1 + +def nodes(repo, nodes): + """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples + + This generator function walks the given nodes. It only returns parents + that are in nodes, too. + """ + include = set(nodes) + for node in nodes: + ctx = repo[node] + parents = [p.rev() for p in ctx.parents() if p.node() in include] + yield (ctx.rev(), CHANGESET, ctx, sorted(parents)) + +def colored(dag): + """annotates a DAG with colored edge information - while curr_rev >= stop_rev: - # Compute revs and next_revs - if curr_rev not in revs: - revs.append(curr_rev) # new head - colors[curr_rev] = new_color - new_color += 1 + For each DAG node this function emits tuples:: + + (id, type, data, (col, color), [(col, nextcol, color)]) + + with the following new elements: - idx = revs.index(curr_rev) - color = colors.pop(curr_rev) - next = revs[:] + - Tuple (col, color) with column and color index for the current node + - A list of tuples indicating the edges between the current node and its + parents. + """ + seen = [] + colors = {} + newcolor = 1 + for (cur, type, data, parents) in dag: - # Add parents to next_revs - parents = [x for x in cl.parentrevs(curr_rev) if x != nullrev] + # Compute seen and next + if cur not in seen: + seen.append(cur) # new head + colors[cur] = newcolor + newcolor += 1 + + col = seen.index(cur) + color = colors.pop(cur) + next = seen[:] + + # Add parents to next addparents = [p for p in parents if p not in next] - next[idx:idx + 1] = addparents + next[col:col + 1] = addparents # Set colors for the parents for i, p in enumerate(addparents): if not i: colors[p] = color else: - colors[p] = new_color - new_color += 1 + colors[p] = newcolor + newcolor += 1 # Add edges to the graph edges = [] - for col, r in enumerate(revs): - if r in next: - edges.append((col, next.index(r), colors[r])) - elif r == curr_rev: + for ecol, eid in enumerate(seen): + if eid in next: + edges.append((ecol, next.index(eid), colors[eid])) + elif eid == cur: for p in parents: - edges.append((col, next.index(p), colors[p])) + edges.append((ecol, next.index(p), colors[p])) # Yield and move on - yield (repo[curr_rev], (idx, color), edges) - revs = next - curr_rev -= 1 + yield (cur, type, data, (col, color), edges) + seen = next
--- a/mercurial/help.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/help.py Sun Jun 21 19:06:57 2009 +0200 @@ -6,6 +6,90 @@ # GNU General Public License version 2, incorporated herein by reference. from i18n import _ +import extensions + + +def moduledoc(file): + '''return the top-level python documentation for the given file + + Loosely inspired by pydoc.source_synopsis(), but rewritten to handle \''' + as well as """ and to return the whole text instead of just the synopsis''' + result = [] + + line = file.readline() + while line[:1] == '#' or not line.strip(): + line = file.readline() + if not line: break + + start = line[:3] + if start == '"""' or start == "'''": + line = line[3:] + while line: + if line.rstrip().endswith(start): + line = line.split(start)[0] + if line: + result.append(line) + break + elif not line: + return None # unmatched delimiter + result.append(line) + line = file.readline() + else: + return None + + return ''.join(result) + +def listexts(header, exts, maxlength): + '''return a text listing of the given extensions''' + if not exts: + return '' + result = '\n%s\n\n' % header + for name, desc in sorted(exts.iteritems()): + result += ' %s %s\n' % (name.ljust(maxlength), desc) + return result + +def extshelp(): + doc = _(r''' + Mercurial has a mechanism for adding new features through the + use of extensions. Extensions may bring new commands, or new + hooks, or change Mercurial's behavior. + + Extensions are not loaded by default for a variety of reasons, + they may be meant for advanced users or provide potentially + dangerous commands (e.g. mq and rebase allow history to be + rewritten), they might not be ready for prime-time yet, or + they may alter Mercurial's behavior. It is thus up to the user + to activate extensions as desired. + + To enable the "foo" extension, either shipped with Mercurial + or in the Python search path, create an entry for it in your + hgrc, like this: + + [extensions] + foo = + + You may also specify the full path to an extension: + + [extensions] + myfeature = ~/.hgext/myfeature.py + + To explicitly disable an extension enabled in an hgrc of broader + scope, prepend its path with !: + + [extensions] + # disabling extension bar residing in /ext/path + hgext.bar = !/path/to/extension/bar.py + # ditto, but no path was supplied for extension baz + hgext.baz = ! + ''') + + exts, maxlength = extensions.enabled() + doc += listexts(_('enabled extensions:'), exts, maxlength) + + exts, maxlength = extensions.disabled() + doc += listexts(_('disabled extensions:'), exts, maxlength) + + return doc helptable = ( (["dates"], _("Date Formats"), @@ -418,4 +502,5 @@ The push command will look for a path named 'default-push', and prefer it over 'default' if both are defined. ''')), + (["extensions"], _("Using additional features"), extshelp), )
--- a/mercurial/hgweb/hgweb_mod.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/hgweb/hgweb_mod.py Sun Jun 21 19:06:57 2009 +0200 @@ -64,7 +64,8 @@ self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) self.maxfiles = int(self.config("web", "maxfiles", 10)) self.allowpull = self.configbool("web", "allowpull", True) - self.encoding = self.config("web", "encoding", encoding.encoding) + encoding.encoding = self.config("web", "encoding", + encoding.encoding) def run(self): if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): @@ -81,28 +82,6 @@ self.refresh() - # process this if it's a protocol request - # protocol bits don't need to create any URLs - # and the clients always use the old URL structure - - cmd = req.form.get('cmd', [''])[0] - if cmd and cmd in protocol.__all__: - try: - if cmd in perms: - try: - self.check_perm(req, perms[cmd]) - except ErrorResponse, inst: - if cmd == 'unbundle': - req.drain() - raise - method = getattr(protocol, cmd) - return method(self.repo, req) - except ErrorResponse, inst: - req.respond(inst, protocol.HGTYPE) - if not inst.message: - return [] - return '0\n%s\n' % inst.message, - # work with CGI variables to create coherent structure # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME @@ -122,6 +101,30 @@ query = req.env['QUERY_STRING'].split('&', 1)[0] query = query.split(';', 1)[0] + # process this if it's a protocol request + # protocol bits don't need to create any URLs + # and the clients always use the old URL structure + + cmd = req.form.get('cmd', [''])[0] + if cmd and cmd in protocol.__all__: + if query: + raise ErrorResponse(HTTP_NOT_FOUND) + try: + if cmd in perms: + try: + self.check_perm(req, perms[cmd]) + except ErrorResponse, inst: + if cmd == 'unbundle': + req.drain() + raise + method = getattr(protocol, cmd) + return method(self.repo, req) + except ErrorResponse, inst: + req.respond(inst, protocol.HGTYPE) + if not inst.message: + return [] + return '0\n%s\n' % inst.message, + # translate user-visible url structure to internal structure args = query.split('/', 2) @@ -160,7 +163,7 @@ try: tmpl = self.templater(req) - ctype = tmpl('mimetype', encoding=self.encoding) + ctype = tmpl('mimetype', encoding=encoding.encoding) ctype = templater.stringify(ctype) # check read permissions non-static content @@ -219,7 +222,7 @@ # some functions for the templater def header(**map): - yield tmpl('header', encoding=self.encoding, **map) + yield tmpl('header', encoding=encoding.encoding, **map) def footer(**map): yield tmpl("footer", **map)
--- a/mercurial/hgweb/hgwebdir_mod.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/hgweb/hgwebdir_mod.py Sun Jun 21 19:06:57 2009 +0200 @@ -70,6 +70,8 @@ elif isinstance(self.conf, dict): paths = self.conf.items() + encoding.encoding = self.ui.config('web', 'encoding', + encoding.encoding) self.motd = self.ui.config('web', 'motd') self.style = self.ui.config('web', 'style', 'paper') self.stripecount = self.ui.config('web', 'stripes', 1)
--- a/mercurial/hgweb/protocol.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/hgweb/protocol.py Sun Jun 21 19:06:57 2009 +0200 @@ -162,8 +162,10 @@ sys.stderr = sys.stdout = cStringIO.StringIO() try: - url = 'remote:%s:%s' % (proto, - req.env.get('REMOTE_HOST', '')) + url = 'remote:%s:%s:%s' % ( + proto, + urllib.quote(req.env.get('REMOTE_HOST', '')), + urllib.quote(req.env.get('REMOTE_USER', ''))) try: ret = repo.addchangegroup(gen, 'serve', url) except util.Abort, inst:
--- a/mercurial/hgweb/webcommands.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/hgweb/webcommands.py Sun Jun 21 19:06:57 2009 +0200 @@ -668,10 +668,13 @@ count = len(web.repo) changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx) - tree = list(graphmod.graph(web.repo, rev, downrev)) + dag = graphmod.revisions(web.repo, rev, downrev) + tree = list(graphmod.colored(dag)) canvasheight = (len(tree) + 1) * bg_height - 27; data = [] - for (ctx, vtx, edges) in tree: + for (id, type, ctx, vtx, edges) in tree: + if type != graphmod.CHANGESET: + continue node = short(ctx.node()) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description())
--- a/mercurial/localrepo.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/localrepo.py Sun Jun 21 19:06:57 2009 +0200 @@ -262,7 +262,7 @@ warn(_("node '%s' is not well formed") % node) continue if bin_n not in self.changelog.nodemap: - warn(_("tag '%s' refers to unknown node") % key) + # silently ignore as pull -r might cause this continue h = [] @@ -290,11 +290,24 @@ globaltags[k] = an, ah tagtypes[k] = tagtype - # read the tags file from each head, ending with the tip + seen = set() f = None - for rev, node, fnode in self._hgtagsnodes(): - f = (f and f.filectx(fnode) or - self.filectx('.hgtags', fileid=fnode)) + ctxs = [] + for node in self.heads(): + try: + fnode = self[node].filenode('.hgtags') + except error.LookupError: + continue + if fnode not in seen: + seen.add(fnode) + if not f: + f = self.filectx('.hgtags', fileid=fnode) + else: + f = f.filectx(fnode) + ctxs.append(f) + + # read the tags file from each head, ending with the tip + for f in reversed(ctxs): readtags(f.data().splitlines(), f, "global") try: @@ -328,22 +341,6 @@ return self._tagstypecache.get(tagname) - def _hgtagsnodes(self): - last = {} - ret = [] - for node in reversed(self.heads()): - c = self[node] - rev = c.rev() - try: - fnode = c.filenode('.hgtags') - except error.LookupError: - continue - ret.append((rev, node, fnode)) - if fnode in last: - ret[last[fnode]] = None - last[fnode] = len(ret) - 1 - return [item for item in ret if item] - def tagslist(self): '''return a list of tags ordered by revision''' l = []
--- a/mercurial/patch.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/patch.py Sun Jun 21 19:06:57 2009 +0200 @@ -972,7 +972,7 @@ def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False, eol=None): """ - Reads a patch from fp and tries to apply it. + Reads a patch from fp and tries to apply it. The dict 'changed' is filled in with all of the filenames changed by the patch. Returns 0 for a clean patch, -1 if any rejects were @@ -1137,7 +1137,7 @@ eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()] except KeyError: raise util.Abort(_('Unsupported line endings type: %s') % eolmode) - + try: fp = file(patchobj, 'rb') except TypeError:
--- a/mercurial/url.py Sat Jun 20 18:58:34 2009 +0200 +++ b/mercurial/url.py Sun Jun 21 19:06:57 2009 +0200 @@ -109,7 +109,9 @@ return (user, passwd) if not user: - user, passwd = self._readauthtoken(authuri) + auth = self.readauthtoken(authuri) + if auth: + user, passwd = auth.get('username'), auth.get('password') if not user or not passwd: if not self.ui.interactive(): raise util.Abort(_('http authorization required')) @@ -132,7 +134,7 @@ msg = _('http auth: user %s, password %s\n') self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) - def _readauthtoken(self, uri): + def readauthtoken(self, uri): # Read configuration config = dict() for key, val in self.ui.configitems('auth'): @@ -143,7 +145,7 @@ # Find the best match scheme, hostpath = uri.split('://', 1) bestlen = 0 - bestauth = None, None + bestauth = None for auth in config.itervalues(): prefix = auth.get('prefix') if not prefix: continue @@ -155,7 +157,7 @@ if (prefix == '*' or hostpath.startswith(prefix)) and \ len(prefix) > bestlen and scheme in schemes: bestlen = len(prefix) - bestauth = auth.get('username'), auth.get('password') + bestauth = auth return bestauth class proxyhandler(urllib2.ProxyHandler): @@ -411,8 +413,38 @@ send = _gen_sendfile(httplib.HTTPSConnection) class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): + def __init__(self, ui): + keepalive.KeepAliveHandler.__init__(self) + urllib2.HTTPSHandler.__init__(self) + self.ui = ui + self.pwmgr = passwordmgr(self.ui) + def https_open(self, req): - return self.do_open(httpsconnection, req) + self.auth = self.pwmgr.readauthtoken(req.get_full_url()) + return self.do_open(self._makeconnection, req) + + def _makeconnection(self, host, port=443, *args, **kwargs): + keyfile = None + certfile = None + + if args: # key_file + keyfile = args.pop(0) + if args: # cert_file + certfile = args.pop(0) + + # if the user has specified different key/cert files in + # hgrc, we prefer these + if self.auth and 'key' in self.auth and 'cert' in self.auth: + keyfile = self.auth['key'] + certfile = self.auth['cert'] + + # let host port take precedence + if ':' in host and '[' not in host or ']:' in host: + host, port = host.rsplit(':', 1) + if '[' in host: + host = host[1:-1] + + return httpsconnection(host, port, keyfile, certfile, *args, **kwargs) # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if # it doesn't know about the auth type requested. This can happen if @@ -460,7 +492,7 @@ ''' handlers = [httphandler()] if has_https: - handlers.append(httpshandler()) + handlers.append(httpshandler(ui)) handlers.append(proxyhandler(ui))
--- a/tests/run-tests.py Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/run-tests.py Sun Jun 21 19:06:57 2009 +0200 @@ -16,7 +16,7 @@ # If you change this script, it is recommended that you ensure you # haven't broken it by running it in various modes with a representative # sample of test scripts. For example: -# +# # 1) serial, no coverage, temp install: # ./run-tests.py test-s* # 2) serial, no coverage, local hg:
--- a/tests/test-convert.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-convert.out Sun Jun 21 19:06:57 2009 +0200 @@ -271,5 +271,5 @@ emptydir does not look like a monotone repo emptydir does not look like a GNU Arch repo emptydir does not look like a Bazaar repo -emptydir does not look like a P4 repo +cannot find required "p4" tool abort: emptydir: missing or unsupported repository
--- a/tests/test-debugcomplete.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-debugcomplete.out Sun Jun 21 19:06:57 2009 +0200 @@ -169,14 +169,14 @@ export: output, switch-parent, text, git, nodates init: ssh, remotecmd log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, prune, patch, git, limit, no-merges, style, template, include, exclude -merge: force, rev, show +merge: force, rev, preview parents: rev, style, template pull: update, force, rev, ssh, remotecmd push: force, rev, ssh, remotecmd remove: after, force, include, exclude serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, include, exclude -update: clean, date, rev +update: clean, check, date, rev addremove: similarity, include, exclude, dry-run archive: no-decode, prefix, rev, type, include, exclude backout: merge, parent, rev, include, exclude, message, logfile, date, user
--- a/tests/test-double-merge Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-double-merge Sun Jun 21 19:06:57 2009 +0200 @@ -20,7 +20,7 @@ hg ci -m 'change foo' -d "1000000 0" # we get conflicts that shouldn't be there -hg merge -S +hg merge -P hg merge --debug echo "-- foo --"
--- a/tests/test-globalopts.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-globalopts.out Sun Jun 21 19:06:57 2009 +0200 @@ -208,6 +208,7 @@ diffs Diff Formats templating Template Usage urls URL Paths + extensions Using additional features use "hg -v help" to show aliases and global options Mercurial Distributed SCM @@ -273,6 +274,7 @@ diffs Diff Formats templating Template Usage urls URL Paths + extensions Using additional features use "hg -v help" to show aliases and global options %% not tested: --debugger
--- a/tests/test-grep Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-grep Sun Jun 21 19:06:57 2009 +0200 @@ -73,3 +73,25 @@ # Used to crash here hg grep -r 1 octarine +# Issue337: grep did not compared changesets by their revision numbers +# instead of following parent-child relationships. +cd .. +echo % issue 337 +hg init issue337 +cd issue337 + +echo white > color +hg commit -A -m "0 white" + +echo red > color +hg commit -A -m "1 red" + +hg update 0 +echo black > color +hg commit -A -m "2 black" + +hg update --clean 1 +echo blue > color +hg commit -A -m "3 blue" + +hg grep --all red
--- a/tests/test-grep.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-grep.out Sun Jun 21 19:06:57 2009 +0200 @@ -38,6 +38,13 @@ noeol:4:no infinite loo % issue 685 adding color +colour:1:octarine color:0:octarine colour:1:octarine -colour:1:octarine +% issue 337 +adding color +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +color:3:-:red +color:1:+:red
--- a/tests/test-hardlinks-safety.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-hardlinks-safety.out Sun Jun 21 19:06:57 2009 +0200 @@ -15,6 +15,8 @@ 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo % foo +patch foo finalized without changeset message +patch bar finalized without changeset message %%% 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
--- a/tests/test-help.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-help.out Sun Jun 21 19:06:57 2009 +0200 @@ -99,6 +99,7 @@ diffs Diff Formats templating Template Usage urls URL Paths + extensions Using additional features use "hg -v help" to show aliases and global options add add the specified files on the next commit @@ -160,6 +161,7 @@ diffs Diff Formats templating Template Usage urls URL Paths + extensions Using additional features hg add [OPTION]... [FILE]... add the specified files on the next commit
--- a/tests/test-merge-default Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-merge-default Sun Jun 21 19:06:57 2009 +0200 @@ -31,7 +31,7 @@ hg commit -mm1 echo % should succeed - 2 heads -hg merge -S +hg merge -P hg merge hg commit -mm2
--- a/tests/test-merge1 Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-merge1 Sun Jun 21 19:06:57 2009 +0200 @@ -30,7 +30,7 @@ hg commit -m "commit #2" -d "1000000 0" echo This is file b1 > b echo %% no merges expected -hg merge -S 1 +hg merge -P 1 hg merge 1 hg diff --nodates hg status
--- a/tests/test-mq-qdelete.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-mq-qdelete.out Sun Jun 21 19:06:57 2009 +0200 @@ -13,9 +13,12 @@ b series status +patch a finalized without changeset message 1 [mq]: a 0 base abort: cannot delete revision 3 above applied patches +patch d finalized without changeset message +patch e finalized without changeset message f 4 [mq]: f 3 [mq]: e @@ -32,11 +35,14 @@ applying c patch c is empty now at: c +patch a finalized without changeset message +patch b finalized without changeset message c 3 imported patch c 2 [mq]: b 1 [mq]: a 0 base +patch c finalized without changeset message 3 imported patch c 2 [mq]: b 1 [mq]: a
--- a/tests/test-mq-qimport.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-mq-qimport.out Sun Jun 21 19:06:57 2009 +0200 @@ -27,3 +27,5 @@ adding another.diff to series file applying another.diff now at: another.diff +patch b.diff finalized without changeset message +patch another.diff finalized without changeset message
--- a/tests/test-mq-qpush-fail Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-mq-qpush-fail Sun Jun 21 19:06:57 2009 +0200 @@ -45,3 +45,12 @@ echo '% bar should be gone; other unknown/ignored files should still be around' hg status -A + +echo '% preparing qpush of a missing patch' +hg qpop -a +hg qpush +rm .hg/patches/patch2 +echo '% now we expect the push to fail, but it should NOT complain about patch1' +hg qpush + +true # happy ending
--- a/tests/test-mq-qpush-fail.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-mq-qpush-fail.out Sun Jun 21 19:06:57 2009 +0200 @@ -19,3 +19,11 @@ ? untracked-file I .hgignore C foo +% preparing qpush of a missing patch +no patches applied +applying patch1 +now at: patch1 +% now we expect the push to fail, but it should NOT complain about patch1 +applying patch2 +unable to read patch2 +now at: patch1
--- a/tests/test-tags.out Sat Jun 20 18:58:34 2009 +0200 +++ b/tests/test-tags.out Sun Jun 21 19:06:57 2009 +0200 @@ -26,14 +26,12 @@ .hgtags@c071f74ab5eb, line 2: cannot parse entry .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed -localtags, line 1: tag 'invalid' refers to unknown node tip 8:4ca6f1b1a68c first 0:0acdaf898367 changeset: 8:4ca6f1b1a68c .hgtags@c071f74ab5eb, line 2: cannot parse entry .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed -localtags, line 1: tag 'invalid' refers to unknown node tag: tip parent: 3:b2ef3841386b user: test