merge default into stable for 2.0 code freeze stable 2.0-rc
authorMatt Mackall <mpm@selenic.com>
Sat, 15 Oct 2011 14:30:50 -0500
branchstable
changeset 15273 384082750f2c
parent 15191 fccd350acf79 (current diff)
parent 15272 2889d4574726 (diff)
child 15274 a06575962c9e
merge default into stable for 2.0 code freeze
--- a/contrib/check-code.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/contrib/check-code.py	Sat Oct 15 14:30:50 2011 -0500
@@ -148,7 +148,7 @@
     (r'(?<!def)\s+(any|all|format)\(',
      "any/all/format not available in Python 2.4"),
     (r'(?<!def)\s+(callable)\(',
-     "callable not available in Python 3, use hasattr(f, '__call__')"),
+     "callable not available in Python 3, use getattr(f, '__call__', None)"),
     (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
     (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
      "gratuitous whitespace after Python keyword"),
@@ -168,6 +168,8 @@
      "comparison with singleton, use 'is' or 'is not' instead"),
     (r'^\s*(while|if) [01]:',
      "use True/False for constant Boolean expression"),
+    (r'(?<!def)\s+hasattr',
+     'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
     (r'opener\([^)]*\).read\(',
      "use opener.read() instead"),
     (r'opener\([^)]*\).write\(',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debugcmdserver.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Dumps output generated by Mercurial's command server in a formatted style to a
+# given file or stderr if '-' is specified. Output is also written in its raw
+# format to stdout.
+#
+# $ ./hg serve --cmds pipe | ./contrib/debugcmdserver.py -
+# o, 52   -> 'capabilities: getencoding runcommand\nencoding: UTF-8'
+
+import sys, struct
+
+if len(sys.argv) != 2:
+    print 'usage: debugcmdserver.py FILE'
+    sys.exit(1)
+
+outputfmt = '>cI'
+outputfmtsize = struct.calcsize(outputfmt)
+
+if sys.argv[1] == '-':
+    log = sys.stderr
+else:
+    log = open(sys.argv[1], 'a')
+
+def read(size):
+    data = sys.stdin.read(size)
+    if not data:
+        raise EOFError()
+    sys.stdout.write(data)
+    sys.stdout.flush()
+    return data
+
+try:
+    while True:
+        header = read(outputfmtsize)
+        channel, length = struct.unpack(outputfmt, header)
+        log.write('%s, %-4d' % (channel, length))
+        if channel in 'IL':
+            log.write(' -> waiting for input\n')
+        else:
+            data = read(length)
+            log.write(' -> %r\n' % data)
+        log.flush()
+except EOFError:
+    pass
+finally:
+    if log != sys.stderr:
+        log.close()
--- a/contrib/setup3k.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/contrib/setup3k.py	Sat Oct 15 14:30:50 2011 -0500
@@ -8,7 +8,7 @@
 from lib2to3.refactor import get_fixers_from_package as getfixers
 
 import sys
-if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
@@ -236,7 +236,7 @@
         try:
             build_ext.build_extension(self, ext)
         except CCompilerError:
-            if not hasattr(ext, 'optional') or not ext.optional:
+            if getattr(ext, 'optional', False):
                 raise
             log.warn("Failed to build optional extension '%s' (skipping)",
                      ext.name)
--- a/contrib/win32/hgwebdir_wsgi.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/contrib/win32/hgwebdir_wsgi.py	Sat Oct 15 14:30:50 2011 -0500
@@ -50,7 +50,7 @@
 #sys.path.insert(0, r'c:\path\to\python\lib')
 
 # Enable tracing. Run 'python -m win32traceutil' to debug
-if hasattr(sys, 'isapidllhandle'):
+if getattr(sys, 'isapidllhandle', None) is not None:
     import win32traceutil
 
 # To serve pages in local charset instead of UTF-8, remove the two lines below
--- a/contrib/zsh_completion	Sun Oct 02 16:41:07 2011 -0500
+++ b/contrib/zsh_completion	Sat Oct 15 14:30:50 2011 -0500
@@ -165,6 +165,7 @@
 _hg_labels() {
   _hg_tags "$@"
   _hg_bookmarks "$@"
+  _hg_branches "$@"
 }
 
 _hg_tags() {
@@ -191,6 +192,17 @@
   (( $#bookmarks )) && _describe -t bookmarks 'bookmarks' bookmarks
 }
 
+_hg_branches() {
+  typeset -a branches
+  local branch
+
+  _hg_cmd branches | while read branch
+  do
+    branches+=(${branch/ #    [0-9]#:*})
+  done
+  (( $#branches )) && _describe -t branches 'branches' branches
+}
+
 # likely merge candidates
 _hg_mergerevs() {
   typeset -a heads
@@ -617,6 +629,7 @@
   '(--only-merges -m)'{-m,--only-merges}'[show only merges]' \
   '(--patch -p)'{-p,--patch}'[show patch]' \
   '(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_labels' \
+  '(--branch -b)'{-b+,--branch}'[show changesets within the given named branch]:branch:_hg_branches' \
   '*:files:_hg_files'
 }
 
--- a/doc/gendoc.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/doc/gendoc.py	Sat Oct 15 14:30:50 2011 -0500
@@ -9,6 +9,7 @@
 from mercurial.i18n import _
 from mercurial.help import helptable
 from mercurial import extensions
+from mercurial import util
 
 def get_desc(docstr):
     if not docstr:
@@ -95,7 +96,7 @@
             ui.write(".. _%s:\n" % name)
         ui.write("\n")
         section(ui, sec)
-        if hasattr(doc, '__call__'):
+        if util.safehasattr(doc, '__call__'):
             doc = doc()
         ui.write(doc)
         ui.write("\n")
--- a/hgext/acl.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/acl.py	Sat Oct 15 14:30:50 2011 -0500
@@ -216,6 +216,8 @@
     if user is None:
         user = getpass.getuser()
 
+    ui.debug('acl: checking access for user "%s"\n' % user)
+
     cfg = ui.config('acl', 'config')
     if cfg:
         ui.readconfig(cfg, sections = ['acl.groups', 'acl.allow.branches',
@@ -242,9 +244,9 @@
 
         for f in ctx.files():
             if deny and deny(f):
-                ui.debug('acl: user %s denied on %s\n' % (user, f))
-                raise util.Abort(_('acl: access denied for changeset %s') % ctx)
+                raise util.Abort(_('acl: user "%s" denied on "%s"'
+                ' (changeset "%s")') % (user, f, ctx))
             if allow and not allow(f):
-                ui.debug('acl: user %s not allowed on %s\n' % (user, f))
-                raise util.Abort(_('acl: access denied for changeset %s') % ctx)
-        ui.debug('acl: allowing changeset %s\n' % ctx)
+                raise util.Abort(_('acl: user "%s" not allowed on "%s"'
+                ' (changeset "%s")') % (user, f, ctx))
+        ui.debug('acl: path access granted: "%s"\n' % ctx)
--- a/hgext/color.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/color.py	Sat Oct 15 14:30:50 2011 -0500
@@ -68,6 +68,9 @@
   branches.current = green
   branches.inactive = none
 
+  tags.normal = green
+  tags.local = black bold
+
 The available effects in terminfo mode are 'blink', 'bold', 'dim',
 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
@@ -257,7 +260,9 @@
            'status.ignored': 'black bold',
            'status.modified': 'blue bold',
            'status.removed': 'red bold',
-           'status.unknown': 'magenta bold underline'}
+           'status.unknown': 'magenta bold underline',
+           'tags.normal': 'green',
+           'tags.local': 'black bold'}
 
 
 def _effect_str(effect):
--- a/hgext/convert/cvsps.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/cvsps.py	Sat Oct 15 14:30:50 2011 -0500
@@ -11,6 +11,7 @@
 from mercurial import util
 from mercurial.i18n import _
 from mercurial import hook
+from mercurial import util
 
 class logentry(object):
     '''Class logentry has the following attributes:
@@ -362,8 +363,14 @@
         elif state == 8:
             # store commit log message
             if re_31.match(line):
-                state = 5
-                store = True
+                cpeek = peek
+                if cpeek.endswith('\n'):
+                    cpeek = cpeek[:-1]
+                if re_50.match(cpeek):
+                    state = 5
+                    store = True
+                else:
+                    e.comment.append(line)
             elif re_32.match(line):
                 state = 0
                 store = True
@@ -513,8 +520,8 @@
                   e.comment == c.comment and
                   e.author == c.author and
                   e.branch == c.branch and
-                  (not hasattr(e, 'branchpoints') or
-                    not hasattr (c, 'branchpoints') or
+                  (not util.safehasattr(e, 'branchpoints') or
+                    not util.safehasattr (c, 'branchpoints') or
                     e.branchpoints == c.branchpoints) and
                   ((c.date[0] + c.date[1]) <=
                    (e.date[0] + e.date[1]) <=
--- a/hgext/convert/filemap.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/filemap.py	Sat Oct 15 14:30:50 2011 -0500
@@ -375,3 +375,6 @@
 
     def lookuprev(self, rev):
         return self.base.lookuprev(rev)
+
+    def getbookmarks(self):
+        return self.base.getbookmarks()
--- a/hgext/convert/git.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/git.py	Sat Oct 15 14:30:50 2011 -0500
@@ -16,7 +16,7 @@
     # Windows does not support GIT_DIR= construct while other systems
     # cannot remove environment variable. Just assume none have
     # both issues.
-    if hasattr(os, 'unsetenv'):
+    if util.safehasattr(os, 'unsetenv'):
         def gitopen(self, s, noerr=False):
             prevgitdir = os.environ.get('GIT_DIR')
             os.environ['GIT_DIR'] = self.path
--- a/hgext/convert/hg.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/hg.py	Sat Oct 15 14:30:50 2011 -0500
@@ -70,10 +70,10 @@
             self.wlock.release()
 
     def revmapfile(self):
-        return os.path.join(self.path, ".hg", "shamap")
+        return self.repo.join("shamap")
 
     def authorfile(self):
-        return os.path.join(self.path, ".hg", "authormap")
+        return self.repo.join("authormap")
 
     def getheads(self):
         h = self.repo.changelog.heads()
@@ -178,7 +178,7 @@
             closed = 'close' in commit.extra
             if not closed and not man.cmp(m1node, man.revision(mnode)):
                 self.ui.status(_("filtering out empty revision\n"))
-                self.repo.rollback()
+                self.repo.rollback(force=True)
                 return parent
         return p2
 
@@ -364,8 +364,7 @@
 
     def converted(self, rev, destrev):
         if self.convertfp is None:
-            self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
-                                  'a')
+            self.convertfp = open(self.repo.join('shamap'), 'a')
         self.convertfp.write('%s %s\n' % (destrev, rev))
         self.convertfp.flush()
 
--- a/hgext/convert/subversion.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/subversion.py	Sat Oct 15 14:30:50 2011 -0500
@@ -501,11 +501,11 @@
                                 and not p[2].startswith(badroot + '/')]
 
                 # Tell tag renamings from tag creations
-                remainings = []
+                renamings = []
                 for source, sourcerev, dest in pendings:
                     tagname = dest.split('/')[-1]
                     if source.startswith(srctagspath):
-                        remainings.append([source, sourcerev, tagname])
+                        renamings.append([source, sourcerev, tagname])
                         continue
                     if tagname in tags:
                         # Keep the latest tag value
@@ -521,7 +521,7 @@
                         # but were really created in the tag
                         # directory.
                         pass
-                pendings = remainings
+                pendings = renamings
                 tagspath = srctagspath
         finally:
             stream.close()
--- a/hgext/convert/transport.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/convert/transport.py	Sat Oct 15 14:30:50 2011 -0500
@@ -54,7 +54,7 @@
                 if p:
                     providers.append(p)
     else:
-        if hasattr(svn.client, 'get_windows_simple_provider'):
+        if util.safehasattr(svn.client, 'get_windows_simple_provider'):
             providers.append(svn.client.get_windows_simple_provider(pool))
 
     return svn.core.svn_auth_open(providers, pool)
@@ -73,7 +73,7 @@
         self.password = ''
 
         # Only Subversion 1.4 has reparent()
-        if ra is None or not hasattr(svn.ra, 'reparent'):
+        if ra is None or not util.safehasattr(svn.ra, 'reparent'):
             self.client = svn.client.create_context(self.pool)
             ab = _create_auth_baton(self.pool)
             if False:
--- a/hgext/eol.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/eol.py	Sat Oct 15 14:30:50 2011 -0500
@@ -52,9 +52,10 @@
    The rules will first apply when files are touched in the working
    copy, e.g. by updating to null and back to tip to touch all files.
 
-The extension uses an optional ``[eol]`` section in your hgrc file
-(not the ``.hgeol`` file) for settings that control the overall
-behavior. There are two settings:
+The extension uses an optional ``[eol]`` section read from both the
+normal Mercurial configuration files and the ``.hgeol`` file, with the
+latter overriding the former. You can use that section to control the
+overall behavior. There are three settings:
 
 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
   ``CRLF`` to override the default interpretation of ``native`` for
@@ -67,6 +68,10 @@
   Such files are normally not touched under the assumption that they
   have mixed EOLs on purpose.
 
+- ``eol.fix-trailing-newline`` (default False) can be set to True to
+  ensure that converted files end with a EOL character (either ``\\n``
+  or ``\\r\\n`` as per the configured patterns).
+
 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
 like the deprecated win32text extension does. This means that you can
 disable win32text and enable eol and your filters will still work. You
@@ -106,6 +111,8 @@
         return s
     if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
         return s
+    if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+        s = s + '\n'
     return eolre.sub('\n', s)
 
 def tocrlf(s, params, ui, **kwargs):
@@ -114,6 +121,8 @@
         return s
     if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
         return s
+    if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+        s = s + '\n'
     return eolre.sub('\r\n', s)
 
 def isbinary(s, params):
@@ -158,7 +167,7 @@
         # about inconsistent newlines.
         self.match = match.match(root, '', [], include, exclude)
 
-    def setfilters(self, ui):
+    def copytoui(self, ui):
         for pattern, style in self.cfg.items('patterns'):
             key = style.upper()
             try:
@@ -167,6 +176,9 @@
             except KeyError:
                 ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
                         % (style, self.cfg.source('patterns', pattern)))
+        # eol.only-consistent can be specified in ~/.hgrc or .hgeol
+        for k, v in self.cfg.items('eol'):
+            ui.setconfig('eol', k, v)
 
     def checkrev(self, repo, ctx, files):
         failed = []
@@ -273,7 +285,7 @@
             eol = parseeol(self.ui, self, nodes)
             if eol is None:
                 return None
-            eol.setfilters(self.ui)
+            eol.copytoui(self.ui)
             return eol.match
 
         def _hgcleardirstate(self):
--- a/hgext/inotify/__init__.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/inotify/__init__.py	Sat Oct 15 14:30:50 2011 -0500
@@ -11,6 +11,7 @@
 # todo: socket permissions
 
 from mercurial.i18n import _
+from mercurial import util
 import server
 from client import client, QueryFailed
 
@@ -31,7 +32,7 @@
         ui.write(('  %s/\n') % path)
 
 def reposetup(ui, repo):
-    if not hasattr(repo, 'dirstate'):
+    if not util.safehasattr(repo, 'dirstate'):
         return
 
     class inotifydirstate(repo.dirstate.__class__):
--- a/hgext/keyword.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/keyword.py	Sat Oct 15 14:30:50 2011 -0500
@@ -249,10 +249,14 @@
         kwcmd = self.restrict and lookup # kwexpand/kwshrink
         if self.restrict or expand and lookup:
             mf = ctx.manifest()
-        lctx = ctx
-        re_kw = (self.restrict or rekw) and self.rekw or self.rekwexp
-        msg = (expand and _('overwriting %s expanding keywords\n')
-               or _('overwriting %s shrinking keywords\n'))
+        if self.restrict or rekw:
+            re_kw = self.rekw
+        else:
+            re_kw = self.rekwexp
+        if expand:
+            msg = _('overwriting %s expanding keywords\n')
+        else:
+            msg = _('overwriting %s shrinking keywords\n')
         for f in candidates:
             if self.restrict:
                 data = self.repo.file(f).read(mf[f])
@@ -262,18 +266,17 @@
                 continue
             if expand:
                 if lookup:
-                    lctx = self.linkctx(f, mf[f])
-                data, found = self.substitute(data, f, lctx, re_kw.subn)
+                    ctx = self.linkctx(f, mf[f])
+                data, found = self.substitute(data, f, ctx, re_kw.subn)
             elif self.restrict:
                 found = re_kw.search(data)
             else:
                 data, found = _shrinktext(data, re_kw.subn)
             if found:
                 self.ui.note(msg % f)
-                fpath = self.repo.wjoin(f)
-                mode = os.lstat(fpath).st_mode
-                self.repo.wwrite(f, data, ctx.flags(f))
-                os.chmod(fpath, mode)
+                fp = self.repo.wopener(f, "wb", atomictemp=True)
+                fp.write(data)
+                fp.close()
                 if kwcmd:
                     self.repo.dirstate.normal(f)
                 elif self.record:
@@ -296,7 +299,9 @@
     def wread(self, fname, data):
         '''If in restricted mode returns data read from wdir with
         keyword substitutions removed.'''
-        return self.restrict and self.shrink(fname, data) or data
+        if self.restrict:
+            return self.shrink(fname, data)
+        return data
 
 class kwfilelog(filelog.filelog):
     '''
@@ -325,11 +330,11 @@
         text = self.kwt.shrink(self.path, text)
         return super(kwfilelog, self).cmp(node, text)
 
-def _status(ui, repo, kwt, *pats, **opts):
+def _status(ui, repo, wctx, kwt, *pats, **opts):
     '''Bails out if [keyword] configuration is not active.
     Returns status of working directory.'''
     if kwt:
-        return repo.status(match=scmutil.match(repo[None], pats, opts), clean=True,
+        return repo.status(match=scmutil.match(wctx, pats, opts), clean=True,
                            unknown=opts.get('unknown') or opts.get('all'))
     if ui.configitems('keyword'):
         raise util.Abort(_('[keyword] patterns cannot match'))
@@ -343,7 +348,7 @@
     kwt = kwtools['templater']
     wlock = repo.wlock()
     try:
-        status = _status(ui, repo, kwt, *pats, **opts)
+        status = _status(ui, repo, wctx, kwt, *pats, **opts)
         modified, added, removed, deleted, unknown, ignored, clean = status
         if modified or added or removed or deleted:
             raise util.Abort(_('outstanding uncommitted changes'))
@@ -415,7 +420,10 @@
                 ui.setconfig('keywordmaps', k, v)
     else:
         ui.status(_('\n\tconfiguration using current keyword template maps\n'))
-        kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
+        if uikwmaps:
+            kwmaps = dict(uikwmaps)
+        else:
+            kwmaps = _defaultkwmaps(ui)
 
     uisetup(ui)
     reposetup(ui, repo)
@@ -478,13 +486,13 @@
       i = ignored (not tracked)
     '''
     kwt = kwtools['templater']
-    status = _status(ui, repo, kwt, *pats, **opts)
+    wctx = repo[None]
+    status = _status(ui, repo, wctx, kwt, *pats, **opts)
     cwd = pats and repo.getcwd() or ''
     modified, added, removed, deleted, unknown, ignored, clean = status
     files = []
     if not opts.get('unknown') or opts.get('all'):
         files = sorted(modified + added + clean)
-    wctx = repo[None]
     kwfiles = kwt.iskwfile(files, wctx)
     kwdeleted = kwt.iskwfile(deleted, wctx)
     kwunknown = kwt.iskwfile(unknown, wctx)
@@ -582,12 +590,12 @@
                 kwt.restrict = restrict
             return n
 
-        def rollback(self, dryrun=False):
+        def rollback(self, dryrun=False, force=False):
             wlock = self.wlock()
             try:
                 if not dryrun:
                     changed = self['.'].files()
-                ret = super(kwrepo, self).rollback(dryrun)
+                ret = super(kwrepo, self).rollback(dryrun, force)
                 if not dryrun:
                     ctx = self['.']
                     modified, added = _preselect(self[None].status(), changed)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/CONTRIBUTORS	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,4 @@
+Greg Ward, author of the original bfiles extension
+Na'Tosha Bard of Unity Technologies
+Fog Creek Software
+Special thanks to the University of Toronto and the UCOSP program
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/__init__.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,94 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''track large binary files
+
+Large binary files tend to be not very compressible, not very
+diffable, and not at all mergeable. Such files are not handled
+efficiently by Mercurial's storage format (revlog), which is based on
+compressed binary deltas; storing large binary files as regular
+Mercurial files wastes bandwidth and disk space and increases
+Mercurial's memory usage. The largefiles extension addresses these
+problems by adding a centralized client-server layer on top of
+Mercurial: largefiles live in a *central store* out on the network
+somewhere, and you only fetch the revisions that you need when you
+need them.
+
+largefiles works by maintaining a "standin file" in .hglf/ for each
+largefile. The standins are small (41 bytes: an SHA-1 hash plus
+newline) and are tracked by Mercurial. Largefile revisions are
+identified by the SHA-1 hash of their contents, which is written to
+the standin. largefiles uses that revision ID to get/put largefile
+revisions from/to the central store. This saves both disk space and
+bandwidth, since you don't need to retrieve all historical revisions
+of large files when you clone or pull.
+
+To start a new repository or add new large binary files, just add
+--large to your ``hg add`` command. For example::
+
+  $ dd if=/dev/urandom of=randomdata count=2000
+  $ hg add --large randomdata
+  $ hg commit -m 'add randomdata as a largefile'
+
+When you push a changeset that adds/modifies largefiles to a remote
+repository, its largefile revisions will be uploaded along with it.
+Note that the remote Mercurial must also have the largefiles extension
+enabled for this to work.
+
+When you pull a changeset that affects largefiles from a remote
+repository, Mercurial behaves as normal. However, when you update to
+such a revision, any largefiles needed by that revision are downloaded
+and cached (if they have never been downloaded before). This means
+that network access may be required to update to changesets you have
+not previously updated to.
+
+If you already have large files tracked by Mercurial without the
+largefiles extension, you will need to convert your repository in
+order to benefit from largefiles. This is done with the 'hg lfconvert'
+command::
+
+  $ hg lfconvert --size 10 oldrepo newrepo
+
+In repositories that already have largefiles in them, any new file
+over 10MB will automatically be added as a largefile. To change this
+threshhold, set ``largefiles.size`` in your Mercurial config file to
+the minimum size in megabytes to track as a largefile, or use the
+--lfsize option to the add command (also in megabytes)::
+
+  [largefiles]
+  size = 2           XXX wouldn't minsize be a better name?
+
+  $ hg add --lfsize 2
+
+The ``largefiles.patterns`` config option allows you to specify a list
+of filename patterns (see ``hg help patterns``) that should always be
+tracked as largefiles::
+
+  [largefiles]
+  patterns =
+    *.jpg
+    re:.*\.(png|bmp)$
+    library.zip
+    content/audio/*
+
+Files that match one of these patterns will be added as largefiles
+regardless of their size.
+'''
+
+from mercurial import commands
+
+import lfcommands
+import reposetup
+import uisetup
+
+reposetup = reposetup.reposetup
+uisetup = uisetup.uisetup
+
+commands.norepo += " lfconvert"
+
+cmdtable = lfcommands.cmdtable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/basestore.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,202 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''base class for store implementations and store-related utility code'''
+
+import os
+import tempfile
+import binascii
+import re
+
+from mercurial import util, node, hg
+from mercurial.i18n import _
+
+import lfutil
+
+class StoreError(Exception):
+    '''Raised when there is a problem getting files from or putting
+    files to a central store.'''
+    def __init__(self, filename, hash, url, detail):
+        self.filename = filename
+        self.hash = hash
+        self.url = url
+        self.detail = detail
+
+    def longmessage(self):
+        if self.url:
+            return ('%s: %s\n'
+                    '(failed URL: %s)\n'
+                    % (self.filename, self.detail, self.url))
+        else:
+            return ('%s: %s\n'
+                    '(no default or default-push path set in hgrc)\n'
+                    % (self.filename, self.detail))
+
+    def __str__(self):
+        return "%s: %s" % (self.url, self.detail)
+
+class basestore(object):
+    def __init__(self, ui, repo, url):
+        self.ui = ui
+        self.repo = repo
+        self.url = url
+
+    def put(self, source, hash):
+        '''Put source file into the store under <filename>/<hash>.'''
+        raise NotImplementedError('abstract method')
+
+    def exists(self, hash):
+        '''Check to see if the store contains the given hash.'''
+        raise NotImplementedError('abstract method')
+
+    def get(self, files):
+        '''Get the specified largefiles from the store and write to local
+        files under repo.root.  files is a list of (filename, hash)
+        tuples.  Return (success, missing), lists of files successfuly
+        downloaded and those not found in the store.  success is a list
+        of (filename, hash) tuples; missing is a list of filenames that
+        we could not get.  (The detailed error message will already have
+        been presented to the user, so missing is just supplied as a
+        summary.)'''
+        success = []
+        missing = []
+        ui = self.ui
+
+        at = 0
+        for filename, hash in files:
+            ui.progress(_('getting largefiles'), at, unit='lfile',
+                total=len(files))
+            at += 1
+            ui.note(_('getting %s:%s\n') % (filename, hash))
+
+            cachefilename = lfutil.cachepath(self.repo, hash)
+            cachedir = os.path.dirname(cachefilename)
+
+            # No need to pass mode='wb' to fdopen(), since mkstemp() already
+            # opened the file in binary mode.
+            (tmpfd, tmpfilename) = tempfile.mkstemp(
+                dir=cachedir, prefix=os.path.basename(filename))
+            tmpfile = os.fdopen(tmpfd, 'w')
+
+            try:
+                hhash = binascii.hexlify(self._getfile(tmpfile, filename, hash))
+            except StoreError, err:
+                ui.warn(err.longmessage())
+                hhash = ""
+
+            if hhash != hash:
+                if hhash != "":
+                    ui.warn(_('%s: data corruption (expected %s, got %s)\n')
+                            % (filename, hash, hhash))
+                tmpfile.close() # no-op if it's already closed
+                os.remove(tmpfilename)
+                missing.append(filename)
+                continue
+
+            if os.path.exists(cachefilename): # Windows
+                os.remove(cachefilename)
+            os.rename(tmpfilename, cachefilename)
+            lfutil.linktosystemcache(self.repo, hash)
+            success.append((filename, hhash))
+
+        ui.progress(_('getting largefiles'), None)
+        return (success, missing)
+
+    def verify(self, revs, contents=False):
+        '''Verify the existence (and, optionally, contents) of every big
+        file revision referenced by every changeset in revs.
+        Return 0 if all is well, non-zero on any errors.'''
+        write = self.ui.write
+        failed = False
+
+        write(_('searching %d changesets for largefiles\n') % len(revs))
+        verified = set()                # set of (filename, filenode) tuples
+
+        for rev in revs:
+            cctx = self.repo[rev]
+            cset = "%d:%s" % (cctx.rev(), node.short(cctx.node()))
+
+            failed = lfutil.any_(self._verifyfile(
+                cctx, cset, contents, standin, verified) for standin in cctx)
+
+        num_revs = len(verified)
+        num_lfiles = len(set([fname for (fname, fnode) in verified]))
+        if contents:
+            write(_('verified contents of %d revisions of %d largefiles\n')
+                  % (num_revs, num_lfiles))
+        else:
+            write(_('verified existence of %d revisions of %d largefiles\n')
+                  % (num_revs, num_lfiles))
+
+        return int(failed)
+
+    def _getfile(self, tmpfile, filename, hash):
+        '''Fetch one revision of one file from the store and write it
+        to tmpfile.  Compute the hash of the file on-the-fly as it
+        downloads and return the binary hash.  Close tmpfile.  Raise
+        StoreError if unable to download the file (e.g. it does not
+        exist in the store).'''
+        raise NotImplementedError('abstract method')
+
+    def _verifyfile(self, cctx, cset, contents, standin, verified):
+        '''Perform the actual verification of a file in the store.
+        '''
+        raise NotImplementedError('abstract method')
+
+import localstore, wirestore
+
+_storeprovider = {
+    'file':  [localstore.localstore],
+    'http':  [wirestore.wirestore],
+    'https': [wirestore.wirestore],
+    'ssh': [wirestore.wirestore],
+    }
+
+_scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
+
+# During clone this function is passed the src's ui object
+# but it needs the dest's ui object so it can read out of
+# the config file. Use repo.ui instead.
+def _openstore(repo, remote=None, put=False):
+    ui = repo.ui
+
+    if not remote:
+        path = (getattr(repo, 'lfpullsource', None) or
+                ui.expandpath('default-push', 'default'))
+
+        # ui.expandpath() leaves 'default-push' and 'default' alone if
+        # they cannot be expanded: fallback to the empty string,
+        # meaning the current directory.
+        if path == 'default-push' or path == 'default':
+            path = ''
+            remote = repo
+        else:
+            remote = hg.peer(repo, {}, path)
+
+    # The path could be a scheme so use Mercurial's normal functionality
+    # to resolve the scheme to a repository and use its path
+    path = util.safehasattr(remote, 'url') and remote.url() or remote.path
+
+    match = _scheme_re.match(path)
+    if not match:                       # regular filesystem path
+        scheme = 'file'
+    else:
+        scheme = match.group(1)
+
+    try:
+        storeproviders = _storeprovider[scheme]
+    except KeyError:
+        raise util.Abort(_('unsupported URL scheme %r') % scheme)
+
+    for class_obj in storeproviders:
+        try:
+            return class_obj(ui, repo, remote)
+        except lfutil.storeprotonotcapable:
+            pass
+
+    raise util.Abort(_('%s does not appear to be a largefile store'), path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/design.txt	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,49 @@
+= largefiles - manage large binary files =
+This extension is based off of Greg Ward's bfiles extension which can be found
+at http://mercurial.selenic.com/wiki/BfilesExtension.
+
+== The largefile store ==
+
+largefile stores are, in the typical use case, centralized servers that have
+every past revision of a given binary file.  Each largefile is identified by
+its sha1 hash, and all interactions with the store take one of the following
+forms.
+
+-Download a bfile with this hash
+-Upload a bfile with this hash
+-Check if the store has a bfile with this hash
+
+largefiles stores can take one of two forms:
+
+-Directories on a network file share
+-Mercurial wireproto servers, either via ssh or http (hgweb)
+
+== The Local Repository ==
+
+The local repository has a largefile cache in .hg/largefiles which holds a
+subset of the largefiles needed. On a clone only the largefiles at tip are
+downloaded. When largefiles are downloaded from the central store, a copy is
+saved in this store.
+
+== The Global Cache ==
+
+largefiles in a local repository cache are hardlinked to files in the global
+cache. Before a file is downloaded we check if it is in the global cache.
+
+== Implementation Details ==
+
+Each largefile has a standin which is in .hglf. The standin is tracked by
+Mercurial.  The standin contains the SHA1 hash of the largefile. When a
+largefile is added/removed/copied/renamed/etc the same operation is applied to
+the standin. Thus the history of the standin is the history of the largefile.
+
+For performance reasons, the contents of a standin are only updated before a
+commit.  Standins are added/removed/copied/renamed from add/remove/copy/rename
+Mercurial commands but their contents will not be updated. The contents of a
+standin will always be the hash of the largefile as of the last commit. To
+support some commands (revert) some standins are temporarily updated but will
+be changed back after the command is finished.
+
+A Mercurial dirstate object tracks the state of the largefiles. The dirstate
+uses the last modified time and current size to detect if a file has changed
+(without reading the entire contents of the file).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/lfcommands.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,481 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''High-level command function for lfconvert, plus the cmdtable.'''
+
+import os
+import shutil
+
+from mercurial import util, match as match_, hg, node, context, error
+from mercurial.i18n import _
+
+import lfutil
+import basestore
+
+# -- Commands ----------------------------------------------------------
+
+def lfconvert(ui, src, dest, *pats, **opts):
+    '''convert a normal repository to a largefiles repository
+
+    Convert repository SOURCE to a new repository DEST, identical to
+    SOURCE except that certain files will be converted as largefiles:
+    specifically, any file that matches any PATTERN *or* whose size is
+    above the minimum size threshold is converted as a largefile. The
+    size used to determine whether or not to track a file as a
+    largefile is the size of the first version of the file. The
+    minimum size can be specified either with --size or in
+    configuration as ``largefiles.size``.
+
+    After running this command you will need to make sure that
+    largefiles is enabled anywhere you intend to push the new
+    repository.
+
+    Use --tonormal to convert largefiles back to normal files; after
+    this, the DEST repository can be used without largefiles at all.'''
+
+    if opts['tonormal']:
+        tolfile = False
+    else:
+        tolfile = True
+        size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
+    try:
+        rsrc = hg.repository(ui, src)
+        if not rsrc.local():
+            raise util.Abort(_('%s is not a local Mercurial repo') % src)
+    except error.RepoError, err:
+        ui.traceback()
+        raise util.Abort(err.args[0])
+    if os.path.exists(dest):
+        if not os.path.isdir(dest):
+            raise util.Abort(_('destination %s already exists') % dest)
+        elif os.listdir(dest):
+            raise util.Abort(_('destination %s is not empty') % dest)
+    try:
+        ui.status(_('initializing destination %s\n') % dest)
+        rdst = hg.repository(ui, dest, create=True)
+        if not rdst.local():
+            raise util.Abort(_('%s is not a local Mercurial repo') % dest)
+    except error.RepoError:
+        ui.traceback()
+        raise util.Abort(_('%s is not a repo') % dest)
+
+    success = False
+    try:
+        # Lock destination to prevent modification while it is converted to.
+        # Don't need to lock src because we are just reading from its history
+        # which can't change.
+        dst_lock = rdst.lock()
+
+        # Get a list of all changesets in the source.  The easy way to do this
+        # is to simply walk the changelog, using changelog.nodesbewteen().
+        # Take a look at mercurial/revlog.py:639 for more details.
+        # Use a generator instead of a list to decrease memory usage
+        ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
+            rsrc.heads())[0])
+        revmap = {node.nullid: node.nullid}
+        if tolfile:
+            lfiles = set()
+            normalfiles = set()
+            if not pats:
+                pats = ui.config(lfutil.longname, 'patterns', default=())
+                if pats:
+                    pats = pats.split(' ')
+            if pats:
+                matcher = match_.match(rsrc.root, '', list(pats))
+            else:
+                matcher = None
+
+            lfiletohash = {}
+            for ctx in ctxs:
+                ui.progress(_('converting revisions'), ctx.rev(),
+                    unit=_('revision'), total=rsrc['tip'].rev())
+                _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
+                    lfiles, normalfiles, matcher, size, lfiletohash)
+            ui.progress(_('converting revisions'), None)
+
+            if os.path.exists(rdst.wjoin(lfutil.shortname)):
+                shutil.rmtree(rdst.wjoin(lfutil.shortname))
+
+            for f in lfiletohash.keys():
+                if os.path.isfile(rdst.wjoin(f)):
+                    os.unlink(rdst.wjoin(f))
+                try:
+                    os.removedirs(os.path.dirname(rdst.wjoin(f)))
+                except OSError:
+                    pass
+
+        else:
+            for ctx in ctxs:
+                ui.progress(_('converting revisions'), ctx.rev(),
+                    unit=_('revision'), total=rsrc['tip'].rev())
+                _addchangeset(ui, rsrc, rdst, ctx, revmap)
+
+            ui.progress(_('converting revisions'), None)
+        success = True
+    finally:
+        if not success:
+            # we failed, remove the new directory
+            shutil.rmtree(rdst.root)
+        dst_lock.release()
+
+def _addchangeset(ui, rsrc, rdst, ctx, revmap):
+ # Convert src parents to dst parents
+    parents = []
+    for p in ctx.parents():
+        parents.append(revmap[p.node()])
+    while len(parents) < 2:
+        parents.append(node.nullid)
+
+    # Generate list of changed files
+    files = set(ctx.files())
+    if node.nullid not in parents:
+        mc = ctx.manifest()
+        mp1 = ctx.parents()[0].manifest()
+        mp2 = ctx.parents()[1].manifest()
+        files |= (set(mp1) | set(mp2)) - set(mc)
+        for f in mc:
+            if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
+                files.add(f)
+
+    def getfilectx(repo, memctx, f):
+        if lfutil.standin(f) in files:
+            # if the file isn't in the manifest then it was removed
+            # or renamed, raise IOError to indicate this
+            try:
+                fctx = ctx.filectx(lfutil.standin(f))
+            except error.LookupError:
+                raise IOError()
+            renamed = fctx.renamed()
+            if renamed:
+                renamed = lfutil.splitstandin(renamed[0])
+
+            hash = fctx.data().strip()
+            path = lfutil.findfile(rsrc, hash)
+            ### TODO: What if the file is not cached?
+            data = ''
+            fd = None
+            try:
+                fd = open(path, 'rb')
+                data = fd.read()
+            finally:
+                if fd:
+                    fd.close()
+            return context.memfilectx(f, data, 'l' in fctx.flags(),
+                                      'x' in fctx.flags(), renamed)
+        else:
+            try:
+                fctx = ctx.filectx(f)
+            except error.LookupError:
+                raise IOError()
+            renamed = fctx.renamed()
+            if renamed:
+                renamed = renamed[0]
+            data = fctx.data()
+            if f == '.hgtags':
+                newdata = []
+                for line in data.splitlines():
+                    id, name = line.split(' ', 1)
+                    newdata.append('%s %s\n' % (node.hex(revmap[node.bin(id)]),
+                        name))
+                data = ''.join(newdata)
+            return context.memfilectx(f, data, 'l' in fctx.flags(),
+                                      'x' in fctx.flags(), renamed)
+
+    dstfiles = []
+    for file in files:
+        if lfutil.isstandin(file):
+            dstfiles.append(lfutil.splitstandin(file))
+        else:
+            dstfiles.append(file)
+    # Commit
+    mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
+                          getfilectx, ctx.user(), ctx.date(), ctx.extra())
+    ret = rdst.commitctx(mctx)
+    rdst.dirstate.setparents(ret)
+    revmap[ctx.node()] = rdst.changelog.tip()
+
+def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
+        matcher, size, lfiletohash):
+    # Convert src parents to dst parents
+    parents = []
+    for p in ctx.parents():
+        parents.append(revmap[p.node()])
+    while len(parents) < 2:
+        parents.append(node.nullid)
+
+    # Generate list of changed files
+    files = set(ctx.files())
+    if node.nullid not in parents:
+        mc = ctx.manifest()
+        mp1 = ctx.parents()[0].manifest()
+        mp2 = ctx.parents()[1].manifest()
+        files |= (set(mp1) | set(mp2)) - set(mc)
+        for f in mc:
+            if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
+                files.add(f)
+
+    dstfiles = []
+    for f in files:
+        if f not in lfiles and f not in normalfiles:
+            islfile = _islfile(f, ctx, matcher, size)
+            # If this file was renamed or copied then copy
+            # the lfileness of its predecessor
+            if f in ctx.manifest():
+                fctx = ctx.filectx(f)
+                renamed = fctx.renamed()
+                renamedlfile = renamed and renamed[0] in lfiles
+                islfile |= renamedlfile
+                if 'l' in fctx.flags():
+                    if renamedlfile:
+                        raise util.Abort(
+                            _('Renamed/copied largefile %s becomes symlink')
+                            % f)
+                    islfile = False
+            if islfile:
+                lfiles.add(f)
+            else:
+                normalfiles.add(f)
+
+        if f in lfiles:
+            dstfiles.append(lfutil.standin(f))
+            # largefile in manifest if it has not been removed/renamed
+            if f in ctx.manifest():
+                if 'l' in ctx.filectx(f).flags():
+                    if renamed and renamed[0] in lfiles:
+                        raise util.Abort(_('largefile %s becomes symlink') % f)
+
+                # largefile was modified, update standins
+                fullpath = rdst.wjoin(f)
+                lfutil.createdir(os.path.dirname(fullpath))
+                m = util.sha1('')
+                m.update(ctx[f].data())
+                hash = m.hexdigest()
+                if f not in lfiletohash or lfiletohash[f] != hash:
+                    try:
+                        fd = open(fullpath, 'wb')
+                        fd.write(ctx[f].data())
+                    finally:
+                        if fd:
+                            fd.close()
+                    executable = 'x' in ctx[f].flags()
+                    os.chmod(fullpath, lfutil.getmode(executable))
+                    lfutil.writestandin(rdst, lfutil.standin(f), hash,
+                        executable)
+                    lfiletohash[f] = hash
+        else:
+            # normal file
+            dstfiles.append(f)
+
+    def getfilectx(repo, memctx, f):
+        if lfutil.isstandin(f):
+            # if the file isn't in the manifest then it was removed
+            # or renamed, raise IOError to indicate this
+            srcfname = lfutil.splitstandin(f)
+            try:
+                fctx = ctx.filectx(srcfname)
+            except error.LookupError:
+                raise IOError()
+            renamed = fctx.renamed()
+            if renamed:
+                # standin is always a largefile because largefile-ness
+                # doesn't change after rename or copy
+                renamed = lfutil.standin(renamed[0])
+
+            return context.memfilectx(f, lfiletohash[srcfname], 'l' in
+                fctx.flags(), 'x' in fctx.flags(), renamed)
+        else:
+            try:
+                fctx = ctx.filectx(f)
+            except error.LookupError:
+                raise IOError()
+            renamed = fctx.renamed()
+            if renamed:
+                renamed = renamed[0]
+
+            data = fctx.data()
+            if f == '.hgtags':
+                newdata = []
+                for line in data.splitlines():
+                    id, name = line.split(' ', 1)
+                    newdata.append('%s %s\n' % (node.hex(revmap[node.bin(id)]),
+                        name))
+                data = ''.join(newdata)
+            return context.memfilectx(f, data, 'l' in fctx.flags(),
+                                      'x' in fctx.flags(), renamed)
+
+    # Commit
+    mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
+                          getfilectx, ctx.user(), ctx.date(), ctx.extra())
+    ret = rdst.commitctx(mctx)
+    rdst.dirstate.setparents(ret)
+    revmap[ctx.node()] = rdst.changelog.tip()
+
+def _islfile(file, ctx, matcher, size):
+    '''Return true if file should be considered a largefile, i.e.
+    matcher matches it or it is larger than size.'''
+    # never store special .hg* files as largefiles
+    if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
+        return False
+    if matcher and matcher(file):
+        return True
+    try:
+        return ctx.filectx(file).size() >= size * 1024 * 1024
+    except error.LookupError:
+        return False
+
+def uploadlfiles(ui, rsrc, rdst, files):
+    '''upload largefiles to the central store'''
+
+    # Don't upload locally. All largefiles are in the system wide cache
+    # so the other repo can just get them from there.
+    if not files or rdst.local():
+        return
+
+    store = basestore._openstore(rsrc, rdst, put=True)
+
+    at = 0
+    files = filter(lambda h: not store.exists(h), files)
+    for hash in files:
+        ui.progress(_('uploading largefiles'), at, unit='largefile',
+                    total=len(files))
+        source = lfutil.findfile(rsrc, hash)
+        if not source:
+            raise util.Abort(_('largefile %s missing from store'
+                               ' (needs to be uploaded)') % hash)
+        # XXX check for errors here
+        store.put(source, hash)
+        at += 1
+    ui.progress(_('uploading largefiles'), None)
+
+def verifylfiles(ui, repo, all=False, contents=False):
+    '''Verify that every big file revision in the current changeset
+    exists in the central store.  With --contents, also verify that
+    the contents of each big file revision are correct (SHA-1 hash
+    matches the revision ID).  With --all, check every changeset in
+    this repository.'''
+    if all:
+        # Pass a list to the function rather than an iterator because we know a
+        # list will work.
+        revs = range(len(repo))
+    else:
+        revs = ['.']
+
+    store = basestore._openstore(repo)
+    return store.verify(revs, contents=contents)
+
+def cachelfiles(ui, repo, node):
+    '''cachelfiles ensures that all largefiles needed by the specified revision
+    are present in the repository's largefile cache.
+
+    returns a tuple (cached, missing).  cached is the list of files downloaded
+    by this operation; missing is the list of files that were needed but could
+    not be found.'''
+    lfiles = lfutil.listlfiles(repo, node)
+    toget = []
+
+    for lfile in lfiles:
+        expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
+        # if it exists and its hash matches, it might have been locally
+        # modified before updating and the user chose 'local'.  in this case,
+        # it will not be in any store, so don't look for it.
+        if ((not os.path.exists(repo.wjoin(lfile)) or
+             expectedhash != lfutil.hashfile(repo.wjoin(lfile))) and
+            not lfutil.findfile(repo, expectedhash)):
+            toget.append((lfile, expectedhash))
+
+    if toget:
+        store = basestore._openstore(repo)
+        ret = store.get(toget)
+        return ret
+
+    return ([], [])
+
+def updatelfiles(ui, repo, filelist=None, printmessage=True):
+    wlock = repo.wlock()
+    try:
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
+
+        if filelist is not None:
+            lfiles = [f for f in lfiles if f in filelist]
+
+        printed = False
+        if printmessage and lfiles:
+            ui.status(_('getting changed largefiles\n'))
+            printed = True
+            cachelfiles(ui, repo, '.')
+
+        updated, removed = 0, 0
+        for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
+            # increment the appropriate counter according to _updatelfile's
+            # return value
+            updated += i > 0 and i or 0
+            removed -= i < 0 and i or 0
+            if printmessage and (removed or updated) and not printed:
+                ui.status(_('getting changed largefiles\n'))
+                printed = True
+
+        lfdirstate.write()
+        if printed and printmessage:
+            ui.status(_('%d largefiles updated, %d removed\n') % (updated,
+                removed))
+    finally:
+        wlock.release()
+
+def _updatelfile(repo, lfdirstate, lfile):
+    '''updates a single largefile and copies the state of its standin from
+    the repository's dirstate to its state in the lfdirstate.
+
+    returns 1 if the file was modified, -1 if the file was removed, 0 if the
+    file was unchanged, and None if the needed largefile was missing from the
+    cache.'''
+    ret = 0
+    abslfile = repo.wjoin(lfile)
+    absstandin = repo.wjoin(lfutil.standin(lfile))
+    if os.path.exists(absstandin):
+        if os.path.exists(absstandin+'.orig'):
+            shutil.copyfile(abslfile, abslfile+'.orig')
+        expecthash = lfutil.readstandin(repo, lfile)
+        if (expecthash != '' and
+            (not os.path.exists(abslfile) or
+             expecthash != lfutil.hashfile(abslfile))):
+            if not lfutil.copyfromcache(repo, expecthash, lfile):
+                return None # don't try to set the mode or update the dirstate
+            ret = 1
+        mode = os.stat(absstandin).st_mode
+        if mode != os.stat(abslfile).st_mode:
+            os.chmod(abslfile, mode)
+            ret = 1
+    else:
+        if os.path.exists(abslfile):
+            os.unlink(abslfile)
+            ret = -1
+    state = repo.dirstate[lfutil.standin(lfile)]
+    if state == 'n':
+        lfdirstate.normal(lfile)
+    elif state == 'r':
+        lfdirstate.remove(lfile)
+    elif state == 'a':
+        lfdirstate.add(lfile)
+    elif state == '?':
+        lfdirstate.drop(lfile)
+    return ret
+
+# -- hg commands declarations ------------------------------------------------
+
+cmdtable = {
+    'lfconvert': (lfconvert,
+                  [('s', 'size', '',
+                    _('minimum size (MB) for files to be converted '
+                      'as largefiles'),
+                    'SIZE'),
+                  ('', 'tonormal', False,
+                   _('convert from a largefiles repo to a normal repo')),
+                  ],
+                  _('hg lfconvert SOURCE DEST [FILE ...]')),
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/lfutil.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,448 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''largefiles utility code: must not import other modules in this package.'''
+
+import os
+import errno
+import shutil
+import stat
+import hashlib
+
+from mercurial import dirstate, httpconnection, match as match_, util, scmutil
+from mercurial.i18n import _
+
+shortname = '.hglf'
+longname = 'largefiles'
+
+
+# -- Portability wrappers ----------------------------------------------
+
+def dirstate_walk(dirstate, matcher, unknown=False, ignored=False):
+    return dirstate.walk(matcher, [], unknown, ignored)
+
+def repo_add(repo, list):
+    add = repo[None].add
+    return add(list)
+
+def repo_remove(repo, list, unlink=False):
+    def remove(list, unlink):
+        wlock = repo.wlock()
+        try:
+            if unlink:
+                for f in list:
+                    try:
+                        util.unlinkpath(repo.wjoin(f))
+                    except OSError, inst:
+                        if inst.errno != errno.ENOENT:
+                            raise
+            repo[None].forget(list)
+        finally:
+            wlock.release()
+    return remove(list, unlink=unlink)
+
+def repo_forget(repo, list):
+    forget = repo[None].forget
+    return forget(list)
+
+def findoutgoing(repo, remote, force):
+    from mercurial import discovery
+    common, _anyinc, _heads = discovery.findcommonincoming(repo,
+        remote, force=force)
+    return repo.changelog.findmissing(common)
+
+# -- Private worker functions ------------------------------------------
+
+def getminsize(ui, assumelfiles, opt, default=10):
+    lfsize = opt
+    if not lfsize and assumelfiles:
+        lfsize = ui.config(longname, 'size', default=default)
+    if lfsize:
+        try:
+            lfsize = float(lfsize)
+        except ValueError:
+            raise util.Abort(_('largefiles: size must be number (not %s)\n')
+                             % lfsize)
+    if lfsize is None:
+        raise util.Abort(_('minimum size for largefiles must be specified'))
+    return lfsize
+
+def link(src, dest):
+    try:
+        util.oslink(src, dest)
+    except OSError:
+        # if hardlinks fail, fallback on copy
+        shutil.copyfile(src, dest)
+        os.chmod(dest, os.stat(src).st_mode)
+
+def systemcachepath(ui, hash):
+    path = ui.config(longname, 'systemcache', None)
+    if path:
+        path = os.path.join(path, hash)
+    else:
+        if os.name == 'nt':
+            appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
+            path = os.path.join(appdata, longname, hash)
+        elif os.name == 'posix':
+            path = os.path.join(os.getenv('HOME'), '.' + longname, hash)
+        else:
+            raise util.Abort(_('unknown operating system: %s\n') % os.name)
+    return path
+
+def insystemcache(ui, hash):
+    return os.path.exists(systemcachepath(ui, hash))
+
+def findfile(repo, hash):
+    if incache(repo, hash):
+        repo.ui.note(_('Found %s in cache\n') % hash)
+        return cachepath(repo, hash)
+    if insystemcache(repo.ui, hash):
+        repo.ui.note(_('Found %s in system cache\n') % hash)
+        return systemcachepath(repo.ui, hash)
+    return None
+
+class largefiles_dirstate(dirstate.dirstate):
+    def __getitem__(self, key):
+        return super(largefiles_dirstate, self).__getitem__(unixpath(key))
+    def normal(self, f):
+        return super(largefiles_dirstate, self).normal(unixpath(f))
+    def remove(self, f):
+        return super(largefiles_dirstate, self).remove(unixpath(f))
+    def add(self, f):
+        return super(largefiles_dirstate, self).add(unixpath(f))
+    def drop(self, f):
+        return super(largefiles_dirstate, self).drop(unixpath(f))
+    def forget(self, f):
+        return super(largefiles_dirstate, self).forget(unixpath(f))
+
+def openlfdirstate(ui, repo):
+    '''
+    Return a dirstate object that tracks largefiles: i.e. its root is
+    the repo root, but it is saved in .hg/largefiles/dirstate.
+    '''
+    admin = repo.join(longname)
+    opener = scmutil.opener(admin)
+    if util.safehasattr(repo.dirstate, '_validate'):
+        lfdirstate = largefiles_dirstate(opener, ui, repo.root,
+            repo.dirstate._validate)
+    else:
+        lfdirstate = largefiles_dirstate(opener, ui, repo.root)
+
+    # If the largefiles dirstate does not exist, populate and create
+    # it. This ensures that we create it on the first meaningful
+    # largefiles operation in a new clone. It also gives us an easy
+    # way to forcibly rebuild largefiles state:
+    #   rm .hg/largefiles/dirstate && hg status
+    # Or even, if things are really messed up:
+    #   rm -rf .hg/largefiles && hg status
+    if not os.path.exists(os.path.join(admin, 'dirstate')):
+        util.makedirs(admin)
+        matcher = getstandinmatcher(repo)
+        for standin in dirstate_walk(repo.dirstate, matcher):
+            lfile = splitstandin(standin)
+            hash = readstandin(repo, lfile)
+            lfdirstate.normallookup(lfile)
+            try:
+                if hash == hashfile(lfile):
+                    lfdirstate.normal(lfile)
+            except IOError, err:
+                if err.errno != errno.ENOENT:
+                    raise
+
+        lfdirstate.write()
+
+    return lfdirstate
+
+def lfdirstate_status(lfdirstate, repo, rev):
+    wlock = repo.wlock()
+    try:
+        match = match_.always(repo.root, repo.getcwd())
+        s = lfdirstate.status(match, [], False, False, False)
+        unsure, modified, added, removed, missing, unknown, ignored, clean = s
+        for lfile in unsure:
+            if repo[rev][standin(lfile)].data().strip() != \
+                    hashfile(repo.wjoin(lfile)):
+                modified.append(lfile)
+            else:
+                clean.append(lfile)
+                lfdirstate.normal(lfile)
+        lfdirstate.write()
+    finally:
+        wlock.release()
+    return (modified, added, removed, missing, unknown, ignored, clean)
+
+def listlfiles(repo, rev=None, matcher=None):
+    '''return a list of largefiles in the working copy or the
+    specified changeset'''
+
+    if matcher is None:
+        matcher = getstandinmatcher(repo)
+
+    # ignore unknown files in working directory
+    return [splitstandin(f)
+            for f in repo[rev].walk(matcher)
+            if rev is not None or repo.dirstate[f] != '?']
+
+def incache(repo, hash):
+    return os.path.exists(cachepath(repo, hash))
+
+def createdir(dir):
+    if not os.path.exists(dir):
+        os.makedirs(dir)
+
+def cachepath(repo, hash):
+    return repo.join(os.path.join(longname, hash))
+
+def copyfromcache(repo, hash, filename):
+    '''Copy the specified largefile from the repo or system cache to
+    filename in the repository. Return true on success or false if the
+    file was not found in either cache (which should not happened:
+    this is meant to be called only after ensuring that the needed
+    largefile exists in the cache).'''
+    path = findfile(repo, hash)
+    if path is None:
+        return False
+    util.makedirs(os.path.dirname(repo.wjoin(filename)))
+    shutil.copy(path, repo.wjoin(filename))
+    return True
+
+def copytocache(repo, rev, file, uploaded=False):
+    hash = readstandin(repo, file)
+    if incache(repo, hash):
+        return
+    copytocacheabsolute(repo, repo.wjoin(file), hash)
+
+def copytocacheabsolute(repo, file, hash):
+    createdir(os.path.dirname(cachepath(repo, hash)))
+    if insystemcache(repo.ui, hash):
+        link(systemcachepath(repo.ui, hash), cachepath(repo, hash))
+    else:
+        shutil.copyfile(file, cachepath(repo, hash))
+        os.chmod(cachepath(repo, hash), os.stat(file).st_mode)
+        linktosystemcache(repo, hash)
+
+def linktosystemcache(repo, hash):
+    createdir(os.path.dirname(systemcachepath(repo.ui, hash)))
+    link(cachepath(repo, hash), systemcachepath(repo.ui, hash))
+
+def getstandinmatcher(repo, pats=[], opts={}):
+    '''Return a match object that applies pats to the standin directory'''
+    standindir = repo.pathto(shortname)
+    if pats:
+        # patterns supplied: search standin directory relative to current dir
+        cwd = repo.getcwd()
+        if os.path.isabs(cwd):
+            # cwd is an absolute path for hg -R <reponame>
+            # work relative to the repository root in this case
+            cwd = ''
+        pats = [os.path.join(standindir, cwd, pat) for pat in pats]
+    elif os.path.isdir(standindir):
+        # no patterns: relative to repo root
+        pats = [standindir]
+    else:
+        # no patterns and no standin dir: return matcher that matches nothing
+        match = match_.match(repo.root, None, [], exact=True)
+        match.matchfn = lambda f: False
+        return match
+    return getmatcher(repo, pats, opts, showbad=False)
+
+def getmatcher(repo, pats=[], opts={}, showbad=True):
+    '''Wrapper around scmutil.match() that adds showbad: if false,
+    neuter the match object's bad() method so it does not print any
+    warnings about missing files or directories.'''
+    match = scmutil.match(repo[None], pats, opts)
+
+    if not showbad:
+        match.bad = lambda f, msg: None
+    return match
+
+def composestandinmatcher(repo, rmatcher):
+    '''Return a matcher that accepts standins corresponding to the
+    files accepted by rmatcher. Pass the list of files in the matcher
+    as the paths specified by the user.'''
+    smatcher = getstandinmatcher(repo, rmatcher.files())
+    isstandin = smatcher.matchfn
+    def composed_matchfn(f):
+        return isstandin(f) and rmatcher.matchfn(splitstandin(f))
+    smatcher.matchfn = composed_matchfn
+
+    return smatcher
+
+def standin(filename):
+    '''Return the repo-relative path to the standin for the specified big
+    file.'''
+    # Notes:
+    # 1) Most callers want an absolute path, but _create_standin() needs
+    #    it repo-relative so lfadd() can pass it to repo_add().  So leave
+    #    it up to the caller to use repo.wjoin() to get an absolute path.
+    # 2) Join with '/' because that's what dirstate always uses, even on
+    #    Windows. Change existing separator to '/' first in case we are
+    #    passed filenames from an external source (like the command line).
+    return shortname + '/' + filename.replace(os.sep, '/')
+
+def isstandin(filename):
+    '''Return true if filename is a big file standin. filename must be
+    in Mercurial's internal form (slash-separated).'''
+    return filename.startswith(shortname + '/')
+
+def splitstandin(filename):
+    # Split on / because that's what dirstate always uses, even on Windows.
+    # Change local separator to / first just in case we are passed filenames
+    # from an external source (like the command line).
+    bits = filename.replace(os.sep, '/').split('/', 1)
+    if len(bits) == 2 and bits[0] == shortname:
+        return bits[1]
+    else:
+        return None
+
+def updatestandin(repo, standin):
+    file = repo.wjoin(splitstandin(standin))
+    if os.path.exists(file):
+        hash = hashfile(file)
+        executable = getexecutable(file)
+        writestandin(repo, standin, hash, executable)
+
+def readstandin(repo, filename, node=None):
+    '''read hex hash from standin for filename at given node, or working
+    directory if no node is given'''
+    return repo[node][standin(filename)].data().strip()
+
+def writestandin(repo, standin, hash, executable):
+    '''write hash to <repo.root>/<standin>'''
+    writehash(hash, repo.wjoin(standin), executable)
+
+def copyandhash(instream, outfile):
+    '''Read bytes from instream (iterable) and write them to outfile,
+    computing the SHA-1 hash of the data along the way.  Close outfile
+    when done and return the binary hash.'''
+    hasher = util.sha1('')
+    for data in instream:
+        hasher.update(data)
+        outfile.write(data)
+
+    # Blecch: closing a file that somebody else opened is rude and
+    # wrong. But it's so darn convenient and practical! After all,
+    # outfile was opened just to copy and hash.
+    outfile.close()
+
+    return hasher.digest()
+
+def hashrepofile(repo, file):
+    return hashfile(repo.wjoin(file))
+
+def hashfile(file):
+    if not os.path.exists(file):
+        return ''
+    hasher = util.sha1('')
+    fd = open(file, 'rb')
+    for data in blockstream(fd):
+        hasher.update(data)
+    fd.close()
+    return hasher.hexdigest()
+
+class limitreader(object):
+    def __init__(self, f, limit):
+        self.f = f
+        self.limit = limit
+
+    def read(self, length):
+        if self.limit == 0:
+            return ''
+        length = length > self.limit and self.limit or length
+        self.limit -= length
+        return self.f.read(length)
+
+    def close(self):
+        pass
+
+def blockstream(infile, blocksize=128 * 1024):
+    """Generator that yields blocks of data from infile and closes infile."""
+    while True:
+        data = infile.read(blocksize)
+        if not data:
+            break
+        yield data
+    # same blecch as copyandhash() above
+    infile.close()
+
+def readhash(filename):
+    rfile = open(filename, 'rb')
+    hash = rfile.read(40)
+    rfile.close()
+    if len(hash) < 40:
+        raise util.Abort(_('bad hash in \'%s\' (only %d bytes long)')
+                         % (filename, len(hash)))
+    return hash
+
+def writehash(hash, filename, executable):
+    util.makedirs(os.path.dirname(filename))
+    if os.path.exists(filename):
+        os.unlink(filename)
+    wfile = open(filename, 'wb')
+
+    try:
+        wfile.write(hash)
+        wfile.write('\n')
+    finally:
+        wfile.close()
+    if os.path.exists(filename):
+        os.chmod(filename, getmode(executable))
+
+def getexecutable(filename):
+    mode = os.stat(filename).st_mode
+    return ((mode & stat.S_IXUSR) and
+            (mode & stat.S_IXGRP) and
+            (mode & stat.S_IXOTH))
+
+def getmode(executable):
+    if executable:
+        return 0755
+    else:
+        return 0644
+
+def urljoin(first, second, *arg):
+    def join(left, right):
+        if not left.endswith('/'):
+            left += '/'
+        if right.startswith('/'):
+            right = right[1:]
+        return left + right
+
+    url = join(first, second)
+    for a in arg:
+        url = join(url, a)
+    return url
+
+def hexsha1(data):
+    """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
+    object data"""
+    h = hashlib.sha1()
+    for chunk in util.filechunkiter(data):
+        h.update(chunk)
+    return h.hexdigest()
+
+def httpsendfile(ui, filename):
+    return httpconnection.httpsendfile(ui, filename, 'rb')
+
+def unixpath(path):
+    '''Return a version of path normalized for use with the lfdirstate.'''
+    return os.path.normpath(path).replace(os.sep, '/')
+
+def islfilesrepo(repo):
+    return ('largefiles' in repo.requirements and
+            any_(shortname + '/' in f[0] for f in repo.store.datafiles()))
+
+def any_(gen):
+    for x in gen:
+        if x:
+            return True
+    return False
+
+class storeprotonotcapable(BaseException):
+    def __init__(self, storetypes):
+        self.storetypes = storetypes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/localstore.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,71 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''store class for local filesystem'''
+
+import os
+
+from mercurial import util
+from mercurial.i18n import _
+
+import lfutil
+import basestore
+
+class localstore(basestore.basestore):
+    '''Because there is a system-wide cache, the local store always
+    uses that cache. Since the cache is updated elsewhere, we can
+    just read from it here as if it were the store.'''
+
+    def __init__(self, ui, repo, remote):
+        url = os.path.join(remote.path, '.hg', lfutil.longname)
+        super(localstore, self).__init__(ui, repo, util.expandpath(url))
+
+    def put(self, source, filename, hash):
+        '''Any file that is put must already be in the system-wide
+        cache so do nothing.'''
+        return
+
+    def exists(self, hash):
+        return lfutil.insystemcache(self.repo.ui, hash)
+
+    def _getfile(self, tmpfile, filename, hash):
+        if lfutil.insystemcache(self.ui, hash):
+            return lfutil.systemcachepath(self.ui, hash)
+        raise basestore.StoreError(filename, hash, '',
+            _("Can't get file locally"))
+
+    def _verifyfile(self, cctx, cset, contents, standin, verified):
+        filename = lfutil.splitstandin(standin)
+        if not filename:
+            return False
+        fctx = cctx[standin]
+        key = (filename, fctx.filenode())
+        if key in verified:
+            return False
+
+        expecthash = fctx.data()[0:40]
+        verified.add(key)
+        if not lfutil.insystemcache(self.ui, expecthash):
+            self.ui.warn(
+                _('changeset %s: %s missing\n'
+                  '  (looked for hash %s)\n')
+                % (cset, filename, expecthash))
+            return True                 # failed
+
+        if contents:
+            storepath = lfutil.systemcachepath(self.ui, expecthash)
+            actualhash = lfutil.hashfile(storepath)
+            if actualhash != expecthash:
+                self.ui.warn(
+                    _('changeset %s: %s: contents differ\n'
+                      '  (%s:\n'
+                      '  expected hash %s,\n'
+                      '  but got %s)\n')
+                    % (cset, filename, storepath, expecthash, actualhash))
+                return True             # failed
+        return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/overrides.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,830 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''Overridden Mercurial commands and functions for the largefiles extension'''
+
+import os
+import copy
+
+from mercurial import hg, commands, util, cmdutil, match as match_, node, \
+        archival, error, merge
+from mercurial.i18n import _
+from mercurial.node import hex
+from hgext import rebase
+import lfutil
+
+try:
+    from mercurial import scmutil
+except ImportError:
+    pass
+
+import lfutil
+import lfcommands
+
+def installnormalfilesmatchfn(manifest):
+    '''overrides scmutil.match so that the matcher it returns will ignore all
+    largefiles'''
+    oldmatch = None # for the closure
+    def override_match(repo, pats=[], opts={}, globbed=False,
+            default='relpath'):
+        match = oldmatch(repo, pats, opts, globbed, default)
+        m = copy.copy(match)
+        notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
+                manifest)
+        m._files = filter(notlfile, m._files)
+        m._fmap = set(m._files)
+        orig_matchfn = m.matchfn
+        m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
+        return m
+    oldmatch = installmatchfn(override_match)
+
+def installmatchfn(f):
+    oldmatch = scmutil.match
+    setattr(f, 'oldmatch', oldmatch)
+    scmutil.match = f
+    return oldmatch
+
+def restorematchfn():
+    '''restores scmutil.match to what it was before installnormalfilesmatchfn
+    was called.  no-op if scmutil.match is its original function.
+
+    Note that n calls to installnormalfilesmatchfn will require n calls to
+    restore matchfn to reverse'''
+    scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
+
+# -- Wrappers: modify existing commands --------------------------------
+
+# Add works by going through the files that the user wanted to add and
+# checking if they should be added as largefiles. Then it makes a new
+# matcher which matches only the normal files and runs the original
+# version of add.
+def override_add(orig, ui, repo, *pats, **opts):
+    large = opts.pop('large', None)
+    lfsize = lfutil.getminsize(
+        ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
+
+    lfmatcher = None
+    if os.path.exists(repo.wjoin(lfutil.shortname)):
+        lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
+        if lfpats:
+            lfmatcher = match_.match(repo.root, '', list(lfpats))
+
+    lfnames = []
+    m = scmutil.match(repo[None], pats, opts)
+    m.bad = lambda x, y: None
+    wctx = repo[None]
+    for f in repo.walk(m):
+        exact = m.exact(f)
+        lfile = lfutil.standin(f) in wctx
+        nfile = f in wctx
+        exists = lfile or nfile
+
+        # Don't warn the user when they attempt to add a normal tracked file.
+        # The normal add code will do that for us.
+        if exact and exists:
+            if lfile:
+                ui.warn(_('%s already a largefile\n') % f)
+            continue
+
+        if exact or not exists:
+            abovemin = (lfsize and
+                        os.path.getsize(repo.wjoin(f)) >= lfsize * 1024 * 1024)
+            if large or abovemin or (lfmatcher and lfmatcher(f)):
+                lfnames.append(f)
+                if ui.verbose or not exact:
+                    ui.status(_('adding %s as a largefile\n') % m.rel(f))
+
+    bad = []
+    standins = []
+
+    # Need to lock, otherwise there could be a race condition between
+    # when standins are created and added to the repo.
+    wlock = repo.wlock()
+    try:
+        if not opts.get('dry_run'):
+            lfdirstate = lfutil.openlfdirstate(ui, repo)
+            for f in lfnames:
+                standinname = lfutil.standin(f)
+                lfutil.writestandin(repo, standinname, hash='',
+                    executable=lfutil.getexecutable(repo.wjoin(f)))
+                standins.append(standinname)
+                if lfdirstate[f] == 'r':
+                    lfdirstate.normallookup(f)
+                else:
+                    lfdirstate.add(f)
+            lfdirstate.write()
+            bad += [lfutil.splitstandin(f)
+                    for f in lfutil.repo_add(repo, standins)
+                    if f in m.files()]
+    finally:
+        wlock.release()
+
+    installnormalfilesmatchfn(repo[None].manifest())
+    result = orig(ui, repo, *pats, **opts)
+    restorematchfn()
+
+    return (result == 1 or bad) and 1 or 0
+
+def override_remove(orig, ui, repo, *pats, **opts):
+    manifest = repo[None].manifest()
+    installnormalfilesmatchfn(manifest)
+    orig(ui, repo, *pats, **opts)
+    restorematchfn()
+
+    after, force = opts.get('after'), opts.get('force')
+    if not pats and not after:
+        raise util.Abort(_('no files specified'))
+    m = scmutil.match(repo[None], pats, opts)
+    try:
+        repo.lfstatus = True
+        s = repo.status(match=m, clean=True)
+    finally:
+        repo.lfstatus = False
+    modified, added, deleted, clean = [[f for f in list
+                                        if lfutil.standin(f) in manifest]
+                                       for list in [s[0], s[1], s[3], s[6]]]
+
+    def warn(files, reason):
+        for f in files:
+            ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
+                    % (m.rel(f), reason))
+
+    if force:
+        remove, forget = modified + deleted + clean, added
+    elif after:
+        remove, forget = deleted, []
+        warn(modified + added + clean, _('still exists'))
+    else:
+        remove, forget = deleted + clean, []
+        warn(modified, _('is modified'))
+        warn(added, _('has been marked for add'))
+
+    for f in sorted(remove + forget):
+        if ui.verbose or not m.exact(f):
+            ui.status(_('removing %s\n') % m.rel(f))
+
+    # Need to lock because standin files are deleted then removed from the
+    # repository and we could race inbetween.
+    wlock = repo.wlock()
+    try:
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        for f in remove:
+            if not after:
+                os.unlink(repo.wjoin(f))
+                currentdir = os.path.split(f)[0]
+                while currentdir and not os.listdir(repo.wjoin(currentdir)):
+                    os.rmdir(repo.wjoin(currentdir))
+                    currentdir = os.path.split(currentdir)[0]
+            lfdirstate.remove(f)
+        lfdirstate.write()
+
+        forget = [lfutil.standin(f) for f in forget]
+        remove = [lfutil.standin(f) for f in remove]
+        lfutil.repo_forget(repo, forget)
+        lfutil.repo_remove(repo, remove, unlink=True)
+    finally:
+        wlock.release()
+
+def override_status(orig, ui, repo, *pats, **opts):
+    try:
+        repo.lfstatus = True
+        return orig(ui, repo, *pats, **opts)
+    finally:
+        repo.lfstatus = False
+
+def override_log(orig, ui, repo, *pats, **opts):
+    try:
+        repo.lfstatus = True
+        orig(ui, repo, *pats, **opts)
+    finally:
+        repo.lfstatus = False
+
+def override_verify(orig, ui, repo, *pats, **opts):
+    large = opts.pop('large', False)
+    all = opts.pop('lfa', False)
+    contents = opts.pop('lfc', False)
+
+    result = orig(ui, repo, *pats, **opts)
+    if large:
+        result = result or lfcommands.verifylfiles(ui, repo, all, contents)
+    return result
+
+# Override needs to refresh standins so that update's normal merge
+# will go through properly. Then the other update hook (overriding repo.update)
+# will get the new files. Filemerge is also overriden so that the merge
+# will merge standins correctly.
+def override_update(orig, ui, repo, *pats, **opts):
+    lfdirstate = lfutil.openlfdirstate(ui, repo)
+    s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
+        False, False)
+    (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
+
+    # Need to lock between the standins getting updated and their
+    # largefiles getting updated
+    wlock = repo.wlock()
+    try:
+        if opts['check']:
+            mod = len(modified) > 0
+            for lfile in unsure:
+                standin = lfutil.standin(lfile)
+                if repo['.'][standin].data().strip() != \
+                        lfutil.hashfile(repo.wjoin(lfile)):
+                    mod = True
+                else:
+                    lfdirstate.normal(lfile)
+            lfdirstate.write()
+            if mod:
+                raise util.Abort(_('uncommitted local changes'))
+        # XXX handle removed differently
+        if not opts['clean']:
+            for lfile in unsure + modified + added:
+                lfutil.updatestandin(repo, lfutil.standin(lfile))
+    finally:
+        wlock.release()
+    return orig(ui, repo, *pats, **opts)
+
+# Override filemerge to prompt the user about how they wish to merge
+# largefiles. This will handle identical edits, and copy/rename +
+# edit without prompting the user.
+def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
+    # Use better variable names here. Because this is a wrapper we cannot
+    # change the variable names in the function declaration.
+    fcdest, fcother, fcancestor = fcd, fco, fca
+    if not lfutil.isstandin(orig):
+        return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
+    else:
+        if not fcother.cmp(fcdest): # files identical?
+            return None
+
+        # backwards, use working dir parent as ancestor
+        if fcancestor == fcother:
+            fcancestor = fcdest.parents()[0]
+
+        if orig != fcother.path():
+            repo.ui.status(_('merging %s and %s to %s\n')
+                           % (lfutil.splitstandin(orig),
+                              lfutil.splitstandin(fcother.path()),
+                              lfutil.splitstandin(fcdest.path())))
+        else:
+            repo.ui.status(_('merging %s\n')
+                           % lfutil.splitstandin(fcdest.path()))
+
+        if fcancestor.path() != fcother.path() and fcother.data() == \
+                fcancestor.data():
+            return 0
+        if fcancestor.path() != fcdest.path() and fcdest.data() == \
+                fcancestor.data():
+            repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
+            return 0
+
+        if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
+                             'keep (l)ocal or take (o)ther?') %
+                             lfutil.splitstandin(orig),
+                             (_('&Local'), _('&Other')), 0) == 0:
+            return 0
+        else:
+            repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
+            return 0
+
+# Copy first changes the matchers to match standins instead of
+# largefiles.  Then it overrides util.copyfile in that function it
+# checks if the destination largefile already exists. It also keeps a
+# list of copied files so that the largefiles can be copied and the
+# dirstate updated.
+def override_copy(orig, ui, repo, pats, opts, rename=False):
+    # doesn't remove largefile on rename
+    if len(pats) < 2:
+        # this isn't legal, let the original function deal with it
+        return orig(ui, repo, pats, opts, rename)
+
+    def makestandin(relpath):
+        path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
+        return os.path.join(os.path.relpath('.', repo.getcwd()),
+            lfutil.standin(path))
+
+    fullpats = scmutil.expandpats(pats)
+    dest = fullpats[-1]
+
+    if os.path.isdir(dest):
+        if not os.path.isdir(makestandin(dest)):
+            os.makedirs(makestandin(dest))
+    # This could copy both lfiles and normal files in one command,
+    # but we don't want to do that. First replace their matcher to
+    # only match normal files and run it, then replace it to just
+    # match largefiles and run it again.
+    nonormalfiles = False
+    nolfiles = False
+    try:
+        installnormalfilesmatchfn(repo[None].manifest())
+        result = orig(ui, repo, pats, opts, rename)
+    except util.Abort, e:
+        if str(e) != 'no files to copy':
+            raise e
+        else:
+            nonormalfiles = True
+        result = 0
+    finally:
+        restorematchfn()
+
+    # The first rename can cause our current working directory to be removed.
+    # In that case there is nothing left to copy/rename so just quit.
+    try:
+        repo.getcwd()
+    except OSError:
+        return result
+
+    try:
+        # When we call orig below it creates the standins but we don't add them
+        # to the dir state until later so lock during that time.
+        wlock = repo.wlock()
+
+        manifest = repo[None].manifest()
+        oldmatch = None # for the closure
+        def override_match(repo, pats=[], opts={}, globbed=False,
+                default='relpath'):
+            newpats = []
+            # The patterns were previously mangled to add the standin
+            # directory; we need to remove that now
+            for pat in pats:
+                if match_.patkind(pat) is None and lfutil.shortname in pat:
+                    newpats.append(pat.replace(lfutil.shortname, ''))
+                else:
+                    newpats.append(pat)
+            match = oldmatch(repo, newpats, opts, globbed, default)
+            m = copy.copy(match)
+            lfile = lambda f: lfutil.standin(f) in manifest
+            m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
+            m._fmap = set(m._files)
+            orig_matchfn = m.matchfn
+            m.matchfn = lambda f: (lfutil.isstandin(f) and
+                                   lfile(lfutil.splitstandin(f)) and
+                                   orig_matchfn(lfutil.splitstandin(f)) or
+                                   None)
+            return m
+        oldmatch = installmatchfn(override_match)
+        listpats = []
+        for pat in pats:
+            if match_.patkind(pat) is not None:
+                listpats.append(pat)
+            else:
+                listpats.append(makestandin(pat))
+
+        try:
+            origcopyfile = util.copyfile
+            copiedfiles = []
+            def override_copyfile(src, dest):
+                if lfutil.shortname in src and lfutil.shortname in dest:
+                    destlfile = dest.replace(lfutil.shortname, '')
+                    if not opts['force'] and os.path.exists(destlfile):
+                        raise IOError('',
+                            _('destination largefile already exists'))
+                copiedfiles.append((src, dest))
+                origcopyfile(src, dest)
+
+            util.copyfile = override_copyfile
+            result += orig(ui, repo, listpats, opts, rename)
+        finally:
+            util.copyfile = origcopyfile
+
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        for (src, dest) in copiedfiles:
+            if lfutil.shortname in src and lfutil.shortname in dest:
+                srclfile = src.replace(lfutil.shortname, '')
+                destlfile = dest.replace(lfutil.shortname, '')
+                destlfiledir = os.path.dirname(destlfile) or '.'
+                if not os.path.isdir(destlfiledir):
+                    os.makedirs(destlfiledir)
+                if rename:
+                    os.rename(srclfile, destlfile)
+                    lfdirstate.remove(os.path.relpath(srclfile,
+                        repo.root))
+                else:
+                    util.copyfile(srclfile, destlfile)
+                lfdirstate.add(os.path.relpath(destlfile,
+                    repo.root))
+        lfdirstate.write()
+    except util.Abort, e:
+        if str(e) != 'no files to copy':
+            raise e
+        else:
+            nolfiles = True
+    finally:
+        restorematchfn()
+        wlock.release()
+
+    if nolfiles and nonormalfiles:
+        raise util.Abort(_('no files to copy'))
+
+    return result
+
+# When the user calls revert, we have to be careful to not revert any
+# changes to other largefiles accidentally. This means we have to keep
+# track of the largefiles that are being reverted so we only pull down
+# the necessary largefiles.
+#
+# Standins are only updated (to match the hash of largefiles) before
+# commits. Update the standins then run the original revert, changing
+# the matcher to hit standins instead of largefiles. Based on the
+# resulting standins update the largefiles. Then return the standins
+# to their proper state
+def override_revert(orig, ui, repo, *pats, **opts):
+    # Because we put the standins in a bad state (by updating them)
+    # and then return them to a correct state we need to lock to
+    # prevent others from changing them in their incorrect state.
+    wlock = repo.wlock()
+    try:
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        (modified, added, removed, missing, unknown, ignored, clean) = \
+            lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
+        for lfile in modified:
+            lfutil.updatestandin(repo, lfutil.standin(lfile))
+
+        try:
+            ctx = repo[opts.get('rev')]
+            oldmatch = None # for the closure
+            def override_match(ctxorrepo, pats=[], opts={}, globbed=False,
+                    default='relpath'):
+                if util.safehasattr(ctxorrepo, 'match'):
+                    ctx0 = ctxorrepo
+                else:
+                    ctx0 = ctxorrepo[None]
+                match = oldmatch(ctxorrepo, pats, opts, globbed, default)
+                m = copy.copy(match)
+                def tostandin(f):
+                    if lfutil.standin(f) in ctx0 or lfutil.standin(f) in ctx:
+                        return lfutil.standin(f)
+                    elif lfutil.standin(f) in repo[None]:
+                        return None
+                    return f
+                m._files = [tostandin(f) for f in m._files]
+                m._files = [f for f in m._files if f is not None]
+                m._fmap = set(m._files)
+                orig_matchfn = m.matchfn
+                def matchfn(f):
+                    if lfutil.isstandin(f):
+                        # We need to keep track of what largefiles are being
+                        # matched so we know which ones to update later --
+                        # otherwise we accidentally revert changes to other
+                        # largefiles. This is repo-specific, so duckpunch the
+                        # repo object to keep the list of largefiles for us
+                        # later.
+                        if orig_matchfn(lfutil.splitstandin(f)) and \
+                                (f in repo[None] or f in ctx):
+                            lfileslist = getattr(repo, '_lfilestoupdate', [])
+                            lfileslist.append(lfutil.splitstandin(f))
+                            repo._lfilestoupdate = lfileslist
+                            return True
+                        else:
+                            return False
+                    return orig_matchfn(f)
+                m.matchfn = matchfn
+                return m
+            oldmatch = installmatchfn(override_match)
+            scmutil.match
+            matches = override_match(repo[None], pats, opts)
+            orig(ui, repo, *pats, **opts)
+        finally:
+            restorematchfn()
+        lfileslist = getattr(repo, '_lfilestoupdate', [])
+        lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
+                                printmessage=False)
+
+        # empty out the largefiles list so we start fresh next time
+        repo._lfilestoupdate = []
+        for lfile in modified:
+            if lfile in lfileslist:
+                if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
+                        in repo['.']:
+                    lfutil.writestandin(repo, lfutil.standin(lfile),
+                        repo['.'][lfile].data().strip(),
+                        'x' in repo['.'][lfile].flags())
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        for lfile in added:
+            standin = lfutil.standin(lfile)
+            if standin not in ctx and (standin in matches or opts.get('all')):
+                if lfile in lfdirstate:
+                    lfdirstate.drop(lfile)
+                util.unlinkpath(repo.wjoin(standin))
+        lfdirstate.write()
+    finally:
+        wlock.release()
+
+def hg_update(orig, repo, node):
+    result = orig(repo, node)
+    # XXX check if it worked first
+    lfcommands.updatelfiles(repo.ui, repo)
+    return result
+
+def hg_clean(orig, repo, node, show_stats=True):
+    result = orig(repo, node, show_stats)
+    lfcommands.updatelfiles(repo.ui, repo)
+    return result
+
+def hg_merge(orig, repo, node, force=None, remind=True):
+    result = orig(repo, node, force, remind)
+    lfcommands.updatelfiles(repo.ui, repo)
+    return result
+
+# When we rebase a repository with remotely changed largefiles, we need to
+# take some extra care so that the largefiles are correctly updated in the
+# working copy
+def override_pull(orig, ui, repo, source=None, **opts):
+    if opts.get('rebase', False):
+        repo._isrebasing = True
+        try:
+            if opts.get('update'):
+                 del opts['update']
+                 ui.debug('--update and --rebase are not compatible, ignoring '
+                          'the update flag\n')
+            del opts['rebase']
+            cmdutil.bailifchanged(repo)
+            revsprepull = len(repo)
+            origpostincoming = commands.postincoming
+            def _dummy(*args, **kwargs):
+                pass
+            commands.postincoming = _dummy
+            repo.lfpullsource = source
+            if not source:
+                source = 'default'
+            try:
+                result = commands.pull(ui, repo, source, **opts)
+            finally:
+                commands.postincoming = origpostincoming
+            revspostpull = len(repo)
+            if revspostpull > revsprepull:
+                result = result or rebase.rebase(ui, repo)
+        finally:
+            repo._isrebasing = False
+    else:
+        repo.lfpullsource = source
+        if not source:
+            source = 'default'
+        result = orig(ui, repo, source, **opts)
+    return result
+
+def override_rebase(orig, ui, repo, **opts):
+    repo._isrebasing = True
+    try:
+        orig(ui, repo, **opts)
+    finally:
+        repo._isrebasing = False
+
+def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
+            prefix=None, mtime=None, subrepos=None):
+    # No need to lock because we are only reading history and
+    # largefile caches, neither of which are modified.
+    lfcommands.cachelfiles(repo.ui, repo, node)
+
+    if kind not in archival.archivers:
+        raise util.Abort(_("unknown archive type '%s'") % kind)
+
+    ctx = repo[node]
+
+    if kind == 'files':
+        if prefix:
+            raise util.Abort(
+                _('cannot give prefix when archiving to files'))
+    else:
+        prefix = archival.tidyprefix(dest, kind, prefix)
+
+    def write(name, mode, islink, getdata):
+        if matchfn and not matchfn(name):
+            return
+        data = getdata()
+        if decode:
+            data = repo.wwritedata(name, data)
+        archiver.addfile(prefix + name, mode, islink, data)
+
+    archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
+
+    if repo.ui.configbool("ui", "archivemeta", True):
+        def metadata():
+            base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
+                hex(repo.changelog.node(0)), hex(node), ctx.branch())
+
+            tags = ''.join('tag: %s\n' % t for t in ctx.tags()
+                           if repo.tagtype(t) == 'global')
+            if not tags:
+                repo.ui.pushbuffer()
+                opts = {'template': '{latesttag}\n{latesttagdistance}',
+                        'style': '', 'patch': None, 'git': None}
+                cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
+                ltags, dist = repo.ui.popbuffer().split('\n')
+                tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
+                tags += 'latesttagdistance: %s\n' % dist
+
+            return base + tags
+
+        write('.hg_archival.txt', 0644, False, metadata)
+
+    for f in ctx:
+        ff = ctx.flags(f)
+        getdata = ctx[f].data
+        if lfutil.isstandin(f):
+            path = lfutil.findfile(repo, getdata().strip())
+            f = lfutil.splitstandin(f)
+
+            def getdatafn():
+                try:
+                    fd = open(path, 'rb')
+                    return fd.read()
+                finally:
+                    fd.close()
+
+            getdata = getdatafn
+        write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
+
+    if subrepos:
+        for subpath in ctx.substate:
+            sub = ctx.sub(subpath)
+            try:
+                sub.archive(repo.ui, archiver, prefix)
+            except TypeError:
+                sub.archive(archiver, prefix)
+
+    archiver.done()
+
+# If a largefile is modified, the change is not reflected in its
+# standin until a commit. cmdutil.bailifchanged() raises an exception
+# if the repo has uncommitted changes. Wrap it to also check if
+# largefiles were changed. This is used by bisect and backout.
+def override_bailifchanged(orig, repo):
+    orig(repo)
+    repo.lfstatus = True
+    modified, added, removed, deleted = repo.status()[:4]
+    repo.lfstatus = False
+    if modified or added or removed or deleted:
+        raise util.Abort(_('outstanding uncommitted changes'))
+
+# Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
+def override_fetch(orig, ui, repo, *pats, **opts):
+    repo.lfstatus = True
+    modified, added, removed, deleted = repo.status()[:4]
+    repo.lfstatus = False
+    if modified or added or removed or deleted:
+        raise util.Abort(_('outstanding uncommitted changes'))
+    return orig(ui, repo, *pats, **opts)
+
+def override_forget(orig, ui, repo, *pats, **opts):
+    installnormalfilesmatchfn(repo[None].manifest())
+    orig(ui, repo, *pats, **opts)
+    restorematchfn()
+    m = scmutil.match(repo[None], pats, opts)
+
+    try:
+        repo.lfstatus = True
+        s = repo.status(match=m, clean=True)
+    finally:
+        repo.lfstatus = False
+    forget = sorted(s[0] + s[1] + s[3] + s[6])
+    forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
+
+    for f in forget:
+        if lfutil.standin(f) not in repo.dirstate and not \
+                os.path.isdir(m.rel(lfutil.standin(f))):
+            ui.warn(_('not removing %s: file is already untracked\n')
+                    % m.rel(f))
+
+    for f in forget:
+        if ui.verbose or not m.exact(f):
+            ui.status(_('removing %s\n') % m.rel(f))
+
+    # Need to lock because standin files are deleted then removed from the
+    # repository and we could race inbetween.
+    wlock = repo.wlock()
+    try:
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        for f in forget:
+            if lfdirstate[f] == 'a':
+                lfdirstate.drop(f)
+            else:
+                lfdirstate.remove(f)
+        lfdirstate.write()
+        lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
+            unlink=True)
+    finally:
+        wlock.release()
+
+def getoutgoinglfiles(ui, repo, dest=None, **opts):
+    dest = ui.expandpath(dest or 'default-push', dest or 'default')
+    dest, branches = hg.parseurl(dest, opts.get('branch'))
+    revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
+    if revs:
+        revs = [repo.lookup(rev) for rev in revs]
+
+    remoteui = hg.remoteui
+
+    try:
+        remote = hg.repository(remoteui(repo, opts), dest)
+    except error.RepoError:
+        return None
+    o = lfutil.findoutgoing(repo, remote, False)
+    if not o:
+        return None
+    o = repo.changelog.nodesbetween(o, revs)[0]
+    if opts.get('newest_first'):
+        o.reverse()
+
+    toupload = set()
+    for n in o:
+        parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
+        ctx = repo[n]
+        files = set(ctx.files())
+        if len(parents) == 2:
+            mc = ctx.manifest()
+            mp1 = ctx.parents()[0].manifest()
+            mp2 = ctx.parents()[1].manifest()
+            for f in mp1:
+                if f not in mc:
+                        files.add(f)
+            for f in mp2:
+                if f not in mc:
+                    files.add(f)
+            for f in mc:
+                if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
+                    files.add(f)
+        toupload = toupload.union(
+            set([f for f in files if lfutil.isstandin(f) and f in ctx]))
+    return toupload
+
+def override_outgoing(orig, ui, repo, dest=None, **opts):
+    orig(ui, repo, dest, **opts)
+
+    if opts.pop('large', None):
+        toupload = getoutgoinglfiles(ui, repo, dest, **opts)
+        if toupload is None:
+            ui.status(_('largefiles: No remote repo\n'))
+        else:
+            ui.status(_('largefiles to upload:\n'))
+            for file in toupload:
+                ui.status(lfutil.splitstandin(file) + '\n')
+            ui.status('\n')
+
+def override_summary(orig, ui, repo, *pats, **opts):
+    orig(ui, repo, *pats, **opts)
+
+    if opts.pop('large', None):
+        toupload = getoutgoinglfiles(ui, repo, None, **opts)
+        if toupload is None:
+            ui.status(_('largefiles: No remote repo\n'))
+        else:
+            ui.status(_('largefiles: %d to upload\n') % len(toupload))
+
+def override_addremove(orig, ui, repo, *pats, **opts):
+    # Check if the parent or child has largefiles; if so, disallow
+    # addremove. If there is a symlink in the manifest then getting
+    # the manifest throws an exception: catch it and let addremove
+    # deal with it.
+    try:
+        manifesttip = set(repo['tip'].manifest())
+    except util.Abort:
+        manifesttip = set()
+    try:
+        manifestworking = set(repo[None].manifest())
+    except util.Abort:
+        manifestworking = set()
+
+    # Manifests are only iterable so turn them into sets then union
+    for file in manifesttip.union(manifestworking):
+        if file.startswith(lfutil.shortname):
+            raise util.Abort(
+                _('addremove cannot be run on a repo with largefiles'))
+
+    return orig(ui, repo, *pats, **opts)
+
+# Calling purge with --all will cause the largefiles to be deleted.
+# Override repo.status to prevent this from happening.
+def override_purge(orig, ui, repo, *dirs, **opts):
+    oldstatus = repo.status
+    def override_status(node1='.', node2=None, match=None, ignored=False,
+                        clean=False, unknown=False, listsubrepos=False):
+        r = oldstatus(node1, node2, match, ignored, clean, unknown,
+                      listsubrepos)
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        modified, added, removed, deleted, unknown, ignored, clean = r
+        unknown = [f for f in unknown if lfdirstate[f] == '?']
+        ignored = [f for f in ignored if lfdirstate[f] == '?']
+        return modified, added, removed, deleted, unknown, ignored, clean
+    repo.status = override_status
+    orig(ui, repo, *dirs, **opts)
+    repo.status = oldstatus
+
+def override_rollback(orig, ui, repo, **opts):
+    result = orig(ui, repo, **opts)
+    merge.update(repo, node=None, branchmerge=False, force=True,
+        partial=lfutil.isstandin)
+    lfdirstate = lfutil.openlfdirstate(ui, repo)
+    lfiles = lfutil.listlfiles(repo)
+    oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
+    for file in lfiles:
+        if file in oldlfiles:
+            lfdirstate.normallookup(file)
+        else:
+            lfdirstate.add(file)
+    lfdirstate.write()
+    return result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/proto.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,160 @@
+# Copyright 2011 Fog Creek Software
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import os
+import tempfile
+import urllib2
+
+from mercurial import error, httprepo, util, wireproto
+from mercurial.i18n import _
+
+import lfutil
+
+LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
+                           '\n\nPlease enable it in your Mercurial config '
+                           'file.\n')
+
+def putlfile(repo, proto, sha):
+    '''Put a largefile into a repository's local cache and into the
+    system cache.'''
+    f = None
+    proto.redirect()
+    try:
+        try:
+            f = tempfile.NamedTemporaryFile(mode='wb+', prefix='hg-putlfile-')
+            proto.getfile(f)
+            f.seek(0)
+            if sha != lfutil.hexsha1(f):
+                return wireproto.pushres(1)
+            lfutil.copytocacheabsolute(repo, f.name, sha)
+        except IOError:
+            repo.ui.warn(
+                _('error: could not put received data into largefile store'))
+            return wireproto.pushres(1)
+    finally:
+        if f:
+            f.close()
+
+    return wireproto.pushres(0)
+
+def getlfile(repo, proto, sha):
+    '''Retrieve a largefile from the repository-local cache or system
+    cache.'''
+    filename = lfutil.findfile(repo, sha)
+    if not filename:
+        raise util.Abort(_('requested largefile %s not present in cache') % sha)
+    f = open(filename, 'rb')
+    length = os.fstat(f.fileno())[6]
+
+    # Since we can't set an HTTP content-length header here, and
+    # Mercurial core provides no way to give the length of a streamres
+    # (and reading the entire file into RAM would be ill-advised), we
+    # just send the length on the first line of the response, like the
+    # ssh proto does for string responses.
+    def generator():
+        yield '%d\n' % length
+        for chunk in f:
+            yield chunk
+    return wireproto.streamres(generator())
+
+def statlfile(repo, proto, sha):
+    '''Return '2\n' if the largefile is missing, '1\n' if it has a
+    mismatched checksum, or '0\n' if it is in good condition'''
+    filename = lfutil.findfile(repo, sha)
+    if not filename:
+        return '2\n'
+    fd = None
+    try:
+        fd = open(filename, 'rb')
+        return lfutil.hexsha1(fd) == sha and '0\n' or '1\n'
+    finally:
+        if fd:
+            fd.close()
+
+def wirereposetup(ui, repo):
+    class lfileswirerepository(repo.__class__):
+        def putlfile(self, sha, fd):
+            # unfortunately, httprepository._callpush tries to convert its
+            # input file-like into a bundle before sending it, so we can't use
+            # it ...
+            if issubclass(self.__class__, httprepo.httprepository):
+                try:
+                    return int(self._call('putlfile', data=fd, sha=sha,
+                        headers={'content-type':'application/mercurial-0.1'}))
+                except (ValueError, urllib2.HTTPError):
+                    return 1
+            # ... but we can't use sshrepository._call because the data=
+            # argument won't get sent, and _callpush does exactly what we want
+            # in this case: send the data straight through
+            else:
+                try:
+                    ret, output = self._callpush("putlfile", fd, sha=sha)
+                    if ret == "":
+                        raise error.ResponseError(_('putlfile failed:'),
+                                output)
+                    return int(ret)
+                except IOError:
+                    return 1
+                except ValueError:
+                    raise error.ResponseError(
+                        _('putlfile failed (unexpected response):'), ret)
+
+        def getlfile(self, sha):
+            stream = self._callstream("getlfile", sha=sha)
+            length = stream.readline()
+            try:
+                length = int(length)
+            except ValueError:
+                self._abort(error.ResponseError(_("unexpected response:"),
+                                                length))
+            return (length, stream)
+
+        def statlfile(self, sha):
+            try:
+                return int(self._call("statlfile", sha=sha))
+            except (ValueError, urllib2.HTTPError):
+                # If the server returns anything but an integer followed by a
+                # newline, newline, it's not speaking our language; if we get
+                # an HTTP error, we can't be sure the largefile is present;
+                # either way, consider it missing.
+                return 2
+
+    repo.__class__ = lfileswirerepository
+
+# advertise the largefiles=serve capability
+def capabilities(repo, proto):
+    return capabilities_orig(repo, proto) + ' largefiles=serve'
+
+# duplicate what Mercurial's new out-of-band errors mechanism does, because
+# clients old and new alike both handle it well
+def webproto_refuseclient(self, message):
+    self.req.header([('Content-Type', 'application/hg-error')])
+    return message
+
+def sshproto_refuseclient(self, message):
+    self.ui.write_err('%s\n-\n' % message)
+    self.fout.write('\n')
+    self.fout.flush()
+
+    return ''
+
+def heads(repo, proto):
+    if lfutil.islfilesrepo(repo):
+        return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
+    return wireproto.heads(repo, proto)
+
+def sshrepo_callstream(self, cmd, **args):
+    if cmd == 'heads' and self.capable('largefiles'):
+        cmd = 'lheads'
+    if cmd == 'batch' and self.capable('largefiles'):
+        args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
+    return ssh_oldcallstream(self, cmd, **args)
+
+def httprepo_callstream(self, cmd, **args):
+    if cmd == 'heads' and self.capable('largefiles'):
+        cmd = 'lheads'
+    if cmd == 'batch' and self.capable('largefiles'):
+        args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
+    return http_oldcallstream(self, cmd, **args)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/remotestore.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,106 @@
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''remote largefile store; the base class for servestore'''
+
+import urllib2
+
+from mercurial import util
+from mercurial.i18n import _
+
+import lfutil
+import basestore
+
+class remotestore(basestore.basestore):
+    '''a largefile store accessed over a network'''
+    def __init__(self, ui, repo, url):
+        super(remotestore, self).__init__(ui, repo, url)
+
+    def put(self, source, hash):
+        if self._verify(hash):
+            return
+        if self.sendfile(source, hash):
+            raise util.Abort(
+                _('remotestore: could not put %s to remote store %s')
+                % (source, self.url))
+        self.ui.debug(
+            _('remotestore: put %s to remote store %s') % (source, self.url))
+
+    def exists(self, hash):
+        return self._verify(hash)
+
+    def sendfile(self, filename, hash):
+        self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash))
+        fd = None
+        try:
+            try:
+                fd = lfutil.httpsendfile(self.ui, filename)
+            except IOError, e:
+                raise util.Abort(
+                    _('remotestore: could not open file %s: %s')
+                    % (filename, str(e)))
+            return self._put(hash, fd)
+        finally:
+            if fd:
+                fd.close()
+
+    def _getfile(self, tmpfile, filename, hash):
+        # quit if the largefile isn't there
+        stat = self._stat(hash)
+        if stat == 1:
+            raise util.Abort(_('remotestore: largefile %s is invalid') % hash)
+        elif stat == 2:
+            raise util.Abort(_('remotestore: largefile %s is missing') % hash)
+
+        try:
+            length, infile = self._get(hash)
+        except urllib2.HTTPError, e:
+            # 401s get converted to util.Aborts; everything else is fine being
+            # turned into a StoreError
+            raise basestore.StoreError(filename, hash, self.url, str(e))
+        except urllib2.URLError, e:
+            # This usually indicates a connection problem, so don't
+            # keep trying with the other files... they will probably
+            # all fail too.
+            raise util.Abort('%s: %s' % (self.url, e.reason))
+        except IOError, e:
+            raise basestore.StoreError(filename, hash, self.url, str(e))
+
+        # Mercurial does not close its SSH connections after writing a stream
+        if length is not None:
+            infile = lfutil.limitreader(infile, length)
+        return lfutil.copyandhash(lfutil.blockstream(infile), tmpfile)
+
+    def _verify(self, hash):
+        return not self._stat(hash)
+
+    def _verifyfile(self, cctx, cset, contents, standin, verified):
+        filename = lfutil.splitstandin(standin)
+        if not filename:
+            return False
+        fctx = cctx[standin]
+        key = (filename, fctx.filenode())
+        if key in verified:
+            return False
+
+        verified.add(key)
+
+        stat = self._stat(hash)
+        if not stat:
+            return False
+        elif stat == 1:
+            self.ui.warn(
+                _('changeset %s: %s: contents differ\n')
+                % (cset, filename))
+            return True # failed
+        elif stat == 2:
+            self.ui.warn(
+                _('changeset %s: %s missing\n')
+                % (cset, filename))
+            return True # failed
+        else:
+            raise RuntimeError('verify failed: unexpected response from '
+                               'statlfile (%r)' % stat)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/reposetup.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,416 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''setup for largefiles repositories: reposetup'''
+import copy
+import types
+import os
+import re
+
+from mercurial import context, error, manifest, match as match_, \
+        node, util
+from mercurial.i18n import _
+
+import lfcommands
+import proto
+import lfutil
+
+def reposetup(ui, repo):
+    # wire repositories should be given new wireproto functions but not the
+    # other largefiles modifications
+    if not repo.local():
+        return proto.wirereposetup(ui, repo)
+
+    for name in ('status', 'commitctx', 'commit', 'push'):
+        method = getattr(repo, name)
+        #if not (isinstance(method, types.MethodType) and
+        #        method.im_func is repo.__class__.commitctx.im_func):
+        if (isinstance(method, types.FunctionType) and
+            method.func_name == 'wrap'):
+            ui.warn(_('largefiles: repo method %r appears to have already been'
+                    ' wrapped by another extension: '
+                    'largefiles may behave incorrectly\n')
+                    % name)
+
+    class lfiles_repo(repo.__class__):
+        lfstatus = False
+        def status_nolfiles(self, *args, **kwargs):
+            return super(lfiles_repo, self).status(*args, **kwargs)
+
+        # When lfstatus is set, return a context that gives the names
+        # of largefiles instead of their corresponding standins and
+        # identifies the largefiles as always binary, regardless of
+        # their actual contents.
+        def __getitem__(self, changeid):
+            ctx = super(lfiles_repo, self).__getitem__(changeid)
+            if self.lfstatus:
+                class lfiles_manifestdict(manifest.manifestdict):
+                    def __contains__(self, filename):
+                        if super(lfiles_manifestdict,
+                                self).__contains__(filename):
+                            return True
+                        return super(lfiles_manifestdict,
+                            self).__contains__(lfutil.shortname+'/' + filename)
+                class lfiles_ctx(ctx.__class__):
+                    def files(self):
+                        filenames = super(lfiles_ctx, self).files()
+                        return [re.sub('^\\'+lfutil.shortname+'/', '',
+                                       filename) for filename in filenames]
+                    def manifest(self):
+                        man1 = super(lfiles_ctx, self).manifest()
+                        man1.__class__ = lfiles_manifestdict
+                        return man1
+                    def filectx(self, path, fileid=None, filelog=None):
+                        try:
+                            result = super(lfiles_ctx, self).filectx(path,
+                                fileid, filelog)
+                        except error.LookupError:
+                            # Adding a null character will cause Mercurial to
+                            # identify this as a binary file.
+                            result = super(lfiles_ctx, self).filectx(
+                                lfutil.shortname + '/' + path, fileid,
+                                filelog)
+                            olddata = result.data
+                            result.data = lambda: olddata() + '\0'
+                        return result
+                ctx.__class__ = lfiles_ctx
+            return ctx
+
+        # Figure out the status of big files and insert them into the
+        # appropriate list in the result. Also removes standin files
+        # from the listing. Revert to the original status if
+        # self.lfstatus is False.
+        def status(self, node1='.', node2=None, match=None, ignored=False,
+                clean=False, unknown=False, listsubrepos=False):
+            listignored, listclean, listunknown = ignored, clean, unknown
+            if not self.lfstatus:
+                try:
+                    return super(lfiles_repo, self).status(node1, node2, match,
+                        listignored, listclean, listunknown, listsubrepos)
+                except TypeError:
+                    return super(lfiles_repo, self).status(node1, node2, match,
+                        listignored, listclean, listunknown)
+            else:
+                # some calls in this function rely on the old version of status
+                self.lfstatus = False
+                if isinstance(node1, context.changectx):
+                    ctx1 = node1
+                else:
+                    ctx1 = repo[node1]
+                if isinstance(node2, context.changectx):
+                    ctx2 = node2
+                else:
+                    ctx2 = repo[node2]
+                working = ctx2.rev() is None
+                parentworking = working and ctx1 == self['.']
+
+                def inctx(file, ctx):
+                    try:
+                        if ctx.rev() is None:
+                            return file in ctx.manifest()
+                        ctx[file]
+                        return True
+                    except KeyError:
+                        return False
+
+                if match is None:
+                    match = match_.always(self.root, self.getcwd())
+
+                # Create a copy of match that matches standins instead
+                # of largefiles.
+                def tostandin(file):
+                    if inctx(lfutil.standin(file), ctx2):
+                        return lfutil.standin(file)
+                    return file
+
+                m = copy.copy(match)
+                m._files = [tostandin(f) for f in m._files]
+
+                # get ignored, clean, and unknown but remove them
+                # later if they were not asked for
+                try:
+                    result = super(lfiles_repo, self).status(node1, node2, m,
+                        True, True, True, listsubrepos)
+                except TypeError:
+                    result = super(lfiles_repo, self).status(node1, node2, m,
+                        True, True, True)
+                if working:
+                    # hold the wlock while we read largefiles and
+                    # update the lfdirstate
+                    wlock = repo.wlock()
+                    try:
+                        # Any non-largefiles that were explicitly listed must be
+                        # taken out or lfdirstate.status will report an error.
+                        # The status of these files was already computed using
+                        # super's status.
+                        lfdirstate = lfutil.openlfdirstate(ui, self)
+                        match._files = [f for f in match._files if f in
+                            lfdirstate]
+                        s = lfdirstate.status(match, [], listignored,
+                                listclean, listunknown)
+                        (unsure, modified, added, removed, missing, unknown,
+                                ignored, clean) = s
+                        if parentworking:
+                            for lfile in unsure:
+                                if ctx1[lfutil.standin(lfile)].data().strip() \
+                                        != lfutil.hashfile(self.wjoin(lfile)):
+                                    modified.append(lfile)
+                                else:
+                                    clean.append(lfile)
+                                    lfdirstate.normal(lfile)
+                            lfdirstate.write()
+                        else:
+                            tocheck = unsure + modified + added + clean
+                            modified, added, clean = [], [], []
+
+                            for lfile in tocheck:
+                                standin = lfutil.standin(lfile)
+                                if inctx(standin, ctx1):
+                                    if ctx1[standin].data().strip() != \
+                                            lfutil.hashfile(self.wjoin(lfile)):
+                                        modified.append(lfile)
+                                    else:
+                                        clean.append(lfile)
+                                else:
+                                    added.append(lfile)
+                    finally:
+                        wlock.release()
+
+                    for standin in ctx1.manifest():
+                        if not lfutil.isstandin(standin):
+                            continue
+                        lfile = lfutil.splitstandin(standin)
+                        if not match(lfile):
+                            continue
+                        if lfile not in lfdirstate:
+                            removed.append(lfile)
+                    # Handle unknown and ignored differently
+                    lfiles = (modified, added, removed, missing, [], [], clean)
+                    result = list(result)
+                    # Unknown files
+                    result[4] = [f for f in unknown
+                                 if (repo.dirstate[f] == '?' and
+                                     not lfutil.isstandin(f))]
+                    # Ignored files must be ignored by both the dirstate and
+                    # lfdirstate
+                    result[5] = set(ignored).intersection(set(result[5]))
+                    # combine normal files and largefiles
+                    normals = [[fn for fn in filelist
+                                if not lfutil.isstandin(fn)]
+                               for filelist in result]
+                    result = [sorted(list1 + list2)
+                              for (list1, list2) in zip(normals, lfiles)]
+                else:
+                    def toname(f):
+                        if lfutil.isstandin(f):
+                            return lfutil.splitstandin(f)
+                        return f
+                    result = [[toname(f) for f in items] for items in result]
+
+                if not listunknown:
+                    result[4] = []
+                if not listignored:
+                    result[5] = []
+                if not listclean:
+                    result[6] = []
+                self.lfstatus = True
+                return result
+
+        # As part of committing, copy all of the largefiles into the
+        # cache.
+        def commitctx(self, *args, **kwargs):
+            node = super(lfiles_repo, self).commitctx(*args, **kwargs)
+            ctx = self[node]
+            for filename in ctx.files():
+                if lfutil.isstandin(filename) and filename in ctx.manifest():
+                    realfile = lfutil.splitstandin(filename)
+                    lfutil.copytocache(self, ctx.node(), realfile)
+
+            return node
+
+        # Before commit, largefile standins have not had their
+        # contents updated to reflect the hash of their largefile.
+        # Do that here.
+        def commit(self, text="", user=None, date=None, match=None,
+                force=False, editor=False, extra={}):
+            orig = super(lfiles_repo, self).commit
+
+            wlock = repo.wlock()
+            try:
+                if getattr(repo, "_isrebasing", False):
+                    # We have to take the time to pull down the new
+                    # largefiles now. Otherwise if we are rebasing,
+                    # any largefiles that were modified in the
+                    # destination changesets get overwritten, either
+                    # by the rebase or in the first commit after the
+                    # rebase.
+                    lfcommands.updatelfiles(repo.ui, repo)
+                # Case 1: user calls commit with no specific files or
+                # include/exclude patterns: refresh and commit all files that
+                # are "dirty".
+                if ((match is None) or
+                    (not match.anypats() and not match.files())):
+                    # Spend a bit of time here to get a list of files we know
+                    # are modified so we can compare only against those.
+                    # It can cost a lot of time (several seconds)
+                    # otherwise to update all standins if the largefiles are
+                    # large.
+                    lfdirstate = lfutil.openlfdirstate(ui, self)
+                    dirtymatch = match_.always(repo.root, repo.getcwd())
+                    s = lfdirstate.status(dirtymatch, [], False, False, False)
+                    modifiedfiles = []
+                    for i in s:
+                        modifiedfiles.extend(i)
+                    lfiles = lfutil.listlfiles(self)
+                    # this only loops through largefiles that exist (not
+                    # removed/renamed)
+                    for lfile in lfiles:
+                        if lfile in modifiedfiles:
+                            if os.path.exists(self.wjoin(lfutil.standin(lfile))):
+                                # this handles the case where a rebase is being
+                                # performed and the working copy is not updated
+                                # yet.
+                                if os.path.exists(self.wjoin(lfile)):
+                                    lfutil.updatestandin(self,
+                                        lfutil.standin(lfile))
+                                    lfdirstate.normal(lfile)
+                    for lfile in lfdirstate:
+                        if lfile in modifiedfiles:
+                            if not os.path.exists(
+                                    repo.wjoin(lfutil.standin(lfile))):
+                                lfdirstate.drop(lfile)
+                    lfdirstate.write()
+
+                    return orig(text=text, user=user, date=date, match=match,
+                                    force=force, editor=editor, extra=extra)
+
+                for f in match.files():
+                    if lfutil.isstandin(f):
+                        raise util.Abort(
+                            _('file "%s" is a largefile standin') % f,
+                            hint=('commit the largefile itself instead'))
+
+                # Case 2: user calls commit with specified patterns: refresh
+                # any matching big files.
+                smatcher = lfutil.composestandinmatcher(self, match)
+                standins = lfutil.dirstate_walk(self.dirstate, smatcher)
+
+                # No matching big files: get out of the way and pass control to
+                # the usual commit() method.
+                if not standins:
+                    return orig(text=text, user=user, date=date, match=match,
+                                    force=force, editor=editor, extra=extra)
+
+                # Refresh all matching big files.  It's possible that the
+                # commit will end up failing, in which case the big files will
+                # stay refreshed.  No harm done: the user modified them and
+                # asked to commit them, so sooner or later we're going to
+                # refresh the standins.  Might as well leave them refreshed.
+                lfdirstate = lfutil.openlfdirstate(ui, self)
+                for standin in standins:
+                    lfile = lfutil.splitstandin(standin)
+                    if lfdirstate[lfile] <> 'r':
+                        lfutil.updatestandin(self, standin)
+                        lfdirstate.normal(lfile)
+                    else:
+                        lfdirstate.drop(lfile)
+                lfdirstate.write()
+
+                # Cook up a new matcher that only matches regular files or
+                # standins corresponding to the big files requested by the
+                # user.  Have to modify _files to prevent commit() from
+                # complaining "not tracked" for big files.
+                lfiles = lfutil.listlfiles(repo)
+                match = copy.copy(match)
+                orig_matchfn = match.matchfn
+
+                # Check both the list of largefiles and the list of
+                # standins because if a largefile was removed, it
+                # won't be in the list of largefiles at this point
+                match._files += sorted(standins)
+
+                actualfiles = []
+                for f in match._files:
+                    fstandin = lfutil.standin(f)
+
+                    # ignore known largefiles and standins
+                    if f in lfiles or fstandin in standins:
+                        continue
+
+                    # append directory separator to avoid collisions
+                    if not fstandin.endswith(os.sep):
+                        fstandin += os.sep
+
+                    # prevalidate matching standin directories
+                    if lfutil.any_(st for st in match._files
+                                   if st.startswith(fstandin)):
+                        continue
+                    actualfiles.append(f)
+                match._files = actualfiles
+
+                def matchfn(f):
+                    if orig_matchfn(f):
+                        return f not in lfiles
+                    else:
+                        return f in standins
+
+                match.matchfn = matchfn
+                return orig(text=text, user=user, date=date, match=match,
+                                force=force, editor=editor, extra=extra)
+            finally:
+                wlock.release()
+
+        def push(self, remote, force=False, revs=None, newbranch=False):
+            o = lfutil.findoutgoing(repo, remote, force)
+            if o:
+                toupload = set()
+                o = repo.changelog.nodesbetween(o, revs)[0]
+                for n in o:
+                    parents = [p for p in repo.changelog.parents(n)
+                               if p != node.nullid]
+                    ctx = repo[n]
+                    files = set(ctx.files())
+                    if len(parents) == 2:
+                        mc = ctx.manifest()
+                        mp1 = ctx.parents()[0].manifest()
+                        mp2 = ctx.parents()[1].manifest()
+                        for f in mp1:
+                            if f not in mc:
+                                files.add(f)
+                        for f in mp2:
+                            if f not in mc:
+                                files.add(f)
+                        for f in mc:
+                            if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
+                                    None):
+                                files.add(f)
+
+                    toupload = toupload.union(
+                        set([ctx[f].data().strip()
+                             for f in files
+                             if lfutil.isstandin(f) and f in ctx]))
+                lfcommands.uploadlfiles(ui, self, remote, toupload)
+            return super(lfiles_repo, self).push(remote, force, revs,
+                newbranch)
+
+    repo.__class__ = lfiles_repo
+
+    def checkrequireslfiles(ui, repo, **kwargs):
+        if 'largefiles' not in repo.requirements and lfutil.any_(
+                lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
+            # workaround bug in Mercurial 1.9 whereby requirements is
+            # a list on newly-cloned repos
+            repo.requirements = set(repo.requirements)
+
+            repo.requirements |= set(['largefiles'])
+            repo._writerequirements()
+
+    checkrequireslfiles(ui, repo)
+
+    ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
+    ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/uisetup.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,138 @@
+# Copyright 2009-2010 Gregory P. Ward
+# Copyright 2009-2010 Intelerad Medical Systems Incorporated
+# Copyright 2010-2011 Fog Creek Software
+# Copyright 2010-2011 Unity Technologies
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''setup for largefiles extension: uisetup'''
+
+from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
+    httprepo, localrepo, sshrepo, sshserver, util, wireproto
+from mercurial.i18n import _
+from mercurial.hgweb import hgweb_mod, protocol
+
+import overrides
+import proto
+
+def uisetup(ui):
+    # Disable auto-status for some commands which assume that all
+    # files in the result are under Mercurial's control
+
+    entry = extensions.wrapcommand(commands.table, 'add',
+                                   overrides.override_add)
+    addopt = [('', 'large', None, _('add as largefile')),
+            ('', 'lfsize', '', _('add all files above this size (in megabytes)'
+                                 'as largefiles (default: 10)'))]
+    entry[1].extend(addopt)
+
+    entry = extensions.wrapcommand(commands.table, 'addremove',
+            overrides.override_addremove)
+    entry = extensions.wrapcommand(commands.table, 'remove',
+                                   overrides.override_remove)
+    entry = extensions.wrapcommand(commands.table, 'forget',
+                                   overrides.override_forget)
+    entry = extensions.wrapcommand(commands.table, 'status',
+                                   overrides.override_status)
+    entry = extensions.wrapcommand(commands.table, 'log',
+                                   overrides.override_log)
+    entry = extensions.wrapcommand(commands.table, 'rollback',
+                                   overrides.override_rollback)
+    entry = extensions.wrapcommand(commands.table, 'verify',
+                                   overrides.override_verify)
+
+    verifyopt = [('', 'large', None, _('verify largefiles')),
+                 ('', 'lfa', None,
+                     _('verify all revisions of largefiles not just current')),
+                 ('', 'lfc', None,
+                     _('verify largefile contents not just existence'))]
+    entry[1].extend(verifyopt)
+
+    entry = extensions.wrapcommand(commands.table, 'outgoing',
+        overrides.override_outgoing)
+    outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
+    entry[1].extend(outgoingopt)
+    entry = extensions.wrapcommand(commands.table, 'summary',
+                                   overrides.override_summary)
+    summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
+    entry[1].extend(summaryopt)
+
+    entry = extensions.wrapcommand(commands.table, 'update',
+                                   overrides.override_update)
+    entry = extensions.wrapcommand(commands.table, 'pull',
+                                   overrides.override_pull)
+    entry = extensions.wrapfunction(filemerge, 'filemerge',
+                                    overrides.override_filemerge)
+    entry = extensions.wrapfunction(cmdutil, 'copy',
+                                    overrides.override_copy)
+
+    # Backout calls revert so we need to override both the command and the
+    # function
+    entry = extensions.wrapcommand(commands.table, 'revert',
+                                   overrides.override_revert)
+    entry = extensions.wrapfunction(commands, 'revert',
+                                    overrides.override_revert)
+
+    # clone uses hg._update instead of hg.update even though they are the
+    # same function... so wrap both of them)
+    extensions.wrapfunction(hg, 'update', overrides.hg_update)
+    extensions.wrapfunction(hg, '_update', overrides.hg_update)
+    extensions.wrapfunction(hg, 'clean', overrides.hg_clean)
+    extensions.wrapfunction(hg, 'merge', overrides.hg_merge)
+
+    extensions.wrapfunction(archival, 'archive', overrides.override_archive)
+    if util.safehasattr(cmdutil, 'bailifchanged'):
+        extensions.wrapfunction(cmdutil, 'bailifchanged',
+            overrides.override_bailifchanged)
+    else:
+        extensions.wrapfunction(cmdutil, 'bail_if_changed',
+            overrides.override_bailifchanged)
+
+    # create the new wireproto commands ...
+    wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
+    wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
+    wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
+
+    # ... and wrap some existing ones
+    wireproto.commands['capabilities'] = (proto.capabilities, '')
+    wireproto.commands['heads'] = (proto.heads, '')
+    wireproto.commands['lheads'] = (wireproto.heads, '')
+
+    # make putlfile behave the same as push and {get,stat}lfile behave
+    # the same as pull w.r.t. permissions checks
+    hgweb_mod.perms['putlfile'] = 'push'
+    hgweb_mod.perms['getlfile'] = 'pull'
+    hgweb_mod.perms['statlfile'] = 'pull'
+
+    # the hello wireproto command uses wireproto.capabilities, so it won't see
+    # our largefiles capability unless we replace the actual function as well.
+    proto.capabilities_orig = wireproto.capabilities
+    wireproto.capabilities = proto.capabilities
+
+    # these let us reject non-largefiles clients and make them display
+    # our error messages
+    protocol.webproto.refuseclient = proto.webproto_refuseclient
+    sshserver.sshserver.refuseclient = proto.sshproto_refuseclient
+
+    # can't do this in reposetup because it needs to have happened before
+    # wirerepo.__init__ is called
+    proto.ssh_oldcallstream = sshrepo.sshrepository._callstream
+    proto.http_oldcallstream = httprepo.httprepository._callstream
+    sshrepo.sshrepository._callstream = proto.sshrepo_callstream
+    httprepo.httprepository._callstream = proto.httprepo_callstream
+
+    # don't die on seeing a repo with the largefiles requirement
+    localrepo.localrepository.supported |= set(['largefiles'])
+
+    # override some extensions' stuff as well
+    for name, module in extensions.extensions():
+        if name == 'fetch':
+            extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
+                overrides.override_fetch)
+        if name == 'purge':
+            extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
+                overrides.override_purge)
+        if name == 'rebase':
+            extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
+                overrides.override_rebase)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/usage.txt	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,51 @@
+Largefiles allows for tracking large, incompressible binary files in Mercurial
+without requiring excessive bandwidth for clones and pulls.  Files added as
+largefiles are not tracked directly by Mercurial; rather, their revisions are
+identified by a checksum, and Mercurial tracks these checksums.  This way, when
+you clone a repository or pull in changesets, the large files in older
+revisions of the repository are not needed, and only the ones needed to update
+to the current version are downloaded.  This saves both disk space and
+bandwidth.
+
+If you are starting a new repository or adding new large binary files, using
+largefiles for them is as easy as adding '--large' to your hg add command.  For
+example:
+
+$ dd if=/dev/urandom of=thisfileislarge count=2000
+$ hg add --large thisfileislarge
+$ hg commit -m 'add thisfileislarge, which is large, as a largefile'
+
+When you push a changeset that affects largefiles to a remote repository, its
+largefile revisions will be uploaded along with it.  Note that the remote
+Mercurial must also have the largefiles extension enabled for this to work.
+
+When you pull a changeset that affects largefiles from a remote repository,
+nothing different from Mercurial's normal behavior happens.  However, when you
+update to such a revision, any largefiles needed by that revision are
+downloaded and cached if they have never been downloaded before.  This means
+that network access is required to update to revision you have not yet updated
+to.
+
+If you already have large files tracked by Mercurial without the largefiles
+extension, you will need to convert your repository in order to benefit from
+largefiles.  This is done with the 'hg lfconvert' command:
+
+$ hg lfconvert --size 10 oldrepo newrepo
+
+By default, in repositories that already have largefiles in them, any new file
+over 10MB will automatically be added as largefiles.  To change this
+threshhold, set [largefiles].size in your Mercurial config file to the minimum
+size in megabytes to track as a largefile, or use the --lfsize option to the
+add command (also in megabytes):
+
+[largefiles]
+size = 2
+
+$ hg add --lfsize 2
+
+The [largefiles].patterns config option allows you to specify specific
+space-separated filename patterns (in shell glob syntax) that should always be
+tracked as largefiles:
+
+[largefiles]
+pattens = *.jpg *.{png,bmp} library.zip content/audio/*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/largefiles/wirestore.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,29 @@
+# Copyright 2010-2011 Fog Creek Software
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''largefile store working over Mercurial's wire protocol'''
+
+import lfutil
+import remotestore
+
+class wirestore(remotestore.remotestore):
+    def __init__(self, ui, repo, remote):
+        cap = remote.capable('largefiles')
+        if not cap:
+            raise lfutil.storeprotonotcapable([])
+        storetypes = cap.split(',')
+        if not 'serve' in storetypes:
+            raise lfutil.storeprotonotcapable(storetypes)
+        self.remote = remote
+        super(wirestore, self).__init__(ui, repo, remote.url())
+
+    def _put(self, hash, fd):
+        return self.remote.putlfile(hash, fd)
+
+    def _get(self, hash):
+        return self.remote.getlfile(hash)
+
+    def _stat(self, hash):
+        return self.remote.statlfile(hash)
--- a/hgext/mq.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/mq.py	Sat Oct 15 14:30:50 2011 -0500
@@ -287,25 +287,31 @@
 
     @util.propertycache
     def applied(self):
-        if os.path.exists(self.join(self.statuspath)):
-            def parselines(lines):
-                for l in lines:
-                    entry = l.split(':', 1)
-                    if len(entry) > 1:
-                        n, name = entry
-                        yield statusentry(bin(n), name)
-                    elif l.strip():
-                        self.ui.warn(_('malformated mq status line: %s\n') % entry)
-                    # else we ignore empty lines
+        def parselines(lines):
+            for l in lines:
+                entry = l.split(':', 1)
+                if len(entry) > 1:
+                    n, name = entry
+                    yield statusentry(bin(n), name)
+                elif l.strip():
+                    self.ui.warn(_('malformated mq status line: %s\n') % entry)
+                # else we ignore empty lines
+        try:
             lines = self.opener.read(self.statuspath).splitlines()
             return list(parselines(lines))
-        return []
+        except IOError, e:
+            if e.errno == errno.ENOENT:
+                return []
+            raise
 
     @util.propertycache
     def fullseries(self):
-        if os.path.exists(self.join(self.seriespath)):
-            return self.opener.read(self.seriespath).splitlines()
-        return []
+        try:
+             return self.opener.read(self.seriespath).splitlines()
+        except IOError, e:
+            if e.errno == errno.ENOENT:
+                return []
+            raise
 
     @util.propertycache
     def series(self):
@@ -626,6 +632,7 @@
             self.ui.note(str(inst) + '\n')
             if not self.ui.verbose:
                 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
+            self.ui.traceback()
             return (False, list(files), False)
 
     def apply(self, repo, series, list=False, update_status=True,
@@ -938,7 +945,7 @@
                         p.write("# User " + user + "\n")
                     if date:
                         p.write("# Date %s %s\n\n" % date)
-                if hasattr(msg, '__call__'):
+                if util.safehasattr(msg, '__call__'):
                     msg = msg()
                 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
                 n = repo.commit(commitmsg, user, date, match=match, force=True)
@@ -1010,12 +1017,10 @@
     # if the exact patch name does not exist, we try a few
     # variations.  If strict is passed, we try only #1
     #
-    # 1) a number to indicate an offset in the series file
+    # 1) a number (as string) to indicate an offset in the series file
     # 2) a unique substring of the patch name was given
     # 3) patchname[-+]num to indicate an offset in the series file
     def lookup(self, patch, strict=False):
-        patch = patch and str(patch)
-
         def partialname(s):
             if s in self.series:
                 return s
@@ -1034,8 +1039,6 @@
                     return self.series[0]
             return None
 
-        if patch is None:
-            return None
         if patch in self.series:
             return patch
 
@@ -1095,12 +1098,12 @@
                 self.ui.warn(_('no patches in series\n'))
                 return 0
 
-            patch = self.lookup(patch)
             # Suppose our series file is: A B C and the current 'top'
             # patch is B. qpush C should be performed (moving forward)
             # qpush B is a NOP (no change) qpush A is an error (can't
             # go backwards with qpush)
             if patch:
+                patch = self.lookup(patch)
                 info = self.isapplied(patch)
                 if info and info[0] >= len(self.applied) - 1:
                     self.ui.warn(
@@ -1492,7 +1495,7 @@
                 n = repo.commit(message, user, ph.date, match=match,
                                 force=True)
                 # only write patch after a successful commit
-                patchf.rename()
+                patchf.close()
                 self.applied.append(statusentry(n, patchfn))
             except:
                 ctx = repo[cparents[0]]
@@ -2675,7 +2678,11 @@
     return 0
 
 @command("strip",
-         [('f', 'force', None, _('force removal of changesets, discard '
+         [
+          ('r', 'rev', [], _('strip specified revision (optional, '
+                               'can specify revisions without this '
+                               'option)'), _('REV')),
+          ('f', 'force', None, _('force removal of changesets, discard '
                                  'uncommitted changes (no backup)')),
           ('b', 'backup', None, _('bundle only changesets with local revision'
                                   ' number greater than REV which are not'
@@ -2716,6 +2723,7 @@
         backup = 'none'
 
     cl = repo.changelog
+    revs = list(revs) + opts.get('rev')
     revs = set(scmutil.revrange(repo, revs))
     if not revs:
         raise util.Abort(_('empty revision set'))
@@ -2867,7 +2875,7 @@
                 if i == 0:
                     q.pop(repo, all=True)
                 else:
-                    q.pop(repo, i - 1)
+                    q.pop(repo, str(i - 1))
                 break
     if popped:
         try:
@@ -2915,6 +2923,7 @@
 
 @command("qqueue",
          [('l', 'list', False, _('list all available queues')),
+          ('', 'active', False, _('print name of active queue')),
           ('c', 'create', False, _('create new queue')),
           ('', 'rename', False, _('rename active queue')),
           ('', 'delete', False, _('delete reference to queue')),
@@ -2929,7 +2938,8 @@
 
     Omitting a queue name or specifying -l/--list will show you the registered
     queues - by default the "normal" patches queue is registered. The currently
-    active queue will be marked with "(active)".
+    active queue will be marked with "(active)". Specifying --active will print
+    only the name of the active queue.
 
     To create a new queue, use -c/--create. The queue is automatically made
     active, except in the case where there are applied patches from the
@@ -3022,8 +3032,11 @@
         fh.close()
         util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
 
-    if not name or opts.get('list'):
+    if not name or opts.get('list') or opts.get('active'):
         current = _getcurrent()
+        if opts.get('active'):
+            ui.write('%s\n' % (current,))
+            return
         for queue in _getqueues():
             ui.write('%s' % (queue,))
             if queue == current and not ui.quiet:
--- a/hgext/notify.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/notify.py	Sat Oct 15 14:30:50 2011 -0500
@@ -5,71 +5,115 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-'''hooks for sending email notifications at commit/push time
+'''hooks for sending email push notifications
 
-Subscriptions can be managed through a hgrc file. Default mode is to
-print messages to stdout, for testing and configuring.
+This extension let you run hooks sending email notifications when
+changesets are being pushed, from the sending or receiving side.
 
-To use, configure the notify extension and enable it in hgrc like
-this::
-
-  [extensions]
-  notify =
+First, enable the extension as explained in :hg:`help extensions`, and
+register the hook you want to run. ``incoming`` and ``outgoing`` hooks
+are run by the changesets receiver while the ``outgoing`` one is for
+the sender::
 
   [hooks]
   # one email for each incoming changeset
   incoming.notify = python:hgext.notify.hook
-  # batch emails when many changesets incoming at one time
+  # one email for all incoming changesets
   changegroup.notify = python:hgext.notify.hook
-  # batch emails when many changesets outgoing at one time (client side)
+
+  # one email for all outgoing changesets
   outgoing.notify = python:hgext.notify.hook
 
-  [notify]
-  # config items go here
-
-Required configuration items::
-
-  config = /path/to/file # file containing subscriptions
-
-Optional configuration items::
-
-  test = True            # print messages to stdout for testing
-  strip = 3              # number of slashes to strip for url paths
-  domain = example.com   # domain to use if committer missing domain
-  style = ...            # style file to use when formatting email
-  template = ...         # template to use when formatting email
-  incoming = ...         # template to use when run as incoming hook
-  outgoing = ...         # template to use when run as outgoing hook
-  changegroup = ...      # template to use when run as changegroup hook
-  maxdiff = 300          # max lines of diffs to include (0=none, -1=all)
-  maxsubject = 67        # truncate subject line longer than this
-  diffstat = True        # add a diffstat before the diff content
-  sources = serve        # notify if source of incoming changes in this list
-                         # (serve == ssh or http, push, pull, bundle)
-  merge = False          # send notification for merges (default True)
-  [email]
-  from = user@host.com   # email address to send as if none given
-  [web]
-  baseurl = http://hgserver/... # root of hg web site for browsing commits
-
-The notify config file has same format as a regular hgrc file. It has
-two sections so you can express subscriptions in whatever way is
-handier for you.
-
-::
+Now the hooks are running, subscribers must be assigned to
+repositories. Use the ``[usersubs]`` section to map repositories to a
+given email or the ``[reposubs]`` section to map emails to a single
+repository::
 
   [usersubs]
-  # key is subscriber email, value is ","-separated list of glob patterns
+  # key is subscriber email, value is a comma-separated list of glob
+  # patterns
   user@host = pattern
 
   [reposubs]
-  # key is glob pattern, value is ","-separated list of subscriber emails
+  # key is glob pattern, value is a comma-separated list of subscriber
+  # emails
   pattern = user@host
 
-Glob patterns are matched against path to repository root.
+Glob patterns are matched against absolute path to repository
+root. The subscriptions can be defined in their own file and
+referenced with::
+
+  [notify]
+  config = /path/to/subscriptionsfile
+
+Alternatively, they can be added to Mercurial configuration files by
+setting the previous entry to an empty value.
+
+At this point, notifications should be generated but will not be sent until you
+set the ``notify.test`` entry to ``False``.
+
+Notifications content can be tweaked with the following configuration entries:
+
+notify.test
+  If ``True``, print messages to stdout instead of sending them. Default: True.
+
+notify.sources
+  Space separated list of change sources. Notifications are sent only
+  if it includes the incoming or outgoing changes source. Incoming
+  sources can be ``serve`` for changes coming from http or ssh,
+  ``pull`` for pulled changes, ``unbundle`` for changes added by
+  :hg:`unbundle` or ``push`` for changes being pushed
+  locally. Outgoing sources are the same except for ``unbundle`` which
+  is replaced by ``bundle``. Default: serve.
+
+notify.strip
+  Number of leading slashes to strip from url paths. By default, notifications
+  references repositories with their absolute path. ``notify.strip`` let you
+  turn them into relative paths. For example, ``notify.strip=3`` will change
+  ``/long/path/repository`` into ``repository``. Default: 0.
+
+notify.domain
+  If subscribers emails or the from email have no domain set, complete them
+  with this value.
 
-If you like, you can put notify config file in repository that users
-can push changes to, they can manage their own subscriptions.
+notify.style
+  Style file to use when formatting emails.
+
+notify.template
+  Template to use when formatting emails.
+
+notify.incoming
+  Template to use when run as incoming hook, override ``notify.template``.
+
+notify.outgoing
+  Template to use when run as outgoing hook, override ``notify.template``.
+
+notify.changegroup
+  Template to use when running as changegroup hook, override
+  ``notify.template``.
+
+notify.maxdiff
+  Maximum number of diff lines to include in notification email. Set to 0
+  to disable the diff, -1 to include all of it. Default: 300.
+
+notify.maxsubject
+  Maximum number of characters in emails subject line. Default: 67.
+
+notify.diffstat
+  Set to True to include a diffstat before diff content. Default: True.
+
+notify.merge
+  If True, send notifications for merge changesets. Default: True.
+
+If set, the following entries will also be used to customize the notifications:
+
+email.from
+  Email ``From`` address to use if none can be found in generated email content.
+
+web.baseurl
+  Root repository browsing URL to combine with repository paths when making
+  references. See also ``notify.strip``.
+
 '''
 
 from mercurial.i18n import _
@@ -167,9 +211,6 @@
         return [mail.addressencode(self.ui, s, self.charsets, self.test)
                 for s in sorted(subs)]
 
-    def url(self, path=None):
-        return self.ui.config('web', 'baseurl') + (path or self.root)
-
     def node(self, ctx, **props):
         '''format one changeset, unless it is a suppressed merge.'''
         if not self.merge and len(ctx.parents()) > 1:
--- a/hgext/pager.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/pager.py	Sat Oct 15 14:30:50 2011 -0500
@@ -58,7 +58,7 @@
 from mercurial.i18n import _
 
 def _runpager(p):
-    if not hasattr(os, 'fork'):
+    if not util.safehasattr(os, 'fork'):
         sys.stdout = util.popen(p, 'wb')
         if util.isatty(sys.stderr):
             sys.stderr = sys.stdout
--- a/hgext/patchbomb.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/patchbomb.py	Sat Oct 15 14:30:50 2011 -0500
@@ -57,24 +57,15 @@
 command = cmdutil.command(cmdtable)
 
 def prompt(ui, prompt, default=None, rest=':'):
-    if not ui.interactive() and default is None:
-        raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
     if default:
         prompt += ' [%s]' % default
-    prompt += rest
-    while True:
-        r = ui.prompt(prompt, default=default)
-        if r:
-            return r
-        if default is not None:
-            return default
-        ui.warn(_('Please enter a valid value.\n'))
+    return ui.prompt(prompt + rest, default)
 
-def introneeded(opts, number):
-    '''is an introductory message required?'''
+def introwanted(opts, number):
+    '''is an introductory message apparently wanted?'''
     return number > 1 or opts.get('intro') or opts.get('desc')
 
-def makepatch(ui, repo, patchlines, opts, _charsets, idx, total,
+def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, numbered,
               patchname=None):
 
     desc = []
@@ -141,7 +132,7 @@
         flag = ' ' + flag
 
     subj = desc[0].strip().rstrip('. ')
-    if not introneeded(opts, total):
+    if not numbered:
         subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
     else:
         tlen = len(str(total))
@@ -352,51 +343,66 @@
             ui.write(_('\nWrite the introductory message for the '
                        'patch series.\n\n'))
             body = ui.edit(body, sender)
-            # Save serie description in case sendmail fails
+            # Save series description in case sendmail fails
             msgfile = repo.opener('last-email.txt', 'wb')
             msgfile.write(body)
             msgfile.close()
         return body
 
     def getpatchmsgs(patches, patchnames=None):
-        jumbo = []
         msgs = []
 
         ui.write(_('This patch series consists of %d patches.\n\n')
                  % len(patches))
 
+        # build the intro message, or skip it if the user declines
+        if introwanted(opts, len(patches)):
+            msg = makeintro(patches)
+            if msg:
+                msgs.append(msg)
+
+        # are we going to send more than one message?
+        numbered = len(msgs) + len(patches) > 1
+
+        # now generate the actual patch messages
         name = None
         for i, p in enumerate(patches):
-            jumbo.extend(p)
             if patchnames:
                 name = patchnames[i]
             msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
-                            len(patches), name)
+                            len(patches), numbered, name)
             msgs.append(msg)
 
-        if introneeded(opts, len(patches)):
-            tlen = len(str(len(patches)))
+        return msgs
+
+    def makeintro(patches):
+        tlen = len(str(len(patches)))
 
-            flag = ' '.join(opts.get('flag'))
-            if flag:
-                subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
-            else:
-                subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
-            subj += ' ' + (opts.get('subject') or
-                           prompt(ui, 'Subject: ', rest=subj))
+        flag = opts.get('flag') or ''
+        if flag:
+            flag = ' ' + ' '.join(flag)
+        prefix = '[PATCH %0*d of %d%s]' % (tlen, 0, len(patches), flag)
+
+        subj = (opts.get('subject') or
+                prompt(ui, 'Subject: ', rest=prefix, default=''))
+        if not subj:
+            return None         # skip intro if the user doesn't bother
 
-            body = ''
-            ds = patch.diffstat(jumbo)
-            if ds and opts.get('diffstat'):
-                body = '\n' + ds
+        subj = prefix + ' ' + subj
 
-            body = getdescription(body, sender)
-            msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
-            msg['Subject'] = mail.headencode(ui, subj, _charsets,
-                                             opts.get('test'))
+        body = ''
+        if opts.get('diffstat'):
+            # generate a cumulative diffstat of the whole patch series
+            diffstat = patch.diffstat(sum(patches, []))
+            body = '\n' + diffstat
+        else:
+            diffstat = None
 
-            msgs.insert(0, (msg, subj, ds))
-        return msgs
+        body = getdescription(body, sender)
+        msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
+        msg['Subject'] = mail.headencode(ui, subj, _charsets,
+                                         opts.get('test'))
+        return (msg, subj, diffstat)
 
     def getbundlemsgs(bundle):
         subj = (opts.get('subject')
@@ -429,29 +435,33 @@
 
     showaddrs = []
 
-    def getaddrs(opt, prpt=None, default=None):
-        addrs = opts.get(opt.replace('-', '_'))
-        if opt != 'reply-to':
-            showaddr = '%s:' % opt.capitalize()
-        else:
-            showaddr = 'Reply-To:'
-
+    def getaddrs(header, ask=False, default=None):
+        configkey = header.lower()
+        opt = header.replace('-', '_').lower()
+        addrs = opts.get(opt)
         if addrs:
-            showaddrs.append('%s %s' % (showaddr, ', '.join(addrs)))
+            showaddrs.append('%s: %s' % (header, ', '.join(addrs)))
             return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
 
-        addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or ''
-        if not addrs and prpt:
-            addrs = prompt(ui, prpt, default)
+        # not on the command line: fallback to config and then maybe ask
+        addr = (ui.config('email', configkey) or
+                ui.config('patchbomb', configkey) or
+                '')
+        if not addr and ask:
+            addr = prompt(ui, header, default=default)
+        if addr:
+            showaddrs.append('%s: %s' % (header, addr))
+            return mail.addrlistencode(ui, [addr], _charsets, opts.get('test'))
+        else:
+            return default
 
-        if addrs:
-            showaddrs.append('%s %s' % (showaddr, addrs))
-        return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
-
-    to = getaddrs('to', 'To')
-    cc = getaddrs('cc', 'Cc', '')
-    bcc = getaddrs('bcc')
-    replyto = getaddrs('reply-to')
+    to = getaddrs('To', ask=True)
+    if not to:
+        # we can get here in non-interactive mode
+        raise util.Abort(_('no recipient addresses provided'))
+    cc = getaddrs('Cc', ask=True, default='') or []
+    bcc = getaddrs('Bcc') or []
+    replyto = getaddrs('Reply-To')
 
     if opts.get('diffstat') or opts.get('confirm'):
         ui.write(_('\nFinal summary:\n\n'))
--- a/hgext/progress.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/progress.py	Sat Oct 15 14:30:50 2011 -0500
@@ -27,6 +27,9 @@
 
   [progress]
   delay = 3 # number of seconds (float) before showing the progress bar
+  changedelay = 1 # changedelay: minimum delay before showing a new topic.
+                  # If set to less than 3 * refresh, that value will
+                  # be used instead.
   refresh = 0.1 # time in seconds between refreshes of the progress bar
   format = topic bar number estimate # format of the progress bar
   width = <none> # if set, the maximum width of the progress information
@@ -53,7 +56,7 @@
     return ' '.join(s for s in args if s)
 
 def shouldprint(ui):
-    return (util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty'))
+    return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
 
 def fmtremaining(seconds):
     if seconds < 60:
@@ -105,9 +108,13 @@
         self.printed = False
         self.lastprint = time.time() + float(self.ui.config(
             'progress', 'delay', default=3))
+        self.lasttopic = None
         self.indetcount = 0
         self.refresh = float(self.ui.config(
             'progress', 'refresh', default=0.1))
+        self.changedelay = max(3 * self.refresh,
+                               float(self.ui.config(
+                                   'progress', 'changedelay', default=1)))
         self.order = self.ui.configlist(
             'progress', 'format',
             default=['topic', 'bar', 'number', 'estimate'])
@@ -184,6 +191,7 @@
         else:
             out = spacejoin(head, tail)
         sys.stderr.write('\r' + out[:termwidth])
+        self.lasttopic = topic
         sys.stderr.flush()
 
     def clear(self):
@@ -248,10 +256,18 @@
                 self.topics.append(topic)
             self.topicstates[topic] = pos, item, unit, total
             if now - self.lastprint >= self.refresh and self.topics:
-                self.lastprint = now
-                self.show(now, topic, *self.topicstates[topic])
+                if (self.lasttopic is None # first time we printed
+                    # not a topic change
+                    or topic == self.lasttopic
+                    # it's been long enough we should print anyway
+                    or now - self.lastprint >= self.changedelay):
+                    self.lastprint = now
+                    self.show(now, topic, *self.topicstates[topic])
+
+_singleton = None
 
 def uisetup(ui):
+    global _singleton
     class progressui(ui.__class__):
         _progbar = None
 
@@ -278,7 +294,9 @@
         # we instantiate one globally shared progress bar to avoid
         # competing progress bars when multiple UI objects get created
         if not progressui._progbar:
-            progressui._progbar = progbar(ui)
+            if _singleton is None:
+                _singleton = progbar(ui)
+            progressui._progbar = _singleton
 
 def reposetup(ui, repo):
     uisetup(repo.ui)
--- a/hgext/rebase.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/rebase.py	Sat Oct 15 14:30:50 2011 -0500
@@ -15,7 +15,7 @@
 '''
 
 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
-from mercurial import extensions, copies, patch
+from mercurial import extensions, patch
 from mercurial.commands import templateopts
 from mercurial.node import nullrev
 from mercurial.lock import release
@@ -34,11 +34,15 @@
      _('rebase from the base of the specified changeset '
        '(up to greatest common ancestor of base and dest)'),
      _('REV')),
+    ('r', 'rev', [],
+     _('rebase these revisions'),
+     _('REV')),
     ('d', 'dest', '',
      _('rebase onto the specified changeset'), _('REV')),
     ('', 'collapse', False, _('collapse the rebased changesets')),
     ('m', 'message', '',
      _('use text as collapse commit message'), _('TEXT')),
+    ('e', 'edit', False, _('invoke editor on commit messages')),
     ('l', 'logfile', '',
      _('read collapse commit message from file'), _('FILE')),
     ('', 'keep', False, _('keep original changesets')),
@@ -105,6 +109,10 @@
     skipped = set()
     targetancestors = set()
 
+    editor = None
+    if opts.get('edit'):
+        editor = cmdutil.commitforceeditor
+
     lock = wlock = None
     try:
         lock = repo.lock()
@@ -114,6 +122,7 @@
         destf = opts.get('dest', None)
         srcf = opts.get('source', None)
         basef = opts.get('base', None)
+        revf = opts.get('rev', [])
         contf = opts.get('continue')
         abortf = opts.get('abort')
         collapsef = opts.get('collapse', False)
@@ -151,7 +160,13 @@
         else:
             if srcf and basef:
                 raise util.Abort(_('cannot specify both a '
+                                   'source and a base'))
+            if revf and basef:
+                raise util.Abort(_('cannot specify both a'
                                    'revision and a base'))
+            if revf and srcf:
+                raise util.Abort(_('cannot specify both a'
+                                   'revision and a source'))
             if detachf:
                 if not srcf:
                     raise util.Abort(
@@ -160,7 +175,38 @@
                     raise util.Abort(_('cannot specify a base with detach'))
 
             cmdutil.bailifchanged(repo)
-            result = buildstate(repo, destf, srcf, basef, detachf)
+
+            if not destf:
+                # Destination defaults to the latest revision in the
+                # current branch
+                branch = repo[None].branch()
+                dest = repo[branch]
+            else:
+                dest = repo[destf]
+
+            if revf:
+                revgen = repo.set('%lr', revf)
+            elif srcf:
+                revgen = repo.set('(%r)::', srcf)
+            else:
+                base = basef or '.'
+                revgen = repo.set('(children(ancestor(%r, %d)) and ::(%r))::',
+                                  base, dest, base)
+
+            rebaseset = [c.rev() for c in revgen]
+
+            if not rebaseset:
+                repo.ui.debug('base is ancestor of destination')
+                result = None
+            elif not keepf and list(repo.set('first(children(%ld) - %ld)',
+                                            rebaseset, rebaseset)):
+                raise util.Abort(
+                    _("can't remove original changesets with"
+                      " unrebased descendants"),
+                    hint=_('use --keep to keep original changesets'))
+            else:
+                result = buildstate(repo, dest, rebaseset, detachf)
+
             if not result:
                 # Empty state built, nothing to rebase
                 ui.status(_('nothing to rebase\n'))
@@ -215,9 +261,10 @@
                                         'resolve, then hg rebase --continue)'))
                     finally:
                         ui.setconfig('ui', 'forcemerge', '')
-                updatedirstate(repo, rev, target, p2)
+                cmdutil.duplicatecopies(repo, rev, target, p2)
                 if not collapsef:
-                    newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
+                    newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
+                                          editor=editor)
                 else:
                     # Skip commit if we are collapsing
                     repo.dirstate.setparents(repo[p1].node())
@@ -247,7 +294,7 @@
                         commitmsg += '\n* %s' % repo[rebased].description()
                 commitmsg = ui.edit(commitmsg, repo.ui.username())
             newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
-                                  extrafn=extrafn)
+                                  extrafn=extrafn, editor=editor)
 
         if 'qtip' in repo.tags():
             updatemq(repo, state, skipped, **opts)
@@ -301,21 +348,7 @@
                 external = p.rev()
     return external
 
-def updatedirstate(repo, rev, p1, p2):
-    """Keep track of renamed files in the revision that is going to be rebased
-    """
-    # Here we simulate the copies and renames in the source changeset
-    cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
-    m1 = repo[rev].manifest()
-    m2 = repo[p1].manifest()
-    for k, v in cop.iteritems():
-        if k in m1:
-            if v in m1 or v in m2:
-                repo.dirstate.copy(v, k)
-                if v in m2 and v not in m1 and k in m2:
-                    repo.dirstate.remove(v)
-
-def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
+def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
     'Commit the changes and store useful information in extra'
     try:
         repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
@@ -327,7 +360,7 @@
             extrafn(ctx, extra)
         # Commit might fail if unresolved files exist
         newrev = repo.commit(text=commitmsg, user=ctx.user(),
-                             date=ctx.date(), extra=extra)
+                             date=ctx.date(), extra=extra, editor=editor)
         repo.dirstate.setbranch(repo[newrev].branch())
         return newrev
     except util.Abort:
@@ -515,68 +548,47 @@
         repo.ui.warn(_('rebase aborted\n'))
         return 0
 
-def buildstate(repo, dest, src, base, detach):
-    'Define which revisions are going to be rebased and where'
-    targetancestors = set()
-    detachset = set()
+def buildstate(repo, dest, rebaseset, detach):
+    '''Define which revisions are going to be rebased and where
 
-    if not dest:
-        # Destination defaults to the latest revision in the current branch
-        branch = repo[None].branch()
-        dest = repo[branch].rev()
-    else:
-        dest = repo[dest].rev()
+    repo: repo
+    dest: context
+    rebaseset: set of rev
+    detach: boolean'''
 
     # This check isn't strictly necessary, since mq detects commits over an
     # applied patch. But it prevents messing up the working directory when
     # a partially completed rebase is blocked by mq.
-    if 'qtip' in repo.tags() and (repo[dest].node() in
+    if 'qtip' in repo.tags() and (dest.node() in
                             [s.node for s in repo.mq.applied]):
         raise util.Abort(_('cannot rebase onto an applied mq patch'))
 
-    if src:
-        commonbase = repo[src].ancestor(repo[dest])
-        samebranch = repo[src].branch() == repo[dest].branch()
-        if commonbase == repo[src]:
-            raise util.Abort(_('source is ancestor of destination'))
-        if samebranch and commonbase == repo[dest]:
-            raise util.Abort(_('source is descendant of destination'))
-        source = repo[src].rev()
-        if detach:
-            # We need to keep track of source's ancestors up to the common base
-            srcancestors = set(repo.changelog.ancestors(source))
-            baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
-            detachset = srcancestors - baseancestors
-            detachset.discard(commonbase.rev())
-    else:
-        if base:
-            cwd = repo[base].rev()
-        else:
-            cwd = repo['.'].rev()
+    detachset = set()
+    roots = list(repo.set('roots(%ld)', rebaseset))
+    if not roots:
+        raise util.Abort(_('no matching revisions'))
+    if len(roots) > 1:
+        raise util.Abort(_("can't rebase multiple roots"))
+    root = roots[0]
 
-        if cwd == dest:
-            repo.ui.debug('source and destination are the same\n')
-            return None
-
-        targetancestors = set(repo.changelog.ancestors(dest))
-        if cwd in targetancestors:
-            repo.ui.debug('source is ancestor of destination\n')
-            return None
+    commonbase = root.ancestor(dest)
+    if commonbase == root:
+        raise util.Abort(_('source is ancestor of destination'))
+    if commonbase == dest:
+        samebranch = root.branch() == dest.branch()
+        if samebranch and root in dest.children():
+           repo.ui.debug('source is a child of destination')
+           return None
+        # rebase on ancestor, force detach
+        detach = True
+    if detach:
+        detachset = [c.rev() for c in repo.set('::%d - ::%d - %d',
+                                                root, commonbase, root)]
 
-        cwdancestors = set(repo.changelog.ancestors(cwd))
-        if dest in cwdancestors:
-            repo.ui.debug('source is descendant of destination\n')
-            return None
-
-        cwdancestors.add(cwd)
-        rebasingbranch = cwdancestors - targetancestors
-        source = min(rebasingbranch)
-
-    repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
-    state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
+    repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
+    state = dict.fromkeys(rebaseset, nullrev)
     state.update(dict.fromkeys(detachset, nullmerge))
-    state[source] = nullrev
-    return repo['.'].rev(), repo[dest].rev(), state
+    return repo['.'].rev(), dest.rev(), state
 
 def pullrebase(orig, ui, repo, *args, **opts):
     'Call rebase after pull if the latter has been invoked with --rebase'
--- a/hgext/relink.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/relink.py	Sat Oct 15 14:30:50 2011 -0500
@@ -36,7 +36,8 @@
     command is running. (Both repositories will be locked against
     writes.)
     """
-    if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
+    if (not util.safehasattr(util, 'samefile') or
+        not util.safehasattr(util, 'samedevice')):
         raise util.Abort(_('hardlinks are not supported on this system'))
     src = hg.repository(ui, ui.expandpath(origin or 'default-relink',
                                           origin or 'default'))
--- a/hgext/share.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/share.py	Sat Oct 15 14:30:50 2011 -0500
@@ -6,7 +6,7 @@
 '''share a common history between several working directories'''
 
 from mercurial.i18n import _
-from mercurial import hg, commands
+from mercurial import hg, commands, util
 
 def share(ui, source, dest=None, noupdate=False):
     """create a new shared repository
@@ -28,11 +28,46 @@
 
     return hg.share(ui, source, dest, not noupdate)
 
+def unshare(ui, repo):
+    """convert a shared repository to a normal one
+
+    Copy the store data to the repo and remove the sharedpath data.
+    """
+
+    if repo.sharedpath == repo.path:
+        raise util.Abort(_("this is not a shared repo"))
+
+    destlock = lock = None
+    lock = repo.lock()
+    try:
+        # we use locks here because if we race with commit, we
+        # can end up with extra data in the cloned revlogs that's
+        # not pointed to by changesets, thus causing verify to
+        # fail
+
+        destlock = hg.copystore(ui, repo, repo.path)
+
+        sharefile = repo.join('sharedpath')
+        util.rename(sharefile, sharefile + '.old')
+
+        repo.requirements.discard('sharedpath')
+        repo._writerequirements()
+    finally:
+        destlock and destlock.release()
+        lock and lock.release()
+
+    # update store, spath, sopener and sjoin of repo
+    repo.__init__(ui, repo.root)
+
 cmdtable = {
     "share":
     (share,
      [('U', 'noupdate', None, _('do not create a working copy'))],
      _('[-U] SOURCE [DEST]')),
+    "unshare":
+    (unshare,
+    [],
+    ''),
 }
 
 commands.norepo += " share"
--- a/hgext/transplant.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/hgext/transplant.py	Sat Oct 15 14:30:50 2011 -0500
@@ -81,6 +81,7 @@
         self.opener = scmutil.opener(self.path)
         self.transplants = transplants(self.path, 'transplants',
                                        opener=self.opener)
+        self.editor = None
 
     def applied(self, repo, node, parent):
         '''returns True if a node is already an ancestor of parent
@@ -105,10 +106,11 @@
         diffopts = patch.diffopts(self.ui, opts)
         diffopts.git = True
 
-        lock = wlock = None
+        lock = wlock = tr = None
         try:
             wlock = repo.wlock()
             lock = repo.lock()
+            tr = repo.transaction('transplant')
             for rev in revs:
                 node = revmap[rev]
                 revstr = '%s:%s' % (rev, short(node))
@@ -172,12 +174,15 @@
                     finally:
                         if patchfile:
                             os.unlink(patchfile)
+            tr.close()
             if pulls:
                 repo.pull(source, heads=pulls)
                 merge.update(repo, pulls[-1], False, False, None)
         finally:
             self.saveseries(revmap, merges)
             self.transplants.write()
+            if tr:
+                tr.release()
             lock.release()
             wlock.release()
 
@@ -253,7 +258,8 @@
         else:
             m = match.exact(repo.root, '', files)
 
-        n = repo.commit(message, user, date, extra=extra, match=m)
+        n = repo.commit(message, user, date, extra=extra, match=m,
+                        editor=self.editor)
         if not n:
             # Crash here to prevent an unclear crash later, in
             # transplants.write().  This can happen if patch.patch()
@@ -304,7 +310,8 @@
                                  revlog.hex(parents[0]))
             if merge:
                 repo.dirstate.setparents(p1, parents[1])
-            n = repo.commit(message, user, date, extra=extra)
+            n = repo.commit(message, user, date, extra=extra,
+                            editor=self.editor)
             if not n:
                 raise util.Abort(_('commit failed'))
             if not merge:
@@ -461,6 +468,7 @@
     ('a', 'all', None, _('pull all changesets up to BRANCH')),
     ('p', 'prune', [], _('skip over REV'), _('REV')),
     ('m', 'merge', [], _('merge at REV'), _('REV')),
+    ('e', 'edit', False, _('invoke editor on commit messages')),
     ('', 'log', None, _('append transplant info to log message')),
     ('c', 'continue', None, _('continue last transplant session '
                               'after repair')),
@@ -549,6 +557,8 @@
         opts['filter'] = ui.config('transplant', 'filter')
 
     tp = transplanter(ui, repo)
+    if opts.get('edit'):
+        tp.editor = cmdutil.commitforceeditor
 
     p1, p2 = repo.dirstate.parents()
     if len(repo) > 0 and p1 == revlog.nullid:
--- a/mercurial/archival.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/archival.py	Sat Oct 15 14:30:50 2011 -0500
@@ -195,7 +195,7 @@
             return
         f = self.opener(name, "w", atomictemp=True)
         f.write(data)
-        f.rename()
+        f.close()
         destfile = os.path.join(self.basedir, name)
         os.chmod(destfile, mode)
 
--- a/mercurial/bdiff.c	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/bdiff.c	Sat Oct 15 14:30:50 2011 -0500
@@ -366,11 +366,11 @@
 
 static PyObject *bdiff(PyObject *self, PyObject *args)
 {
-	char *sa, *sb;
+	char *sa, *sb, *rb;
 	PyObject *result = NULL;
 	struct line *al, *bl;
 	struct hunk l, *h;
-	char encode[12], *rb;
+	uint32_t encode[3];
 	int an, bn, len = 0, la, lb, count;
 
 	if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
@@ -407,9 +407,9 @@
 	for (h = l.next; h; h = h->next) {
 		if (h->a1 != la || h->b1 != lb) {
 			len = bl[h->b1].l - bl[lb].l;
-			*(uint32_t *)(encode)     = htonl(al[la].l - al->l);
-			*(uint32_t *)(encode + 4) = htonl(al[h->a1].l - al->l);
-			*(uint32_t *)(encode + 8) = htonl(len);
+			encode[0] = htonl(al[la].l - al->l);
+			encode[1] = htonl(al[h->a1].l - al->l);
+			encode[2] = htonl(len);
 			memcpy(rb, encode, 12);
 			memcpy(rb + 12, bl[lb].l, len);
 			rb += 12 + len;
--- a/mercurial/bookmarks.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/bookmarks.py	Sat Oct 15 14:30:50 2011 -0500
@@ -26,7 +26,13 @@
     bookmarks = {}
     try:
         for line in repo.opener('bookmarks'):
-            sha, refspec = line.strip().split(' ', 1)
+            line = line.strip()
+            if not line:
+                continue
+            if ' ' not in line:
+                repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
+                continue
+            sha, refspec = line.split(' ', 1)
             refspec = encoding.tolocal(refspec)
             try:
                 bookmarks[refspec] = repo.changelog.lookup(sha)
@@ -84,7 +90,7 @@
         file = repo.opener('bookmarks', 'w', atomictemp=True)
         for refspec, node in refs.iteritems():
             file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
-        file.rename()
+        file.close()
 
         # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
         try:
@@ -115,7 +121,7 @@
     try:
         file = repo.opener('bookmarks.current', 'w', atomictemp=True)
         file.write(encoding.fromlocal(mark))
-        file.rename()
+        file.close()
     finally:
         wlock.release()
     repo._bookmarkcurrent = mark
@@ -140,16 +146,15 @@
             marks[mark] = new.node()
             update = True
     if update:
-        write(repo)
+        repo._writebookmarks(marks)
 
 def listbookmarks(repo):
     # We may try to list bookmarks on a repo type that does not
     # support it (e.g., statichttprepository).
-    if not hasattr(repo, '_bookmarks'):
-        return {}
+    marks = getattr(repo, '_bookmarks', {})
 
     d = {}
-    for k, v in repo._bookmarks.iteritems():
+    for k, v in marks.iteritems():
         d[k] = hex(v)
     return d
 
--- a/mercurial/byterange.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/byterange.py	Sat Oct 15 14:30:50 2011 -0500
@@ -103,9 +103,7 @@
         """This effectively allows us to wrap at the instance level.
         Any attribute not found in _this_ object will be searched for
         in self.fo.  This includes methods."""
-        if hasattr(self.fo, name):
-            return getattr(self.fo, name)
-        raise AttributeError(name)
+        return getattr(self.fo, name)
 
     def tell(self):
         """Return the position within the range.
@@ -170,10 +168,8 @@
         offset is relative to the current position (self.realpos).
         """
         assert offset >= 0
-        if not hasattr(self.fo, 'seek'):
-            self._poor_mans_seek(offset)
-        else:
-            self.fo.seek(self.realpos + offset)
+        seek = getattr(self.fo, 'seek', self._poor_mans_seek)
+        seek(self.realpos + offset)
         self.realpos += offset
 
     def _poor_mans_seek(self, offset):
--- a/mercurial/cmdutil.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/cmdutil.py	Sat Oct 15 14:30:50 2011 -0500
@@ -8,7 +8,7 @@
 from node import hex, nullid, nullrev, short
 from i18n import _
 import os, sys, errno, re, tempfile
-import util, scmutil, templater, patch, error, templatekw, revlog
+import util, scmutil, templater, patch, error, templatekw, revlog, copies
 import match as matchmod
 import subrepo
 
@@ -75,6 +75,10 @@
     modified, added, removed, deleted = repo.status()[:4]
     if modified or added or removed or deleted:
         raise util.Abort(_("outstanding uncommitted changes"))
+    ctx = repo[None]
+    for s in ctx.substate:
+        if ctx.sub(s).dirty():
+            raise util.Abort(_("uncommitted changes in subrepo %s") % s)
 
 def logmessage(ui, opts):
     """ get the log message according to -m and -l option """
@@ -109,12 +113,13 @@
         limit = None
     return limit
 
-def makefilename(repo, pat, node,
+def makefilename(repo, pat, node, desc=None,
                   total=None, seqno=None, revwidth=None, pathname=None):
     node_expander = {
         'H': lambda: hex(node),
         'R': lambda: str(repo.changelog.rev(node)),
         'h': lambda: short(node),
+        'm': lambda: re.sub('[^\w]', '_', str(desc))
         }
     expander = {
         '%': lambda: '%',
@@ -154,14 +159,14 @@
         raise util.Abort(_("invalid format spec '%%%s' in output filename") %
                          inst.args[0])
 
-def makefileobj(repo, pat, node=None, total=None,
+def makefileobj(repo, pat, node=None, desc=None, total=None,
                 seqno=None, revwidth=None, mode='wb', pathname=None):
 
     writable = mode not in ('r', 'rb')
 
     if not pat or pat == '-':
         fp = writable and repo.ui.fout or repo.ui.fin
-        if hasattr(fp, 'fileno'):
+        if util.safehasattr(fp, 'fileno'):
             return os.fdopen(os.dup(fp.fileno()), mode)
         else:
             # if this fp can't be duped properly, return
@@ -177,11 +182,11 @@
                         return getattr(self.f, attr)
 
             return wrappedfileobj(fp)
-    if hasattr(pat, 'write') and writable:
+    if util.safehasattr(pat, 'write') and writable:
         return pat
-    if hasattr(pat, 'read') and 'r' in mode:
+    if util.safehasattr(pat, 'read') and 'r' in mode:
         return pat
-    return open(makefilename(repo, pat, node, total, seqno, revwidth,
+    return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
                               pathname),
                 mode)
 
@@ -516,11 +521,13 @@
 
         shouldclose = False
         if not fp:
-            fp = makefileobj(repo, template, node, total=total, seqno=seqno,
-                             revwidth=revwidth, mode='ab')
+            desc_lines = ctx.description().rstrip().split('\n')
+            desc = desc_lines[0]    #Commit always has a first line.
+            fp = makefileobj(repo, template, node, desc=desc, total=total,
+                             seqno=seqno, revwidth=revwidth, mode='ab')
             if fp != template:
                 shouldclose = True
-        if fp != sys.stdout and hasattr(fp, 'name'):
+        if fp != sys.stdout and util.safehasattr(fp, 'name'):
             repo.ui.note("%s\n" % fp.name)
 
         fp.write("# HG changeset patch\n")
@@ -1173,6 +1180,19 @@
         bad.extend(f for f in rejected if f in match.files())
     return bad
 
+def duplicatecopies(repo, rev, p1, p2):
+    "Reproduce copies found in the source revision in the dirstate for grafts"
+    # Here we simulate the copies and renames in the source changeset
+    cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
+    m1 = repo[rev].manifest()
+    m2 = repo[p1].manifest()
+    for k, v in cop.iteritems():
+        if k in m1:
+            if v in m1 or v in m2:
+                repo.dirstate.copy(v, k)
+                if v in m2 and v not in m1 and k in m2:
+                    repo.dirstate.remove(v)
+
 def commit(ui, repo, commitfunc, pats, opts):
     '''commit the specified files or all outstanding changes'''
     date = opts.get('date')
--- a/mercurial/commands.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/commands.py	Sat Oct 15 14:30:50 2011 -0500
@@ -119,6 +119,10 @@
     ('', 'stat', None, _('output diffstat-style summary of changes')),
 ]
 
+mergetoolopts = [
+    ('t', 'tool', '', _('specify merge tool')),
+]
+
 similarityopts = [
     ('s', 'similarity', '',
      _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
@@ -303,6 +307,18 @@
     The archive type is automatically detected based on file
     extension (or override using -t/--type).
 
+    .. container:: verbose
+
+      Examples:
+
+      - create a zip file containing the 1.0 release::
+
+          hg archive -r 1.0 project-1.0.zip
+
+      - create a tarball excluding .hg files::
+
+          hg archive project.tar.gz -X ".hg*"
+
     Valid types are:
 
     :``files``: a directory full of files (default)
@@ -348,10 +364,10 @@
 
 @command('backout',
     [('', 'merge', None, _('merge with old dirstate parent after backout')),
-    ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
-    ('t', 'tool', '', _('specify merge tool')),
+    ('', 'parent', '',
+     _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
     ('r', 'rev', '', _('revision to backout'), _('REV')),
-    ] + walkopts + commitopts + commitopts2,
+    ] + mergetoolopts + walkopts + commitopts + commitopts2,
     _('[OPTION]... [-r] REV'))
 def backout(ui, repo, node=None, rev=None, **opts):
     '''reverse effect of earlier changeset
@@ -363,15 +379,21 @@
     is committed automatically. Otherwise, hg needs to merge the
     changes and the merged result is left uncommitted.
 
-    By default, the pending changeset will have one parent,
-    maintaining a linear history. With --merge, the pending changeset
-    will instead have two parents: the old parent of the working
-    directory and a new child of REV that simply undoes REV.
-
-    Before version 1.7, the behavior without --merge was equivalent to
-    specifying --merge followed by :hg:`update --clean .` to cancel
-    the merge and leave the child of REV as a head to be merged
-    separately.
+    .. note::
+      backout cannot be used to fix either an unwanted or
+      incorrect merge.
+
+    .. container:: verbose
+
+      By default, the pending changeset will have one parent,
+      maintaining a linear history. With --merge, the pending
+      changeset will instead have two parents: the old parent of the
+      working directory and a new child of REV that simply undoes REV.
+
+      Before version 1.7, the behavior without --merge was equivalent
+      to specifying --merge followed by :hg:`update --clean .` to
+      cancel the merge and leave the child of REV as a head to be
+      merged separately.
 
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
@@ -403,8 +425,7 @@
         raise util.Abort(_('cannot backout a change with no parents'))
     if p2 != nullid:
         if not opts.get('parent'):
-            raise util.Abort(_('cannot backout a merge changeset without '
-                               '--parent'))
+            raise util.Abort(_('cannot backout a merge changeset'))
         p = repo.lookup(opts['parent'])
         if p not in (p1, p2):
             raise util.Abort(_('%s is not a parent of %s') %
@@ -486,6 +507,54 @@
     (command not found) will abort the bisection, and any other
     non-zero exit status means the revision is bad.
 
+    .. container:: verbose
+
+      Some examples:
+
+      - start a bisection with known bad revision 12, and good revision 34::
+
+          hg bisect --bad 34
+          hg bisect --good 12
+
+      - advance the current bisection by marking current revision as good or
+        bad::
+
+          hg bisect --good
+          hg bisect --bad
+
+      - mark the current revision, or a known revision, to be skipped (eg. if
+        that revision is not usable because of another issue)::
+
+          hg bisect --skip
+          hg bisect --skip 23
+
+      - forget the current bisection::
+
+          hg bisect --reset
+
+      - use 'make && make tests' to automatically find the first broken
+        revision::
+
+          hg bisect --reset
+          hg bisect --bad 34
+          hg bisect --good 12
+          hg bisect --command 'make && make tests'
+
+      - see all changesets whose states are already known in the current
+        bisection::
+
+          hg log -r "bisect(pruned)"
+
+      - see all changesets that took part in the current bisection::
+
+          hg log -r "bisect(range)"
+
+      - with the graphlog extension, you can even get a nice graph::
+
+          hg log --graph -r "bisect(range)"
+
+      See :hg:`help revsets` for more about the `bisect()` keyword.
+
     Returns 0 on success.
     """
     def extendbisectrange(nodes, good):
@@ -767,7 +836,6 @@
     :hg:`commit --close-branch` to mark this branch as closed.
 
     .. note::
-
        Branch names are permanent. Use :hg:`bookmark` to create a
        light-weight bookmark instead. See :hg:`help glossary` for more
        information about named branches and bookmarks.
@@ -977,56 +1045,84 @@
     The location of the source is added to the new repository's
     ``.hg/hgrc`` file, as the default to be used for future pulls.
 
-    See :hg:`help urls` for valid source format details.
-
-    It is possible to specify an ``ssh://`` URL as the destination, but no
-    ``.hg/hgrc`` and working directory will be created on the remote side.
-    Please see :hg:`help urls` for important details about ``ssh://`` URLs.
-
-    A set of changesets (tags, or branch names) to pull may be specified
-    by listing each changeset (tag, or branch name) with -r/--rev.
-    If -r/--rev is used, the cloned repository will contain only a subset
-    of the changesets of the source repository. Only the set of changesets
-    defined by all -r/--rev options (including all their ancestors)
-    will be pulled into the destination repository.
-    No subsequent changesets (including subsequent tags) will be present
-    in the destination.
-
-    Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
-    local source repositories.
-
-    For efficiency, hardlinks are used for cloning whenever the source
-    and destination are on the same filesystem (note this applies only
-    to the repository data, not to the working directory). Some
-    filesystems, such as AFS, implement hardlinking incorrectly, but
-    do not report errors. In these cases, use the --pull option to
-    avoid hardlinking.
-
-    In some cases, you can clone repositories and the working directory
-    using full hardlinks with ::
-
-      $ cp -al REPO REPOCLONE
-
-    This is the fastest way to clone, but it is not always safe. The
-    operation is not atomic (making sure REPO is not modified during
-    the operation is up to you) and you have to make sure your editor
-    breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
-    this is not compatible with certain extensions that place their
-    metadata under the .hg directory, such as mq.
-
-    Mercurial will update the working directory to the first applicable
-    revision from this list:
-
-    a) null if -U or the source repository has no changesets
-    b) if -u . and the source repository is local, the first parent of
-       the source repository's working directory
-    c) the changeset specified with -u (if a branch name, this means the
-       latest head of that branch)
-    d) the changeset specified with -r
-    e) the tipmost head specified with -b
-    f) the tipmost head specified with the url#branch source syntax
-    g) the tipmost head of the default branch
-    h) tip
+    Only local paths and ``ssh://`` URLs are supported as
+    destinations. For ``ssh://`` destinations, no working directory or
+    ``.hg/hgrc`` will be created on the remote side.
+
+    To pull only a subset of changesets, specify one or more revisions
+    identifiers with -r/--rev or branches with -b/--branch. The
+    resulting clone will contain only the specified changesets and
+    their ancestors. These options (or 'clone src#rev dest') imply
+    --pull, even for local source repositories. Note that specifying a
+    tag will include the tagged changeset but not the changeset
+    containing the tag.
+
+    To check out a particular version, use -u/--update, or
+    -U/--noupdate to create a clone with no working directory.
+
+    .. container:: verbose
+
+      For efficiency, hardlinks are used for cloning whenever the
+      source and destination are on the same filesystem (note this
+      applies only to the repository data, not to the working
+      directory). Some filesystems, such as AFS, implement hardlinking
+      incorrectly, but do not report errors. In these cases, use the
+      --pull option to avoid hardlinking.
+
+      In some cases, you can clone repositories and the working
+      directory using full hardlinks with ::
+
+        $ cp -al REPO REPOCLONE
+
+      This is the fastest way to clone, but it is not always safe. The
+      operation is not atomic (making sure REPO is not modified during
+      the operation is up to you) and you have to make sure your
+      editor breaks hardlinks (Emacs and most Linux Kernel tools do
+      so). Also, this is not compatible with certain extensions that
+      place their metadata under the .hg directory, such as mq.
+
+      Mercurial will update the working directory to the first applicable
+      revision from this list:
+
+      a) null if -U or the source repository has no changesets
+      b) if -u . and the source repository is local, the first parent of
+         the source repository's working directory
+      c) the changeset specified with -u (if a branch name, this means the
+         latest head of that branch)
+      d) the changeset specified with -r
+      e) the tipmost head specified with -b
+      f) the tipmost head specified with the url#branch source syntax
+      g) the tipmost head of the default branch
+      h) tip
+
+      Examples:
+
+      - clone a remote repository to a new directory named hg/::
+
+          hg clone http://selenic.com/hg
+
+      - create a lightweight local clone::
+
+          hg clone project/ project-feature/
+
+      - clone from an absolute path on an ssh server (note double-slash)::
+
+          hg clone ssh://user@server//home/projects/alpha/
+
+      - do a high-speed clone over a LAN while checking out a
+        specified version::
+
+          hg clone --uncompressed http://server/repo -u 1.5
+
+      - create a repository without changesets after a particular revision::
+
+          hg clone -r 04e544 experimental/ good/
+
+      - clone (and track) a particular named branch::
+
+          hg clone http://selenic.com/hg#stable
+
+    See :hg:`help urls` for details on specifying URLs.
 
     Returns 0 on success.
     """
@@ -1102,8 +1198,8 @@
     ctx = repo[node]
     parents = ctx.parents()
 
-    if bheads and not [x for x in parents
-                       if x.node() in bheads and x.branch() == branch]:
+    if (bheads and node not in bheads and not
+        [x for x in parents if x.node() in bheads and x.branch() == branch]):
         ui.status(_('created new head\n'))
         # The message is not printed for initial roots. For the other
         # changesets, it is printed in the following situations:
@@ -1656,8 +1752,9 @@
 def debugignore(ui, repo, *values, **opts):
     """display the combined ignore pattern"""
     ignore = repo.dirstate._ignore
-    if hasattr(ignore, 'includepat'):
-        ui.write("%s\n" % ignore.includepat)
+    includepat = getattr(ignore, 'includepat', None)
+    if includepat is not None:
+        ui.write("%s\n" % includepat)
     else:
         raise util.Abort(_("no ignore patterns found"))
 
@@ -1755,6 +1852,7 @@
               % os.path.dirname(__file__))
     try:
         import bdiff, mpatch, base85, osutil
+        dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
     except Exception, inst:
         ui.write(" %s\n" % inst)
         ui.write(_(" One or more extensions could not be found"))
@@ -1762,9 +1860,10 @@
         problems += 1
 
     # templates
-    ui.status(_("Checking templates...\n"))
+    import templater
+    p = templater.templatepath()
+    ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
     try:
-        import templater
         templater.templater(templater.templatepath("map-cmdline.default"))
     except Exception, inst:
         ui.write(" %s\n" % inst)
@@ -2170,6 +2269,32 @@
     Use the -g/--git option to generate diffs in the git extended diff
     format. For more information, read :hg:`help diffs`.
 
+    .. container:: verbose
+
+      Examples:
+
+      - compare a file in the current working directory to its parent::
+
+          hg diff foo.c
+
+      - compare two historical versions of a directory, with rename info::
+
+          hg diff --git -r 1.0:1.2 lib/
+
+      - get change stats relative to the last change on some date::
+
+          hg diff --stat -r "date('may 2')"
+
+      - diff all newly-added files that contain a keyword::
+
+          hg diff "set:added() and grep(GNU)"
+
+      - compare a revision and its parents::
+
+          hg diff -c 9353         # compare against first parent
+          hg diff -r 9353^:9353   # same using revset syntax
+          hg diff -r 9353^2:9353  # compare against the second parent
+
     Returns 0 on success.
     """
 
@@ -2225,6 +2350,7 @@
     :``%R``: changeset revision number
     :``%b``: basename of the exporting repository
     :``%h``: short-form changeset hash (12 hexadecimal digits)
+    :``%m``: first line of the commit message (only alphanumeric characters)
     :``%n``: zero-padded sequence number, starting at 1
     :``%r``: zero-padded changeset revision number
 
@@ -2238,6 +2364,25 @@
     With the --switch-parent option, the diff will be against the
     second parent. It can be useful to review a merge.
 
+    .. container:: verbose
+
+      Examples:
+
+      - use export and import to transplant a bugfix to the current
+        branch::
+
+          hg export -r 9353 | hg import -
+
+      - export all the changesets between two revisions to a file with
+        rename information::
+
+          hg export --git -r 123:150 > changes.txt
+
+      - split outgoing changes into a series of patches with
+        descriptive names::
+
+          hg export -r "outgoing()" -o "%n-%m.patch"
+
     Returns 0 on success.
     """
     changesets += tuple(opts.get('rev', []))
@@ -2265,6 +2410,18 @@
 
     To undo a forget before the next commit, see :hg:`add`.
 
+    .. container:: verbose
+
+      Examples:
+
+      - forget newly-added binary files::
+
+          hg forget "set:added() and binary()"
+
+      - forget files that would be excluded by .hgignore::
+
+          hg forget "set:hgignore()"
+
     Returns 0 on success.
     """
 
@@ -2290,6 +2447,160 @@
     repo[None].forget(forget)
     return errs
 
+@command(
+    'graft',
+    [('c', 'continue', False, _('resume interrupted graft')),
+     ('e', 'edit', False, _('invoke editor on commit messages')),
+     ('D', 'currentdate', False,
+      _('record the current date as commit date')),
+     ('U', 'currentuser', False,
+      _('record the current user as committer'), _('DATE'))]
+    + commitopts2 + mergetoolopts,
+    _('[OPTION]... REVISION...'))
+def graft(ui, repo, *revs, **opts):
+    '''copy changes from other branches onto the current branch
+
+    This command uses Mercurial's merge logic to copy individual
+    changes from other branches without merging branches in the
+    history graph. This is sometimes known as 'backporting' or
+    'cherry-picking'. By default, graft will copy user, date, and
+    description from the source changesets.
+
+    Changesets that are ancestors of the current revision, that have
+    already been grafted, or that are merges will be skipped.
+
+    If a graft merge results in conflicts, the graft process is
+    aborted so that the current merge can be manually resolved. Once
+    all conflicts are addressed, the graft process can be continued
+    with the -c/--continue option.
+
+    .. note::
+      The -c/--continue option does not reapply earlier options.
+
+    .. container:: verbose
+
+      Examples:
+
+      - copy a single change to the stable branch and edit its description::
+
+          hg update stable
+          hg graft --edit 9393
+
+      - graft a range of changesets with one exception, updating dates::
+
+          hg graft -D "2085::2093 and not 2091"
+
+      - continue a graft after resolving conflicts::
+
+          hg graft -c
+
+      - show the source of a grafted changeset::
+
+          hg log --debug -r tip
+
+    Returns 0 on successful completion.
+    '''
+
+    if not opts.get('user') and opts.get('currentuser'):
+        opts['user'] = ui.username()
+    if not opts.get('date') and opts.get('currentdate'):
+        opts['date'] = "%d %d" % util.makedate()
+
+    editor = None
+    if opts.get('edit'):
+        editor = cmdutil.commitforceeditor
+
+    cont = False
+    if opts['continue']:
+        cont = True
+        if revs:
+            raise util.Abort(_("can't specify --continue and revisions"))
+        # read in unfinished revisions
+        try:
+            nodes = repo.opener.read('graftstate').splitlines()
+            revs = [repo[node].rev() for node in nodes]
+        except IOError, inst:
+            if inst.errno != errno.ENOENT:
+                raise
+            raise util.Abort(_("no graft state found, can't continue"))
+    else:
+        cmdutil.bailifchanged(repo)
+        if not revs:
+            raise util.Abort(_('no revisions specified'))
+        revs = scmutil.revrange(repo, revs)
+
+    # check for merges
+    for ctx in repo.set('%ld and merge()', revs):
+        ui.warn(_('skipping ungraftable merge revision %s\n') % ctx.rev())
+        revs.remove(ctx.rev())
+    if not revs:
+        return -1
+
+    # check for ancestors of dest branch
+    for ctx in repo.set('::. and %ld', revs):
+        ui.warn(_('skipping ancestor revision %s\n') % ctx.rev())
+        revs.remove(ctx.rev())
+    if not revs:
+        return -1
+
+    # check ancestors for earlier grafts
+    ui.debug('scanning for existing transplants')
+    for ctx in repo.set("::. - ::%ld", revs):
+        n = ctx.extra().get('source')
+        if n and n in repo:
+            r = repo[n].rev()
+            ui.warn(_('skipping already grafted revision %s\n') % r)
+            revs.remove(r)
+    if not revs:
+        return -1
+
+    for pos, ctx in enumerate(repo.set("%ld", revs)):
+        current = repo['.']
+        ui.status('grafting revision %s', ctx.rev())
+
+        # we don't merge the first commit when continuing
+        if not cont:
+            # perform the graft merge with p1(rev) as 'ancestor'
+            try:
+                # ui.forcemerge is an internal variable, do not document
+                repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+                stats = mergemod.update(repo, ctx.node(), True, True, False,
+                                        ctx.p1().node())
+            finally:
+                ui.setconfig('ui', 'forcemerge', '')
+            # drop the second merge parent
+            repo.dirstate.setparents(current.node(), nullid)
+            repo.dirstate.write()
+            # fix up dirstate for copies and renames
+            cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid)
+            # report any conflicts
+            if stats and stats[3] > 0:
+                # write out state for --continue
+                nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
+                repo.opener.write('graftstate', ''.join(nodelines))
+                raise util.Abort(
+                    _("unresolved conflicts, can't continue"),
+                    hint=_('use hg resolve and hg graft --continue'))
+        else:
+            cont = False
+
+        # commit
+        extra = {'source': ctx.hex()}
+        user = ctx.user()
+        if opts.get('user'):
+            user = opts['user']
+        date = ctx.date()
+        if opts.get('date'):
+            date = opts['date']
+        repo.commit(text=ctx.description(), user=user,
+                    date=date, extra=extra, editor=editor)
+
+    # remove state when we complete successfully
+    if os.path.exists(repo.join('graftstate')):
+        util.unlinkpath(repo.join('graftstate'))
+
+    return 0
+
 @command('grep',
     [('0', 'print0', None, _('end fields with NUL')),
     ('', 'all', None, _('print all revisions that match')),
@@ -2576,7 +2887,7 @@
     [('e', 'extension', None, _('show only help for extensions')),
      ('c', 'command', None, _('show only help for commands'))],
     _('[-ec] [TOPIC]'))
-def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
+def help_(ui, name=None, unknowncmd=False, full=True, **opts):
     """show help for a given topic or a help overview
 
     With no arguments, print a list of commands with short help messages.
@@ -2586,14 +2897,67 @@
 
     Returns 0 if successful.
     """
-    option_lists = []
+
     textwidth = min(ui.termwidth(), 80) - 2
 
-    def addglobalopts(aliases):
+    def optrst(options):
+        data = []
+        multioccur = False
+        for option in options:
+            if len(option) == 5:
+                shortopt, longopt, default, desc, optlabel = option
+            else:
+                shortopt, longopt, default, desc = option
+                optlabel = _("VALUE") # default label
+
+            if _("DEPRECATED") in desc and not ui.verbose:
+                continue
+
+            so = ''
+            if shortopt:
+                so = '-' + shortopt
+            lo = '--' + longopt
+            if default:
+                desc += _(" (default: %s)") % default
+
+            if isinstance(default, list):
+                lo += " %s [+]" % optlabel
+                multioccur = True
+            elif (default is not None) and not isinstance(default, bool):
+                lo += " %s" % optlabel
+
+            data.append((so, lo, desc))
+
+        rst = minirst.maketable(data, 1)
+
+        if multioccur:
+            rst += _("\n[+] marked option can be specified multiple times\n")
+
+        return rst
+
+    # list all option lists
+    def opttext(optlist, width):
+        rst = ''
+        if not optlist:
+            return ''
+
+        for title, options in optlist:
+            rst += '\n%s\n' % title
+            if options:
+                rst += "\n"
+                rst += optrst(options)
+                rst += '\n'
+
+        return '\n' + minirst.format(rst, width)
+
+    def addglobalopts(optlist, aliases):
+        if ui.quiet:
+            return []
+
         if ui.verbose:
-            option_lists.append((_("global options:"), globalopts))
+            optlist.append((_("global options:"), globalopts))
             if name == 'shortlist':
-                option_lists.append((_('use "hg help" for the full list '
+                optlist.append((_('use "hg help" for the full list '
                                        'of commands'), ()))
         else:
             if name == 'shortlist':
@@ -2605,14 +2969,10 @@
                 msg = _('use "hg -v help%s" to show builtin aliases and '
                         'global options') % (name and " " + name or "")
             else:
-                msg = _('use "hg -v help %s" to show global options') % name
-            option_lists.append((msg, ()))
+                msg = _('use "hg -v help %s" to show more info') % name
+            optlist.append((msg, ()))
 
     def helpcmd(name):
-        if with_version:
-            version_(ui)
-            ui.write('\n')
-
         try:
             aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
         except error.AmbiguousCommand, inst:
@@ -2620,7 +2980,7 @@
             # except block, nor can be used inside a lambda. python issue4617
             prefix = inst.args[0]
             select = lambda c: c.lstrip('^').startswith(prefix)
-            helplist(_('list of commands:\n\n'), select)
+            helplist(select)
             return
 
         # check if it's an invalid alias and display its error if it is
@@ -2629,42 +2989,33 @@
                 entry[0](ui)
             return
 
+        rst = ""
+
         # synopsis
         if len(entry) > 2:
             if entry[2].startswith('hg'):
-                ui.write("%s\n" % entry[2])
+                rst += "%s\n" % entry[2]
             else:
-                ui.write('hg %s %s\n' % (aliases[0], entry[2]))
+                rst += 'hg %s %s\n' % (aliases[0], entry[2])
         else:
-            ui.write('hg %s\n' % aliases[0])
+            rst += 'hg %s\n' % aliases[0]
 
         # aliases
         if full and not ui.quiet and len(aliases) > 1:
-            ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
+            rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
 
         # description
         doc = gettext(entry[0].__doc__)
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(entry[0], 'definition'):  # aliased command
+        if util.safehasattr(entry[0], 'definition'):  # aliased command
             if entry[0].definition.startswith('!'):  # shell alias
                 doc = _('shell alias for::\n\n    %s') % entry[0].definition[1:]
             else:
                 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
         if ui.quiet or not full:
             doc = doc.splitlines()[0]
-        keep = ui.verbose and ['verbose'] or []
-        formatted, pruned = minirst.format(doc, textwidth, keep=keep)
-        ui.write("\n%s\n" % formatted)
-        if pruned:
-            ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
-
-        if not ui.quiet:
-            # options
-            if entry[1]:
-                option_lists.append((_("options:\n"), entry[1]))
-
-            addglobalopts(False)
+        rst += "\n" + doc + "\n"
 
         # check if this command shadows a non-trivial (multi-line)
         # extension help text
@@ -2674,11 +3025,38 @@
             if '\n' in doc.strip():
                 msg = _('use "hg help -e %s" to show help for '
                         'the %s extension') % (name, name)
-                ui.write('\n%s\n' % msg)
+                rst += '\n%s\n' % msg
         except KeyError:
             pass
 
-    def helplist(header, select=None):
+        # options
+        if not ui.quiet and entry[1]:
+            rst += '\noptions:\n\n'
+            rst += optrst(entry[1])
+
+        if ui.verbose:
+            rst += '\nglobal options:\n\n'
+            rst += optrst(globalopts)
+
+        keep = ui.verbose and ['verbose'] or []
+        formatted, pruned = minirst.format(rst, textwidth, keep=keep)
+        ui.write(formatted)
+
+        if not ui.verbose:
+            if not full:
+                ui.write(_('\nuse "hg help %s" to show the full help text\n')
+                           % name)
+            elif not ui.quiet:
+                ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
+
+
+    def helplist(select=None):
+        # list of commands
+        if name == "shortlist":
+            header = _('basic commands:\n\n')
+        else:
+            header = _('list of commands:\n\n')
+
         h = {}
         cmds = {}
         for c, e in table.iteritems():
@@ -2718,8 +3096,22 @@
                                              initindent=' %-*s   ' % (m, f),
                                              hangindent=' ' * (m + 4))))
 
-        if not ui.quiet:
-            addglobalopts(True)
+        if not name:
+            text = help.listexts(_('enabled extensions:'), extensions.enabled())
+            if text:
+                ui.write("\n%s" % minirst.format(text, textwidth))
+
+            ui.write(_("\nadditional help topics:\n\n"))
+            topics = []
+            for names, header, doc in help.helptable:
+                topics.append((sorted(names, key=len, reverse=True)[0], header))
+            topics_len = max([len(s[0]) for s in topics])
+            for t, desc in topics:
+                ui.write(" %-*s  %s\n" % (topics_len, t, desc))
+
+        optlist = []
+        addglobalopts(optlist, True)
+        ui.write(opttext(optlist, textwidth))
 
     def helptopic(name):
         for names, header, doc in help.helptable:
@@ -2731,11 +3123,11 @@
         # description
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(doc, '__call__'):
+        if util.safehasattr(doc, '__call__'):
             doc = doc()
 
         ui.write("%s\n\n" % header)
-        ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
+        ui.write("%s" % minirst.format(doc, textwidth, indent=4))
         try:
             cmdutil.findcmd(name, table)
             ui.write(_('\nuse "hg help -c %s" to see help for '
@@ -2760,7 +3152,7 @@
         ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
         if tail:
             ui.write(minirst.format(tail, textwidth))
-            ui.status('\n\n')
+            ui.status('\n')
 
         if mod:
             try:
@@ -2768,7 +3160,7 @@
             except AttributeError:
                 ct = {}
             modcmds = set([c.split('|', 1)[0] for c in ct])
-            helplist(_('list of commands:\n\n'), modcmds.__contains__)
+            helplist(modcmds.__contains__)
         else:
             ui.write(_('use "hg help extensions" for information on enabling '
                        'extensions\n'))
@@ -2780,7 +3172,7 @@
         msg = help.listexts(_("'%s' is provided by the following "
                               "extension:") % cmd, {ext: doc}, indent=4)
         ui.write(minirst.format(msg, textwidth))
-        ui.write('\n\n')
+        ui.write('\n')
         ui.write(_('use "hg help extensions" for information on enabling '
                    'extensions\n'))
 
@@ -2803,87 +3195,12 @@
                 i = inst
         if i:
             raise i
-
     else:
         # program name
-        if ui.verbose or with_version:
-            version_(ui)
-        else:
-            ui.status(_("Mercurial Distributed SCM\n"))
+        ui.status(_("Mercurial Distributed SCM\n"))
         ui.status('\n')
-
-        # list of commands
-        if name == "shortlist":
-            header = _('basic commands:\n\n')
-        else:
-            header = _('list of commands:\n\n')
-
-        helplist(header)
-        if name != 'shortlist':
-            text = help.listexts(_('enabled extensions:'), extensions.enabled())
-            if text:
-                ui.write("\n%s\n" % minirst.format(text, textwidth))
-
-    # list all option lists
-    opt_output = []
-    multioccur = False
-    for title, options in option_lists:
-        opt_output.append(("\n%s" % title, None))
-        for option in options:
-            if len(option) == 5:
-                shortopt, longopt, default, desc, optlabel = option
-            else:
-                shortopt, longopt, default, desc = option
-                optlabel = _("VALUE") # default label
-
-            if _("DEPRECATED") in desc and not ui.verbose:
-                continue
-            if isinstance(default, list):
-                numqualifier = " %s [+]" % optlabel
-                multioccur = True
-            elif (default is not None) and not isinstance(default, bool):
-                numqualifier = " %s" % optlabel
-            else:
-                numqualifier = ""
-            opt_output.append(("%2s%s" %
-                               (shortopt and "-%s" % shortopt,
-                                longopt and " --%s%s" %
-                                (longopt, numqualifier)),
-                               "%s%s" % (desc,
-                                         default
-                                         and _(" (default: %s)") % default
-                                         or "")))
-    if multioccur:
-        msg = _("\n[+] marked option can be specified multiple times")
-        if ui.verbose and name != 'shortlist':
-            opt_output.append((msg, None))
-        else:
-            opt_output.insert(-1, (msg, None))
-
-    if not name:
-        ui.write(_("\nadditional help topics:\n\n"))
-        topics = []
-        for names, header, doc in help.helptable:
-            topics.append((sorted(names, key=len, reverse=True)[0], header))
-        topics_len = max([len(s[0]) for s in topics])
-        for t, desc in topics:
-            ui.write(" %-*s  %s\n" % (topics_len, t, desc))
-
-    if opt_output:
-        colwidth = encoding.colwidth
-        # normalize: (opt or message, desc or None, width of opt)
-        entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
-                   for opt, desc in opt_output]
-        hanging = max([e[2] for e in entries])
-        for opt, desc, width in entries:
-            if desc:
-                initindent = ' %s%s  ' % (opt, ' ' * (hanging - width))
-                hangindent = ' ' * (hanging + 3)
-                ui.write('%s\n' % (util.wrap(desc, textwidth,
-                                             initindent=initindent,
-                                             hangindent=hangindent)))
-            else:
-                ui.write("%s\n" % opt)
+        helplist()
+
 
 @command('identify|id',
     [('r', 'rev', '',
@@ -2909,6 +3226,22 @@
     Specifying a path to a repository root or Mercurial bundle will
     cause lookup to operate on that repository/bundle.
 
+    .. container:: verbose
+
+      Examples:
+
+      - generate a build identifier for the working directory::
+
+          hg id --id > build-id.dat
+
+      - find the revision corresponding to a tag::
+
+          hg id -n -r 1.3
+
+      - check the most recent revision of a remote repository::
+
+          hg id -r tip http://selenic.com/hg/
+
     Returns 0 if successful.
     """
 
@@ -3007,6 +3340,7 @@
      _('directory strip option for patch. This has the same '
        'meaning as the corresponding patch option'), _('NUM')),
     ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
+    ('e', 'edit', False, _('invoke editor on commit messages')),
     ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
     ('', 'no-commit', None,
      _("don't commit, just update the working directory")),
@@ -3057,6 +3391,27 @@
     a URL is specified, the patch will be downloaded from it.
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
+    .. container:: verbose
+
+      Examples:
+
+      - import a traditional patch from a website and detect renames::
+
+          hg import -s 80 http://example.com/bugfix.patch
+
+      - import a changeset from an hgweb server::
+
+          hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
+
+      - import all the patches in an Unix-style mbox::
+
+          hg import incoming-patches.mbox
+
+      - attempt to exactly restore an exported changeset (not always
+        possible)::
+
+          hg import --exact proposed-fix.patch
+
     Returns 0 on success.
     """
     patches = (patch1,) + patches
@@ -3065,6 +3420,10 @@
     if date:
         opts['date'] = util.parsedate(date)
 
+    editor = cmdutil.commiteditor
+    if opts.get('edit'):
+        editor = cmdutil.commitforceeditor
+
     update = not opts.get('bypass')
     if not update and opts.get('no_commit'):
         raise util.Abort(_('cannot use --no-commit with --bypass'))
@@ -3080,9 +3439,9 @@
     if (opts.get('exact') or not opts.get('force')) and update:
         cmdutil.bailifchanged(repo)
 
-    d = opts["base"]
+    base = opts["base"]
     strip = opts["strip"]
-    wlock = lock = None
+    wlock = lock = tr = None
     msgs = []
 
     def checkexact(repo, n, nodeid):
@@ -3095,8 +3454,8 @@
             patch.extract(ui, hunk)
 
         if not tmpname:
-            return None
-        commitid = _('to working directory')
+            return (None, None)
+        msg = _('applied to working directory')
 
         try:
             cmdline_message = cmdutil.logmessage(ui, opts)
@@ -3151,11 +3510,8 @@
                         m = scmutil.matchfiles(repo, files or [])
                     n = repo.commit(message, opts.get('user') or user,
                                     opts.get('date') or date, match=m,
-                                    editor=cmdutil.commiteditor)
+                                    editor=editor)
                     checkexact(repo, n, nodeid)
-                    # Force a dirstate write so that the next transaction
-                    # backups an up-to-date file.
-                    repo.dirstate.write()
             else:
                 if opts.get('exact') or opts.get('import_branch'):
                     branch = branch or 'default'
@@ -3181,45 +3537,52 @@
                 finally:
                     store.close()
             if n:
-                commitid = short(n)
-            return commitid
+                msg = _('created %s') % short(n)
+            return (msg, n)
         finally:
             os.unlink(tmpname)
 
     try:
         wlock = repo.wlock()
         lock = repo.lock()
+        tr = repo.transaction('import')
         parents = repo.parents()
-        lastcommit = None
-        for p in patches:
-            pf = os.path.join(d, p)
-
-            if pf == '-':
-                ui.status(_("applying patch from stdin\n"))
-                pf = ui.fin
+        for patchurl in patches:
+            if patchurl == '-':
+                ui.status(_('applying patch from stdin\n'))
+                patchfile = ui.fin
+                patchurl = 'stdin'      # for error message
             else:
-                ui.status(_("applying %s\n") % p)
-                pf = url.open(ui, pf)
+                patchurl = os.path.join(base, patchurl)
+                ui.status(_('applying %s\n') % patchurl)
+                patchfile = url.open(ui, patchurl)
 
             haspatch = False
-            for hunk in patch.split(pf):
-                commitid = tryone(ui, hunk, parents)
-                if commitid:
+            for hunk in patch.split(patchfile):
+                (msg, node) = tryone(ui, hunk, parents)
+                if msg:
                     haspatch = True
-                    if lastcommit:
-                        ui.status(_('applied %s\n') % lastcommit)
-                    lastcommit = commitid
+                    ui.note(msg + '\n')
                 if update or opts.get('exact'):
                     parents = repo.parents()
                 else:
-                    parents = [repo[commitid]]
+                    parents = [repo[node]]
 
             if not haspatch:
-                raise util.Abort(_('no diffs found'))
-
+                raise util.Abort(_('%s: no diffs found') % patchurl)
+
+        tr.close()
         if msgs:
             repo.savecommitmessage('\n* * *\n'.join(msgs))
+    except:
+        # wlock.release() indirectly calls dirstate.write(): since
+        # we're crashing, we do not want to change the working dir
+        # parent after all, so make sure it writes nothing
+        repo.dirstate.invalidate()
+        raise
     finally:
+        if tr:
+            tr.release()
         release(lock, wlock)
 
 @command('incoming|in',
@@ -3356,18 +3719,14 @@
     Print the revision history of the specified files or the entire
     project.
 
+    If no revision range is specified, the default is ``tip:0`` unless
+    --follow is set, in which case the working directory parent is
+    used as the starting revision.
+
     File history is shown without following rename or copy history of
     files. Use -f/--follow with a filename to follow history across
     renames and copies. --follow without a filename will only show
-    ancestors or descendants of the starting revision. --follow-first
-    only follows the first parent of merge revisions.
-
-    If no revision range is specified, the default is ``tip:0`` unless
-    --follow is set, in which case the working directory parent is
-    used as the starting revision. You can specify a revision set for
-    log, see :hg:`help revsets` for more information.
-
-    See :hg:`help dates` for a list of formats valid for -d/--date.
+    ancestors or descendants of the starting revision.
 
     By default this command prints revision number and changeset id,
     tags, non-trivial parents, user, date and time, and a summary for
@@ -3380,6 +3739,57 @@
        its first parent. Also, only files different from BOTH parents
        will appear in files:.
 
+    .. note::
+       for performance reasons, log FILE may omit duplicate changes
+       made on branches and will not show deletions. To see all
+       changes including duplicates and deletions, use the --removed
+       switch.
+
+    .. container:: verbose
+
+      Some examples:
+
+      - changesets with full descriptions and file lists::
+
+          hg log -v
+
+      - changesets ancestral to the working directory::
+
+          hg log -f
+
+      - last 10 commits on the current branch::
+
+          hg log -l 10 -b .
+
+      - changesets showing all modifications of a file, including removals::
+
+          hg log --removed file.c
+
+      - all changesets that touch a directory, with diffs, excluding merges::
+
+          hg log -Mp lib/
+
+      - all revision numbers that match a keyword::
+
+          hg log -k bug --template "{rev}\\n"
+
+      - check if a given changeset is included is a tagged release::
+
+          hg log -r "a21ccf and ancestor(1.9)"
+
+      - find all changesets by some user in a date range::
+
+          hg log -k alice -d "may 2008 to jul 2008"
+
+      - summary of all changesets after the last tag::
+
+          hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
+
+    See :hg:`help dates` for a list of formats valid for -d/--date.
+
+    See :hg:`help revisions` and :hg:`help revsets` for more about
+    specifying revisions.
+
     Returns 0 on success.
     """
 
@@ -3507,10 +3917,10 @@
 
 @command('^merge',
     [('f', 'force', None, _('force a merge with outstanding changes')),
-    ('t', 'tool', '', _('specify merge tool')),
     ('r', 'rev', '', _('revision to merge'), _('REV')),
     ('P', 'preview', None,
-     _('review revisions to merge (no merge is performed)'))],
+     _('review revisions to merge (no merge is performed)'))
+     ] + mergetoolopts,
     _('[-P] [-f] [[-r] REV]'))
 def merge(ui, repo, node=None, **opts):
     """merge working directory with another revision
@@ -3589,7 +3999,7 @@
 
     try:
         # ui.forcemerge is an internal variable, do not document
-        ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+        repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
         return hg.merge(repo, node, force=opts.get('force'))
     finally:
         ui.setconfig('ui', 'forcemerge', '')
@@ -3935,31 +4345,36 @@
 def remove(ui, repo, *pats, **opts):
     """remove the specified files on the next commit
 
-    Schedule the indicated files for removal from the repository.
-
-    This only removes files from the current branch, not from the
-    entire project history. -A/--after can be used to remove only
-    files that have already been deleted, -f/--force can be used to
-    force deletion, and -Af can be used to remove files from the next
-    revision without deleting them from the working directory.
-
-    The following table details the behavior of remove for different
-    file states (columns) and option combinations (rows). The file
-    states are Added [A], Clean [C], Modified [M] and Missing [!] (as
-    reported by :hg:`status`). The actions are Warn, Remove (from
-    branch) and Delete (from disk)::
-
-             A  C  M  !
-      none   W  RD W  R
-      -f     R  RD RD R
-      -A     W  W  W  R
-      -Af    R  R  R  R
-
-    Note that remove never deletes files in Added [A] state from the
-    working directory, not even if option --force is specified.
+    Schedule the indicated files for removal from the current branch.
 
     This command schedules the files to be removed at the next commit.
-    To undo a remove before that, see :hg:`revert`.
+    To undo a remove before that, see :hg:`revert`. To undo added
+    files, see :hg:`forget`.
+
+    .. container:: verbose
+
+      -A/--after can be used to remove only files that have already
+      been deleted, -f/--force can be used to force deletion, and -Af
+      can be used to remove files from the next revision without
+      deleting them from the working directory.
+
+      The following table details the behavior of remove for different
+      file states (columns) and option combinations (rows). The file
+      states are Added [A], Clean [C], Modified [M] and Missing [!]
+      (as reported by :hg:`status`). The actions are Warn, Remove
+      (from branch) and Delete (from disk):
+
+      ======= == == == ==
+              A  C  M  !
+      ======= == == == ==
+      none    W  RD W  R
+      -f      R  RD RD R
+      -A      W  W  W  R
+      -Af     R  R  R  R
+      ======= == == == ==
+
+      Note that remove never deletes files in Added [A] state from the
+      working directory, not even if option --force is specified.
 
     Returns 0 on success, 1 if any warnings encountered.
     """
@@ -3994,8 +4409,8 @@
                       ' to force removal)\n') % m.rel(f))
             ret = 1
         for f in added:
-            ui.warn(_('not removing %s: file has been marked for add (use -f'
-                      ' to force removal)\n') % m.rel(f))
+            ui.warn(_('not removing %s: file has been marked for add'
+                      ' (use forget to undo)\n') % m.rel(f))
             ret = 1
 
     for f in sorted(list):
@@ -4051,9 +4466,8 @@
     ('l', 'list', None, _('list state of files needing merge')),
     ('m', 'mark', None, _('mark files as resolved')),
     ('u', 'unmark', None, _('mark files as unresolved')),
-    ('t', 'tool', '', _('specify merge tool')),
     ('n', 'no-status', None, _('hide status prefix'))]
-    + walkopts,
+    + mergetoolopts + walkopts,
     _('[OPTION]... [FILE]...'))
 def resolve(ui, repo, *pats, **opts):
     """redo merges or set/view the merge status of files
@@ -4072,7 +4486,8 @@
       performed for files already marked as resolved. Use ``--all/-a``
       to select all unresolved files. ``--tool`` can be used to specify
       the merge tool used for the given files. It overrides the HGMERGE
-      environment variable and your configuration files.
+      environment variable and your configuration files.  Previous file
+      contents are saved with a ``.orig`` suffix.
 
     - :hg:`resolve -m [FILE]`: mark a file as having been resolved
       (e.g. after having manually fixed-up the files). The default is
@@ -4145,7 +4560,7 @@
     [('a', 'all', None, _('revert all changes when no arguments given')),
     ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
     ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
-    ('', 'no-backup', None, _('do not save backup copies of files')),
+    ('C', 'no-backup', None, _('do not save backup copies of files')),
     ] + walkopts + dryrunopts,
     _('[OPTION]... [-r REV] [NAME]...'))
 def revert(ui, repo, *pats, **opts):
@@ -4237,6 +4652,10 @@
         def badfn(path, msg):
             if path in names:
                 return
+            if path in repo[node].substate:
+                ui.warn("%s: %s\n" % (m.rel(path),
+                    'reverting subrepos is unsupported'))
+                return
             path_ = path + '/'
             for f in names:
                 if f.startswith(path_):
@@ -4381,7 +4800,8 @@
     finally:
         wlock.release()
 
-@command('rollback', dryrunopts)
+@command('rollback', dryrunopts +
+         [('f', 'force', False, _('ignore safety measures'))])
 def rollback(ui, repo, **opts):
     """roll back the last transaction (dangerous)
 
@@ -4402,6 +4822,12 @@
     - push (with this repository as the destination)
     - unbundle
 
+    It's possible to lose data with rollback: commit, update back to
+    an older changeset, and then rollback. The update removes the
+    changes you committed from the working directory, and rollback
+    removes them from history. To avoid data loss, you must pass
+    --force in this case.
+
     This command is not intended for use on public repositories. Once
     changes are visible for pull by other users, rolling a transaction
     back locally is ineffective (someone else may already have pulled
@@ -4411,7 +4837,8 @@
 
     Returns 0 on success, 1 if no rollback data is available.
     """
-    return repo.rollback(opts.get('dry_run'))
+    return repo.rollback(dryrun=opts.get('dry_run'),
+                         force=opts.get('force'))
 
 @command('root', [])
 def root(ui, repo):
@@ -4653,6 +5080,22 @@
       I = ignored
         = origin of the previous file listed as A (added)
 
+    .. container:: verbose
+
+      Examples:
+
+      - show changes in the working directory relative to a changeset:
+
+          hg status --rev 9353
+
+      - show all changes including copies in an existing changeset::
+
+          hg status --copies --change 9353
+
+      - get a NUL separated list of added files, suitable for xargs::
+
+          hg status -an0
+
     Returns 0 on success.
     """
 
@@ -4727,6 +5170,7 @@
     ctx = repo[None]
     parents = ctx.parents()
     pnode = parents[0].node()
+    marks = []
 
     for p in parents:
         # label with log.changeset (instead of log.parent) since this
@@ -4735,7 +5179,7 @@
                  label='log.changeset')
         ui.write(' '.join(p.tags()), label='log.tag')
         if p.bookmarks():
-            ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
+            marks.extend(p.bookmarks())
         if p.rev() == -1:
             if not len(repo):
                 ui.write(_(' (empty repository)'))
@@ -4754,6 +5198,20 @@
     else:
         ui.status(m, label='log.branch')
 
+    if marks:
+        current = repo._bookmarkcurrent
+        ui.write(_('bookmarks:'), label='log.bookmark')
+        if current is not None:
+            try:
+                marks.remove(current)
+                ui.write(' *' + current, label='bookmarks.current')
+            except ValueError:
+                # current bookmark not in parent ctx marks
+                pass
+        for m in marks:
+          ui.write(' ' + m, label='log.bookmark')
+        ui.write('\n', label='log.bookmark')
+
     st = list(repo.status(unknown=True))[:6]
 
     c = repo.dirstate.copies()
@@ -4988,19 +5446,22 @@
 
     for t, n in reversed(repo.tagslist()):
         if ui.quiet:
-            ui.write("%s\n" % t)
+            ui.write("%s\n" % t, label='tags.normal')
             continue
 
         hn = hexfunc(n)
         r = "%5d:%s" % (repo.changelog.rev(n), hn)
+        rev = ui.label(r, 'log.changeset')
         spaces = " " * (30 - encoding.colwidth(t))
 
+        tag = ui.label(t, 'tags.normal')
         if ui.verbose:
             if repo.tagtype(t) == 'local':
                 tagtype = " local"
+                tag = ui.label(t, 'tags.local')
             else:
                 tagtype = ""
-        ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
+        ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
 
 @command('tip',
     [('p', 'patch', None, _('show patch')),
--- a/mercurial/commandserver.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/commandserver.py	Sat Oct 15 14:30:50 2011 -0500
@@ -185,6 +185,7 @@
         copiedui = self.ui.copy()
         self.repo.baseui = copiedui
         self.repo.ui = self.repo.dirstate._ui = self.repoui.copy()
+        self.repo.invalidate()
 
         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
                                self.cout, self.cerr)
--- a/mercurial/demandimport.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/demandimport.py	Sat Oct 15 14:30:50 2011 -0500
@@ -27,6 +27,17 @@
 import __builtin__
 _origimport = __import__
 
+nothing = object()
+
+try:
+    _origimport(__builtin__.__name__, {}, {}, None, -1)
+except TypeError: # no level argument
+    def _import(name, globals, locals, fromlist, level):
+        "call _origimport with no level argument"
+        return _origimport(name, globals, locals, fromlist)
+else:
+    _import = _origimport
+
 class _demandmod(object):
     """module demand-loader and proxy"""
     def __init__(self, name, globals, locals):
@@ -50,7 +61,7 @@
                 h, t = p, None
                 if '.' in p:
                     h, t = p.split('.', 1)
-                if not hasattr(mod, h):
+                if getattr(mod, h, nothing) is nothing:
                     setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
                 elif t:
                     subload(getattr(mod, h), t)
@@ -81,20 +92,14 @@
 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
     if not locals or name in ignore or fromlist == ('*',):
         # these cases we can't really delay
-        if level == -1:
-            return _origimport(name, globals, locals, fromlist)
-        else:
-            return _origimport(name, globals, locals, fromlist, level)
+        return _import(name, globals, locals, fromlist, level)
     elif not fromlist:
         # import a [as b]
         if '.' in name: # a.b
             base, rest = name.split('.', 1)
             # email.__init__ loading email.mime
             if globals and globals.get('__name__', None) == base:
-                if level != -1:
-                    return _origimport(name, globals, locals, fromlist, level)
-                else:
-                    return _origimport(name, globals, locals, fromlist)
+                return _import(name, globals, locals, fromlist, level)
             # if a is already demand-loaded, add b to its submodule list
             if base in locals:
                 if isinstance(locals[base], _demandmod):
@@ -109,12 +114,12 @@
         mod = _origimport(name, globals, locals)
         # recurse down the module chain
         for comp in name.split('.')[1:]:
-            if not hasattr(mod, comp):
+            if getattr(mod, comp, nothing) is nothing:
                 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
             mod = getattr(mod, comp)
         for x in fromlist:
             # set requested submodules for demand load
-            if not hasattr(mod, x):
+            if getattr(mod, x, nothing) is nothing:
                 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
         return mod
 
@@ -137,6 +142,8 @@
     # raise ImportError if x not defined
     '__main__',
     '_ssl', # conditional imports in the stdlib, issue1964
+    'rfc822',
+    'mimetools',
     ]
 
 def enable():
@@ -146,4 +153,3 @@
 def disable():
     "disable global demand-loading of modules"
     __builtin__.__import__ = _origimport
-
--- a/mercurial/dirstate.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/dirstate.py	Sat Oct 15 14:30:50 2011 -0500
@@ -453,7 +453,7 @@
             write(e)
             write(f)
         st.write(cs.getvalue())
-        st.rename()
+        st.close()
         self._lastnormaltime = None
         self._dirty = self._dirtypl = False
 
--- a/mercurial/dispatch.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/dispatch.py	Sat Oct 15 14:30:50 2011 -0500
@@ -123,6 +123,9 @@
         else:
             ui.warn(_("hg: %s\n") % inst.args[1])
             commands.help_(ui, 'shortlist')
+    except error.OutOfBandError, inst:
+        ui.warn("abort: remote error:\n")
+        ui.warn(''.join(inst.args))
     except error.RepoError, inst:
         ui.warn(_("abort: %s!\n") % inst)
         if inst.hint:
@@ -159,16 +162,16 @@
         elif m in "zlib".split():
             ui.warn(_("(is your Python install correct?)\n"))
     except IOError, inst:
-        if hasattr(inst, "code"):
+        if util.safehasattr(inst, "code"):
             ui.warn(_("abort: %s\n") % inst)
-        elif hasattr(inst, "reason"):
+        elif util.safehasattr(inst, "reason"):
             try: # usually it is in the form (errno, strerror)
                 reason = inst.reason.args[1]
             except (AttributeError, IndexError):
                  # it might be anything, for example a string
                 reason = inst.reason
             ui.warn(_("abort: error: %s\n") % reason)
-        elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
+        elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
             if ui.debugflag:
                 ui.warn(_("broken pipe\n"))
         elif getattr(inst, "strerror", None):
@@ -338,7 +341,7 @@
             ui.debug("alias '%s' shadows command '%s'\n" %
                      (self.name, self.cmdname))
 
-        if hasattr(self, 'shell'):
+        if util.safehasattr(self, 'shell'):
             return self.fn(ui, *args, **opts)
         else:
             try:
@@ -363,7 +366,7 @@
             # definition might not exist or it might not be a cmdalias
             pass
 
-        cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
+        cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
         if aliasdef.norepo:
             commands.norepo += ' %s' % alias
 
@@ -483,15 +486,14 @@
         lui = ui.copy()
         lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
 
-    if rpath:
+    if rpath and rpath[-1]:
         path = lui.expandpath(rpath[-1])
         lui = ui.copy()
         lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
 
     return path, lui
 
-def _checkshellalias(ui, args):
-    cwd = os.getcwd()
+def _checkshellalias(lui, ui, args):
     norepo = commands.norepo
     options = {}
 
@@ -503,12 +505,6 @@
     if not args:
         return
 
-    _parseconfig(ui, options['config'])
-    if options['cwd']:
-        os.chdir(options['cwd'])
-
-    path, lui = _getlocal(ui, [options['repository']])
-
     cmdtable = commands.table.copy()
     addaliases(lui, cmdtable)
 
@@ -517,28 +513,22 @@
         aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
     except (error.AmbiguousCommand, error.UnknownCommand):
         commands.norepo = norepo
-        os.chdir(cwd)
         return
 
     cmd = aliases[0]
     fn = entry[0]
 
-    if cmd and hasattr(fn, 'shell'):
+    if cmd and util.safehasattr(fn, 'shell'):
         d = lambda: fn(ui, *args[1:])
         return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
 
     commands.norepo = norepo
-    os.chdir(cwd)
 
 _loaded = set()
 def _dispatch(req):
     args = req.args
     ui = req.ui
 
-    shellaliasfn = _checkshellalias(ui, args)
-    if shellaliasfn:
-        return shellaliasfn()
-
     # read --config before doing anything else
     # (e.g. to change trust settings for reading .hg/hgrc)
     cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
@@ -551,6 +541,12 @@
     rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
     path, lui = _getlocal(ui, rpath)
 
+    # Now that we're operating in the right directory/repository with
+    # the right config settings, check for shell aliases
+    shellaliasfn = _checkshellalias(lui, ui, args)
+    if shellaliasfn:
+        return shellaliasfn()
+
     # Configure extensions in phases: uisetup, extsetup, cmdtable, and
     # reposetup. Programs like TortoiseHg will call _dispatch several
     # times so we keep track of configured extensions in _loaded.
@@ -635,10 +631,10 @@
         for ui_ in uis:
             ui_.setconfig('web', 'cacerts', '')
 
+    if options['version']:
+        return commands.version_(ui)
     if options['help']:
-        return commands.help_(ui, cmd, options['version'])
-    elif options['version']:
-        return commands.version_(ui)
+        return commands.help_(ui, cmd)
     elif not cmd:
         return commands.help_(ui, 'shortlist')
 
--- a/mercurial/encoding.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/encoding.py	Sat Oct 15 14:30:50 2011 -0500
@@ -139,7 +139,7 @@
         and "WFA" or "WF")
 
 def colwidth(s):
-    "Find the column width of a UTF-8 string for display"
+    "Find the column width of a string for display in the local encoding"
     return ucolwidth(s.decode(encoding, 'replace'))
 
 def ucolwidth(d):
@@ -149,6 +149,14 @@
         return sum([eaw(c) in wide and 2 or 1 for c in d])
     return len(d)
 
+def getcols(s, start, c):
+    '''Use colwidth to find a c-column substring of s starting at byte
+    index start'''
+    for x in xrange(start + c, len(s)):
+        t = s[start:x]
+        if colwidth(t) == c:
+            return t
+
 def lower(s):
     "best-effort encoding-aware case-folding of local string s"
     try:
--- a/mercurial/error.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/error.py	Sat Oct 15 14:30:50 2011 -0500
@@ -39,6 +39,9 @@
 class ConfigError(Abort):
     'Exception raised when parsing config files'
 
+class OutOfBandError(Exception):
+    'Exception raised when a remote repo reports failure'
+
 class ParseError(Exception):
     'Exception raised when parsing config files (msg[, pos])'
 
--- a/mercurial/extensions.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/extensions.py	Sat Oct 15 14:30:50 2011 -0500
@@ -69,7 +69,9 @@
             return mod
         try:
             mod = importh("hgext.%s" % name)
-        except ImportError:
+        except ImportError, err:
+            ui.debug('could not import hgext.%s (%s): trying %s\n'
+                     % (name, err, name))
             mod = importh(name)
     _extensions[shortname] = mod
     _order.append(shortname)
@@ -124,7 +126,7 @@
     where orig is the original (wrapped) function, and *args, **kwargs
     are the arguments passed to it.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert util.safehasattr(wrapper, '__call__')
     aliases, entry = cmdutil.findcmd(command, table)
     for alias, e in table.iteritems():
         if e is entry:
@@ -177,12 +179,12 @@
     your end users, you should play nicely with others by using the
     subclass trick.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert util.safehasattr(wrapper, '__call__')
     def wrap(*args, **kwargs):
         return wrapper(origfn, *args, **kwargs)
 
     origfn = getattr(container, funcname)
-    assert hasattr(origfn, '__call__')
+    assert util.safehasattr(origfn, '__call__')
     setattr(container, funcname, wrap)
     return origfn
 
--- a/mercurial/fancyopts.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/fancyopts.py	Sat Oct 15 14:30:50 2011 -0500
@@ -75,7 +75,7 @@
         # copy defaults to state
         if isinstance(default, list):
             state[name] = default[:]
-        elif hasattr(default, '__call__'):
+        elif getattr(default, '__call__', False):
             state[name] = None
         else:
             state[name] = default
--- a/mercurial/filemerge.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/filemerge.py	Sat Oct 15 14:30:50 2011 -0500
@@ -34,7 +34,8 @@
             p = util.findexe(p + _toolstr(ui, tool, "regappend"))
             if p:
                 return p
-    return util.findexe(_toolstr(ui, tool, "executable", tool))
+    exe = _toolstr(ui, tool, "executable", tool)
+    return util.findexe(util.expandpath(exe))
 
 def _picktool(repo, ui, path, binary, symlink):
     def check(tool, pat, symlink, binary):
--- a/mercurial/hbisect.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hbisect.py	Sat Oct 15 14:30:50 2011 -0500
@@ -8,7 +8,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os
+import os, error
 from i18n import _
 from node import short, hex
 import util
@@ -35,17 +35,18 @@
         # build visit array
         ancestors = [None] * (len(changelog) + 1) # an extra for [-1]
 
-        # set nodes descended from goodrev
-        ancestors[goodrev] = []
+        # set nodes descended from goodrevs
+        for rev in goodrevs:
+            ancestors[rev] = []
         for rev in xrange(goodrev + 1, len(changelog)):
             for prev in clparents(rev):
                 if ancestors[prev] == []:
                     ancestors[rev] = []
 
         # clear good revs from array
-        for node in goodrevs:
-            ancestors[node] = None
-        for rev in xrange(len(changelog), -1, -1):
+        for rev in goodrevs:
+            ancestors[rev] = None
+        for rev in xrange(len(changelog), goodrev, -1):
             if ancestors[rev] is None:
                 for prev in clparents(rev):
                     ancestors[prev] = None
@@ -149,7 +150,102 @@
         for kind in state:
             for node in state[kind]:
                 f.write("%s %s\n" % (kind, hex(node)))
-        f.rename()
+        f.close()
     finally:
         wlock.release()
 
+def get(repo, status):
+    """
+    Return a list of revision(s) that match the given status:
+
+    - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
+    - ``goods``, ``bads``      : csets topologicaly good/bad
+    - ``range``              : csets taking part in the bisection
+    - ``pruned``             : csets that are goods, bads or skipped
+    - ``untested``           : csets whose fate is yet unknown
+    - ``ignored``            : csets ignored due to DAG topology
+    """
+    state = load_state(repo)
+    if status in ('good', 'bad', 'skip'):
+        return [repo.changelog.rev(n) for n in state[status]]
+    else:
+        # In the floowing sets, we do *not* call 'bisect()' with more
+        # than one level of recusrsion, because that can be very, very
+        # time consuming. Instead, we always develop the expression as
+        # much as possible.
+
+        # 'range' is all csets that make the bisection:
+        #   - have a good ancestor and a bad descendant, or conversely
+        # that's because the bisection can go either way
+        range = '( bisect(bad)::bisect(good) | bisect(good)::bisect(bad) )'
+
+        _t = [c.rev() for c in repo.set('bisect(good)::bisect(bad)')]
+        # The sets of topologically good or bad csets
+        if len(_t) == 0:
+            # Goods are topologically after bads
+            goods = 'bisect(good)::'    # Pruned good csets
+            bads  = '::bisect(bad)'     # Pruned bad csets
+        else:
+            # Goods are topologically before bads
+            goods = '::bisect(good)'    # Pruned good csets
+            bads  = 'bisect(bad)::'     # Pruned bad csets
+
+        # 'pruned' is all csets whose fate is already known: good, bad, skip
+        skips = 'bisect(skip)'                 # Pruned skipped csets
+        pruned = '( (%s) | (%s) | (%s) )' % (goods, bads, skips)
+
+        # 'untested' is all cset that are- in 'range', but not in 'pruned'
+        untested = '( (%s) - (%s) )' % (range, pruned)
+
+        # 'ignored' is all csets that were not used during the bisection
+        # due to DAG topology, but may however have had an impact.
+        # Eg., a branch merged between bads and goods, but whose branch-
+        # point is out-side of the range.
+        iba = '::bisect(bad) - ::bisect(good)'  # Ignored bads' ancestors
+        iga = '::bisect(good) - ::bisect(bad)'  # Ignored goods' ancestors
+        ignored = '( ( (%s) | (%s) ) - (%s) )' % (iba, iga, range)
+
+        if status == 'range':
+            return [c.rev() for c in repo.set(range)]
+        elif status == 'pruned':
+            return [c.rev() for c in repo.set(pruned)]
+        elif status == 'untested':
+            return [c.rev() for c in repo.set(untested)]
+        elif status == 'ignored':
+            return [c.rev() for c in repo.set(ignored)]
+        elif status == "goods":
+            return [c.rev() for c in repo.set(goods)]
+        elif status == "bads":
+            return [c.rev() for c in repo.set(bads)]
+
+        else:
+            raise error.ParseError(_('invalid bisect state'))
+
+def label(repo, node, short=False):
+    rev = repo.changelog.rev(node)
+
+    # Try explicit sets
+    if rev in get(repo, 'good'):
+        return _('good')
+    if rev in get(repo, 'bad'):
+        return _('bad')
+    if rev in get(repo, 'skip'):
+        return _('skipped')
+    if rev in get(repo, 'untested'):
+        return _('untested')
+    if rev in get(repo, 'ignored'):
+        return _('ignored')
+
+    # Try implicit sets
+    if rev in get(repo, 'goods'):
+        return _('good (implicit)')
+    if rev in get(repo, 'bads'):
+        return _('bad (implicit)')
+
+    return None
+
+def shortlabel(label):
+    if label:
+        return label[0].upper()
+
+    return None
--- a/mercurial/help.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/help.py	Sat Oct 15 14:30:50 2011 -0500
@@ -31,7 +31,7 @@
     """Return a delayed loader for help/topic.txt."""
 
     def loader():
-        if hasattr(sys, 'frozen'):
+        if util.mainfrozen():
             module = sys.executable
         else:
             module = __file__
--- a/mercurial/help/config.txt	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/help/config.txt	Sat Oct 15 14:30:50 2011 -0500
@@ -223,6 +223,10 @@
 ``$HG_ARGS`` expand to the arguments given to Mercurial. In the ``hg
 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
 
+.. note:: Some global configuration options such as ``-R`` are
+   processed before shell aliases and will thus not be passed to
+   aliases.
+
 ``auth``
 """"""""
 
@@ -1261,6 +1265,12 @@
 ``ipv6``
     Whether to use IPv6. Default is False.
 
+``logoimg``
+    File name of the logo image that some templates display on each page.
+    The file name is relative to ``staticurl``. That is, the full path to
+    the logo image is "staticurl/logoimg".
+    If unset, ``hglogo.png`` will be used.
+
 ``logourl``
     Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
     will be used.
--- a/mercurial/help/subrepos.txt	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/help/subrepos.txt	Sat Oct 15 14:30:50 2011 -0500
@@ -1,13 +1,14 @@
 Subrepositories let you nest external repositories or projects into a
 parent Mercurial repository, and make commands operate on them as a
-group. External Mercurial and Subversion projects are currently
-supported.
+group.
+
+Mercurial currently supports Mercurial, Git, and Subversion
+subrepositories.
 
 Subrepositories are made of three components:
 
 1. Nested repository checkouts. They can appear anywhere in the
-   parent working directory, and are Mercurial clones or Subversion
-   checkouts.
+   parent working directory.
 
 2. Nested repository references. They are defined in ``.hgsub`` and
    tell where the subrepository checkouts come from. Mercurial
@@ -15,12 +16,15 @@
 
      path/to/nested = https://example.com/nested/repo/path
 
+   Git and Subversion subrepos are also supported:
+
+     path/to/nested = [git]git://example.com/nested/repo/path
+     path/to/nested = [svn]https://example.com/nested/trunk/path
+
    where ``path/to/nested`` is the checkout location relatively to the
    parent Mercurial root, and ``https://example.com/nested/repo/path``
    is the source repository path. The source can also reference a
-   filesystem path. Subversion repositories are defined with:
-
-     path/to/nested = [svn]https://example.com/nested/trunk/path
+   filesystem path.
 
    Note that ``.hgsub`` does not exist by default in Mercurial
    repositories, you have to create and add it to the parent
--- a/mercurial/hg.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hg.py	Sat Oct 15 14:30:50 2011 -0500
@@ -98,9 +98,9 @@
             hook(ui, repo)
     return repo
 
-def peer(ui, opts, path, create=False):
+def peer(uiorrepo, opts, path, create=False):
     '''return a repository peer for the specified path'''
-    rui = remoteui(ui, opts)
+    rui = remoteui(uiorrepo, opts)
     return repository(rui, path, create)
 
 def defaultdest(source):
@@ -174,6 +174,36 @@
                 continue
         _update(r, uprev)
 
+def copystore(ui, srcrepo, destpath):
+    '''copy files from store of srcrepo in destpath
+
+    returns destlock
+    '''
+    destlock = None
+    try:
+        hardlink = None
+        num = 0
+        for f in srcrepo.store.copylist():
+            src = os.path.join(srcrepo.sharedpath, f)
+            dst = os.path.join(destpath, f)
+            dstbase = os.path.dirname(dst)
+            if dstbase and not os.path.exists(dstbase):
+                os.mkdir(dstbase)
+            if os.path.exists(src):
+                if dst.endswith('data'):
+                    # lock to avoid premature writing to the target
+                    destlock = lock.lock(os.path.join(dstbase, "lock"))
+                hardlink, n = util.copyfiles(src, dst, hardlink)
+                num += n
+        if hardlink:
+            ui.debug("linked %d files\n" % num)
+        else:
+            ui.debug("copied %d files\n" % num)
+        return destlock
+    except:
+        release(destlock)
+        raise
+
 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
           update=True, stream=False, branch=None):
     """Make a copy of an existing repository.
@@ -287,24 +317,7 @@
                                      % dest)
                 raise
 
-            hardlink = None
-            num = 0
-            for f in srcrepo.store.copylist():
-                src = os.path.join(srcrepo.sharedpath, f)
-                dst = os.path.join(destpath, f)
-                dstbase = os.path.dirname(dst)
-                if dstbase and not os.path.exists(dstbase):
-                    os.mkdir(dstbase)
-                if os.path.exists(src):
-                    if dst.endswith('data'):
-                        # lock to avoid premature writing to the target
-                        destlock = lock.lock(os.path.join(dstbase, "lock"))
-                    hardlink, n = util.copyfiles(src, dst, hardlink)
-                    num += n
-            if hardlink:
-                ui.debug("linked %d files\n" % num)
-            else:
-                ui.debug("copied %d files\n" % num)
+            destlock = copystore(ui, srcrepo, destpath)
 
             # we need to re-init the repo after manually copying the data
             # into it
@@ -537,7 +550,7 @@
 
 def remoteui(src, opts):
     'build a remote ui from ui or repo and opts'
-    if hasattr(src, 'baseui'): # looks like a repository
+    if util.safehasattr(src, 'baseui'): # looks like a repository
         dst = src.baseui.copy() # drop repo-specific config
         src = src.ui # copy target options from repo
     else: # assume it's a global ui object
--- a/mercurial/hgweb/hgweb_mod.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/hgweb_mod.py	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-from mercurial import ui, hg, hook, error, encoding, templater
+from mercurial import ui, hg, hook, error, encoding, templater, util
 from common import get_stat, ErrorResponse, permhooks, caching
 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
@@ -148,7 +148,7 @@
                 cmd = cmd[style + 1:]
 
             # avoid accepting e.g. style parameter as command
-            if hasattr(webcommands, cmd):
+            if util.safehasattr(webcommands, cmd):
                 req.form['cmd'] = [cmd]
             else:
                 cmd = ''
@@ -236,6 +236,7 @@
         port = port != default_port and (":" + port) or ""
         urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
         logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
+        logoimg = self.config("web", "logoimg", "hglogo.png")
         staticurl = self.config("web", "staticurl") or req.url + 'static/'
         if not staticurl.endswith('/'):
             staticurl += '/'
@@ -276,6 +277,7 @@
         tmpl = templater.templater(mapfile,
                                    defaults={"url": req.url,
                                              "logourl": logourl,
+                                             "logoimg": logoimg,
                                              "staticurl": staticurl,
                                              "urlbase": urlbase,
                                              "repo": self.reponame,
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sat Oct 15 14:30:50 2011 -0500
@@ -51,6 +51,33 @@
         yield (prefix + '/' +
                util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
 
+def geturlcgivars(baseurl, port):
+    """
+    Extract CGI variables from baseurl
+
+    >>> geturlcgivars("http://host.org/base", "80")
+    ('host.org', '80', '/base')
+    >>> geturlcgivars("http://host.org:8000/base", "80")
+    ('host.org', '8000', '/base')
+    >>> geturlcgivars('/base', 8000)
+    ('', '8000', '/base')
+    >>> geturlcgivars("base", '8000')
+    ('', '8000', '/base')
+    >>> geturlcgivars("http://host", '8000')
+    ('host', '8000', '/')
+    >>> geturlcgivars("http://host/", '8000')
+    ('host', '8000', '/')
+    """
+    u = util.url(baseurl)
+    name = u.host or ''
+    if u.port:
+        port = u.port
+    path = u.path or ""
+    if not path.startswith('/'):
+        path = '/' + path
+
+    return name, str(port), path
+
 class hgwebdir(object):
     refreshinterval = 20
 
@@ -348,6 +375,7 @@
         start = url[-1] == '?' and '&' or '?'
         sessionvars = webutil.sessionvars(vars, start)
         logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
+        logoimg = config('web', 'logoimg', 'hglogo.png')
         staticurl = config('web', 'staticurl') or url + 'static/'
         if not staticurl.endswith('/'):
             staticurl += '/'
@@ -358,17 +386,14 @@
                                              "motd": motd,
                                              "url": url,
                                              "logourl": logourl,
+                                             "logoimg": logoimg,
                                              "staticurl": staticurl,
                                              "sessionvars": sessionvars})
         return tmpl
 
     def updatereqenv(self, env):
         if self._baseurl is not None:
-            u = util.url(self._baseurl)
-            env['SERVER_NAME'] = u.host
-            if u.port:
-                env['SERVER_PORT'] = u.port
-            path = u.path or ""
-            if not path.startswith('/'):
-                path = '/' + path
+            name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
+            env['SERVER_NAME'] = name
+            env['SERVER_PORT'] = port
             env['SCRIPT_NAME'] = path
--- a/mercurial/hgweb/protocol.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/protocol.py	Sat Oct 15 14:30:50 2011 -0500
@@ -10,6 +10,7 @@
 from common import HTTP_OK
 
 HGTYPE = 'application/mercurial-0.1'
+HGERRTYPE = 'application/hg-error'
 
 class webproto(object):
     def __init__(self, req, ui):
@@ -90,3 +91,7 @@
         rsp = '0\n%s\n' % rsp.res
         req.respond(HTTP_OK, HGTYPE, length=len(rsp))
         return [rsp]
+    elif isinstance(rsp, wireproto.ooberror):
+        rsp = rsp.message
+        req.respond(HTTP_OK, HGERRTYPE, length=len(rsp))
+        return [rsp]
--- a/mercurial/hgweb/request.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/request.py	Sat Oct 15 14:30:50 2011 -0500
@@ -101,7 +101,7 @@
             self.headers = []
 
     def write(self, thing):
-        if hasattr(thing, "__iter__"):
+        if util.safehasattr(thing, "__iter__"):
             for part in thing:
                 self.write(part)
         else:
--- a/mercurial/hgweb/server.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/server.py	Sat Oct 15 14:30:50 2011 -0500
@@ -246,9 +246,10 @@
 
 try:
     from threading import activeCount
+    activeCount() # silence pyflakes
     _mixin = SocketServer.ThreadingMixIn
 except ImportError:
-    if hasattr(os, "fork"):
+    if util.safehasattr(os, "fork"):
         _mixin = SocketServer.ForkingMixIn
     else:
         class _mixin(object):
--- a/mercurial/hgweb/webutil.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/webutil.py	Sat Oct 15 14:30:50 2011 -0500
@@ -72,7 +72,7 @@
         d['date'] = s.date()
         d['description'] = s.description()
         d['branch'] = s.branch()
-        if hasattr(s, 'path'):
+        if util.safehasattr(s, 'path'):
             d['file'] = s.path()
         yield d
 
--- a/mercurial/hgweb/wsgicgi.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hgweb/wsgicgi.py	Sat Oct 15 14:30:50 2011 -0500
@@ -78,5 +78,4 @@
         for chunk in content:
             write(chunk)
     finally:
-        if hasattr(content, 'close'):
-            content.close()
+        getattr(content, 'close', lambda : None)()
--- a/mercurial/hook.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/hook.py	Sat Oct 15 14:30:50 2011 -0500
@@ -21,14 +21,14 @@
 
     ui.note(_("calling hook %s: %s\n") % (hname, funcname))
     obj = funcname
-    if not hasattr(obj, '__call__'):
+    if not util.safehasattr(obj, '__call__'):
         d = funcname.rfind('.')
         if d == -1:
             raise util.Abort(_('%s hook is invalid ("%s" not in '
                                'a module)') % (hname, funcname))
         modname = funcname[:d]
         oldpaths = sys.path
-        if hasattr(sys, "frozen"):
+        if util.mainfrozen():
             # binary installs require sys.path manipulation
             modpath, modfile = os.path.split(modname)
             if modpath and modfile:
@@ -60,7 +60,7 @@
             raise util.Abort(_('%s hook is invalid '
                                '("%s" is not defined)') %
                              (hname, funcname))
-        if not hasattr(obj, '__call__'):
+        if not util.safehasattr(obj, '__call__'):
             raise util.Abort(_('%s hook is invalid '
                                '("%s" is not callable)') %
                              (hname, funcname))
@@ -99,7 +99,7 @@
 
     env = {}
     for k, v in args.iteritems():
-        if hasattr(v, '__call__'):
+        if util.safehasattr(v, '__call__'):
             v = v()
         if isinstance(v, dict):
             # make the dictionary element order stable across Python
@@ -149,7 +149,7 @@
         for hname, cmd in ui.configitems('hooks'):
             if hname.split('.')[0] != name or not cmd:
                 continue
-            if hasattr(cmd, '__call__'):
+            if util.safehasattr(cmd, '__call__'):
                 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
             elif cmd.startswith('python:'):
                 if cmd.count(':') >= 2:
--- a/mercurial/httpclient/__init__.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/httpclient/__init__.py	Sat Oct 15 14:30:50 2011 -0500
@@ -171,6 +171,14 @@
             logger.info('cl: %r body: %r', self._content_len, self._body)
         try:
             data = self.sock.recv(INCOMING_BUFFER_SIZE)
+            # If the socket was readable and no data was read, that
+            # means the socket was closed. If this isn't a
+            # _CLOSE_IS_END socket, then something is wrong if we're
+            # here (we shouldn't enter _select() if the response is
+            # complete), so abort.
+            if not data and self._content_len != _LEN_CLOSE_IS_END:
+                raise HTTPRemoteClosedError(
+                    'server appears to have closed the socket mid-response')
         except socket.sslerror, e:
             if e.args[0] != socket.SSL_ERROR_WANT_READ:
                 raise
@@ -693,6 +701,11 @@
 class HTTPProxyConnectFailedException(httplib.HTTPException):
     """Connecting to the HTTP proxy failed."""
 
+
 class HTTPStateError(httplib.HTTPException):
     """Invalid internal state encountered."""
+
+
+class HTTPRemoteClosedError(httplib.HTTPException):
+    """The server closed the remote socket in the middle of a response."""
 # no-check-code
--- a/mercurial/httpclient/tests/simple_http_test.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/httpclient/tests/simple_http_test.py	Sat Oct 15 14:30:50 2011 -0500
@@ -380,6 +380,21 @@
         con.request('GET', '/')
         self.assertEqual(2, len(sockets))
 
+    def test_server_closes_before_end_of_body(self):
+        con = http.HTTPConnection('1.2.3.4:80')
+        con._connect()
+        s = con.sock
+        s.data = ['HTTP/1.1 200 OK\r\n',
+                  'Server: BogusServer 1.0\r\n',
+                  'Connection: Keep-Alive\r\n',
+                  'Content-Length: 16',
+                  '\r\n\r\n',
+                  'You can '] # Note: this is shorter than content-length
+        s.close_on_empty = True
+        con.request('GET', '/')
+        r1 = con.getresponse()
+        self.assertRaises(http.HTTPRemoteClosedError, r1.read)
+
     def test_no_response_raises_response_not_ready(self):
         con = http.HTTPConnection('foo')
         self.assertRaises(http.httplib.ResponseNotReady, con.getresponse)
--- a/mercurial/httpclient/tests/test_chunked_transfer.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/httpclient/tests/test_chunked_transfer.py	Sat Oct 15 14:30:50 2011 -0500
@@ -134,4 +134,20 @@
         con.request('GET', '/')
         self.assertStringEqual('hi there\nthere\nthere\nthere\nthere\n',
                                con.getresponse().read())
+
+    def testChunkedDownloadEarlyHangup(self):
+        con = http.HTTPConnection('1.2.3.4:80')
+        con._connect()
+        sock = con.sock
+        broken = chunkedblock('hi'*20)[:-1]
+        sock.data = ['HTTP/1.1 200 OK\r\n',
+                     'Server: BogusServer 1.0\r\n',
+                     'transfer-encoding: chunked',
+                     '\r\n\r\n',
+                     broken,
+                     ]
+        sock.close_on_empty = True
+        con.request('GET', '/')
+        resp = con.getresponse()
+        self.assertRaises(http.HTTPRemoteClosedError, resp.read)
 # no-check-code
--- a/mercurial/httprepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/httprepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -28,6 +28,7 @@
         self.path = path
         self.caps = None
         self.handler = None
+        self.urlopener = None
         u = util.url(path)
         if u.query or u.fragment:
             raise util.Abort(_('unsupported URL component: "%s"') %
@@ -42,10 +43,10 @@
         self.urlopener = url.opener(ui, authinfo)
 
     def __del__(self):
-        for h in self.urlopener.handlers:
-            h.close()
-            if hasattr(h, "close_all"):
-                h.close_all()
+        if self.urlopener:
+            for h in self.urlopener.handlers:
+                h.close()
+                getattr(h, "close_all", lambda : None)()
 
     def url(self):
         return self.path
@@ -139,6 +140,8 @@
             proto = resp.headers.get('content-type', '')
 
         safeurl = util.hidepassword(self._url)
+        if proto.startswith('application/hg-error'):
+            raise error.OutOfBandError(resp.read())
         # accept old "text/plain" and "application/hg-changegroup" for now
         if not (proto.startswith('application/mercurial-') or
                 proto.startswith('text/plain') or
--- a/mercurial/i18n.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/i18n.py	Sat Oct 15 14:30:50 2011 -0500
@@ -9,7 +9,7 @@
 import gettext, sys, os
 
 # modelled after templater.templatepath:
-if hasattr(sys, 'frozen'):
+if getattr(sys, 'frozen', None) is not None:
     module = sys.executable
 else:
     module = __file__
@@ -61,4 +61,3 @@
     _ = lambda message: message
 else:
     _ = gettext
-
--- a/mercurial/keepalive.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/keepalive.py	Sat Oct 15 14:30:50 2011 -0500
@@ -547,13 +547,14 @@
         print "send:", repr(str)
     try:
         blocksize = 8192
-        if hasattr(str,'read') :
+        read = getattr(str, 'read', None)
+        if read is not None:
             if self.debuglevel > 0:
                 print "sendIng a read()able"
-            data = str.read(blocksize)
+            data = read(blocksize)
             while data:
                 self.sock.sendall(data)
-                data = str.read(blocksize)
+                data = read(blocksize)
         else:
             self.sock.sendall(str)
     except socket.error, v:
--- a/mercurial/localrepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/localrepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -10,13 +10,14 @@
 import repo, changegroup, subrepo, discovery, pushkey
 import changelog, dirstate, filelog, manifest, context, bookmarks
 import lock, transaction, store, encoding
-import scmutil, util, extensions, hook, error
+import scmutil, util, extensions, hook, error, revset
 import match as matchmod
 import merge as mergemod
 import tags as tagsmod
 from lock import release
 import weakref, errno, os, time, inspect
 propertycache = util.propertycache
+filecache = scmutil.filecache
 
 class localrepository(repo.repository):
     capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
@@ -63,6 +64,7 @@
                     )
                 if self.ui.configbool('format', 'generaldelta', False):
                     requirements.append("generaldelta")
+                requirements = set(requirements)
             else:
                 raise error.RepoError(_("repository %s not found") % path)
         elif create:
@@ -77,7 +79,7 @@
 
         self.sharedpath = self.path
         try:
-            s = os.path.realpath(self.opener.read("sharedpath"))
+            s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
             if not os.path.exists(s):
                 raise error.RepoError(
                     _('.hg/sharedpath points to nonexistent directory %s') % s)
@@ -95,21 +97,19 @@
         if create:
             self._writerequirements()
 
-        # These two define the set of tags for this repository.  _tags
-        # maps tag name to node; _tagtypes maps tag name to 'global' or
-        # 'local'.  (Global tags are defined by .hgtags across all
-        # heads, and local tags are defined in .hg/localtags.)  They
-        # constitute the in-memory cache of tags.
-        self._tags = None
-        self._tagtypes = None
 
         self._branchcache = None
         self._branchcachetip = None
-        self.nodetagscache = None
         self.filterpats = {}
         self._datafilters = {}
         self._transref = self._lockref = self._wlockref = None
 
+        # A cache for various files under .hg/ that tracks file changes,
+        # (used by the filecache decorator)
+        #
+        # Maps a property name to its util.filecacheentry
+        self._filecache = {}
+
     def _applyrequirements(self, requirements):
         self.requirements = requirements
         openerreqs = set(('revlogv1', 'generaldelta'))
@@ -159,15 +159,18 @@
                 parts.pop()
         return False
 
-    @util.propertycache
+    @filecache('bookmarks')
     def _bookmarks(self):
         return bookmarks.read(self)
 
-    @util.propertycache
+    @filecache('bookmarks.current')
     def _bookmarkcurrent(self):
         return bookmarks.readcurrent(self)
 
-    @propertycache
+    def _writebookmarks(self, marks):
+      bookmarks.write(self)
+
+    @filecache('00changelog.i', True)
     def changelog(self):
         c = changelog.changelog(self.sopener)
         if 'HG_PENDING' in os.environ:
@@ -176,11 +179,11 @@
                 c.readpending('00changelog.i.a')
         return c
 
-    @propertycache
+    @filecache('00manifest.i', True)
     def manifest(self):
         return manifest.manifest(self.sopener)
 
-    @propertycache
+    @filecache('dirstate')
     def dirstate(self):
         warned = [0]
         def validate(node):
@@ -217,6 +220,17 @@
         for i in xrange(len(self)):
             yield i
 
+    def set(self, expr, *args):
+        '''
+        Yield a context for each matching revision, after doing arg
+        replacement via revset.formatspec
+        '''
+
+        expr = revset.formatspec(expr, *args)
+        m = revset.match(None, expr)
+        for r in m(self, range(len(self))):
+            yield self[r]
+
     def url(self):
         return 'file:' + self.root
 
@@ -249,8 +263,8 @@
                 fp.write('\n')
             for name in names:
                 m = munge and munge(name) or name
-                if self._tagtypes and name in self._tagtypes:
-                    old = self._tags.get(name, nullid)
+                if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
+                    old = self.tags().get(name, nullid)
                     fp.write('%s %s\n' % (hex(old), m))
                 fp.write('%s %s\n' % (hex(node), m))
             fp.close()
@@ -325,12 +339,31 @@
         self.tags() # instantiate the cache
         self._tag(names, node, message, local, user, date)
 
+    @propertycache
+    def _tagscache(self):
+        '''Returns a tagscache object that contains various tags related caches.'''
+
+        # This simplifies its cache management by having one decorated
+        # function (this one) and the rest simply fetch things from it.
+        class tagscache(object):
+            def __init__(self):
+                # These two define the set of tags for this repository. tags
+                # maps tag name to node; tagtypes maps tag name to 'global' or
+                # 'local'. (Global tags are defined by .hgtags across all
+                # heads, and local tags are defined in .hg/localtags.)
+                # They constitute the in-memory cache of tags.
+                self.tags = self.tagtypes = None
+
+                self.nodetagscache = self.tagslist = None
+
+        cache = tagscache()
+        cache.tags, cache.tagtypes = self._findtags()
+
+        return cache
+
     def tags(self):
         '''return a mapping of tag to node'''
-        if self._tags is None:
-            (self._tags, self._tagtypes) = self._findtags()
-
-        return self._tags
+        return self._tagscache.tags
 
     def _findtags(self):
         '''Do the hard work of finding tags.  Return a pair of dicts
@@ -379,27 +412,29 @@
         None     : tag does not exist
         '''
 
-        self.tags()
-
-        return self._tagtypes.get(tagname)
+        return self._tagscache.tagtypes.get(tagname)
 
     def tagslist(self):
         '''return a list of tags ordered by revision'''
-        l = []
-        for t, n in self.tags().iteritems():
-            r = self.changelog.rev(n)
-            l.append((r, t, n))
-        return [(t, n) for r, t, n in sorted(l)]
+        if not self._tagscache.tagslist:
+            l = []
+            for t, n in self.tags().iteritems():
+                r = self.changelog.rev(n)
+                l.append((r, t, n))
+            self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
+
+        return self._tagscache.tagslist
 
     def nodetags(self, node):
         '''return the tags associated with a node'''
-        if not self.nodetagscache:
-            self.nodetagscache = {}
+        if not self._tagscache.nodetagscache:
+            nodetagscache = {}
             for t, n in self.tags().iteritems():
-                self.nodetagscache.setdefault(n, []).append(t)
-            for tags in self.nodetagscache.itervalues():
+                nodetagscache.setdefault(n, []).append(t)
+            for tags in nodetagscache.itervalues():
                 tags.sort()
-        return self.nodetagscache.get(node, [])
+            self._tagscache.nodetagscache = nodetagscache
+        return self._tagscache.nodetagscache.get(node, [])
 
     def nodebookmarks(self, node):
         marks = []
@@ -489,7 +524,7 @@
             for label, nodes in branches.iteritems():
                 for node in nodes:
                     f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
-            f.rename()
+            f.close()
         except (IOError, OSError):
             pass
 
@@ -722,67 +757,112 @@
         finally:
             lock.release()
 
-    def rollback(self, dryrun=False):
+    def rollback(self, dryrun=False, force=False):
         wlock = lock = None
         try:
             wlock = self.wlock()
             lock = self.lock()
             if os.path.exists(self.sjoin("undo")):
-                try:
-                    args = self.opener.read("undo.desc").splitlines()
-                    if len(args) >= 3 and self.ui.verbose:
-                        desc = _("repository tip rolled back to revision %s"
-                                 " (undo %s: %s)\n") % (
-                                 int(args[0]) - 1, args[1], args[2])
-                    elif len(args) >= 2:
-                        desc = _("repository tip rolled back to revision %s"
-                                 " (undo %s)\n") % (
-                                 int(args[0]) - 1, args[1])
-                except IOError:
-                    desc = _("rolling back unknown transaction\n")
-                self.ui.status(desc)
-                if dryrun:
-                    return
-                transaction.rollback(self.sopener, self.sjoin("undo"),
-                                     self.ui.warn)
-                util.rename(self.join("undo.dirstate"), self.join("dirstate"))
-                if os.path.exists(self.join('undo.bookmarks')):
-                    util.rename(self.join('undo.bookmarks'),
-                                self.join('bookmarks'))
-                try:
-                    branch = self.opener.read("undo.branch")
-                    self.dirstate.setbranch(branch)
-                except IOError:
-                    self.ui.warn(_("named branch could not be reset, "
-                                   "current branch is still: %s\n")
-                                 % self.dirstate.branch())
-                self.invalidate()
-                self.dirstate.invalidate()
-                self.destroyed()
-                parents = tuple([p.rev() for p in self.parents()])
-                if len(parents) > 1:
-                    self.ui.status(_("working directory now based on "
-                                     "revisions %d and %d\n") % parents)
-                else:
-                    self.ui.status(_("working directory now based on "
-                                     "revision %d\n") % parents)
+                return self._rollback(dryrun, force)
             else:
                 self.ui.warn(_("no rollback information available\n"))
                 return 1
         finally:
             release(lock, wlock)
 
+    def _rollback(self, dryrun, force):
+        ui = self.ui
+        try:
+            args = self.opener.read('undo.desc').splitlines()
+            (oldlen, desc, detail) = (int(args[0]), args[1], None)
+            if len(args) >= 3:
+                detail = args[2]
+            oldtip = oldlen - 1
+
+            if detail and ui.verbose:
+                msg = (_('repository tip rolled back to revision %s'
+                         ' (undo %s: %s)\n')
+                       % (oldtip, desc, detail))
+            else:
+                msg = (_('repository tip rolled back to revision %s'
+                         ' (undo %s)\n')
+                       % (oldtip, desc))
+        except IOError:
+            msg = _('rolling back unknown transaction\n')
+            desc = None
+
+        if not force and self['.'] != self['tip'] and desc == 'commit':
+            raise util.Abort(
+                _('rollback of last commit while not checked out '
+                  'may lose data'), hint=_('use -f to force'))
+
+        ui.status(msg)
+        if dryrun:
+            return 0
+
+        parents = self.dirstate.parents()
+        transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
+        if os.path.exists(self.join('undo.bookmarks')):
+            util.rename(self.join('undo.bookmarks'),
+                        self.join('bookmarks'))
+        self.invalidate()
+
+        parentgone = (parents[0] not in self.changelog.nodemap or
+                      parents[1] not in self.changelog.nodemap)
+        if parentgone:
+            util.rename(self.join('undo.dirstate'), self.join('dirstate'))
+            try:
+                branch = self.opener.read('undo.branch')
+                self.dirstate.setbranch(branch)
+            except IOError:
+                ui.warn(_('named branch could not be reset: '
+                          'current branch is still \'%s\'\n')
+                        % self.dirstate.branch())
+
+            self.dirstate.invalidate()
+            self.destroyed()
+            parents = tuple([p.rev() for p in self.parents()])
+            if len(parents) > 1:
+                ui.status(_('working directory now based on '
+                            'revisions %d and %d\n') % parents)
+            else:
+                ui.status(_('working directory now based on '
+                            'revision %d\n') % parents)
+        return 0
+
     def invalidatecaches(self):
-        self._tags = None
-        self._tagtypes = None
-        self.nodetagscache = None
+        try:
+            delattr(self, '_tagscache')
+        except AttributeError:
+            pass
+
         self._branchcache = None # in UTF-8
         self._branchcachetip = None
 
+    def invalidatedirstate(self):
+        '''Invalidates the dirstate, causing the next call to dirstate
+        to check if it was modified since the last time it was read,
+        rereading it if it has.
+
+        This is different to dirstate.invalidate() that it doesn't always
+        rereads the dirstate. Use dirstate.invalidate() if you want to
+        explicitly read the dirstate again (i.e. restoring it to a previous
+        known good state).'''
+        try:
+            delattr(self, 'dirstate')
+        except AttributeError:
+            pass
+
     def invalidate(self):
-        for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
-            if a in self.__dict__:
-                delattr(self, a)
+        for k in self._filecache:
+            # dirstate is invalidated separately in invalidatedirstate()
+            if k == 'dirstate':
+                continue
+
+            try:
+                delattr(self, k)
+            except AttributeError:
+                pass
         self.invalidatecaches()
 
     def _lock(self, lockname, wait, releasefn, acquirefn, desc):
@@ -809,7 +889,14 @@
             l.lock()
             return l
 
-        l = self._lock(self.sjoin("lock"), wait, self.store.write,
+        def unlock():
+            self.store.write()
+            for k, ce in self._filecache.items():
+                if k == 'dirstate':
+                    continue
+                ce.refresh()
+
+        l = self._lock(self.sjoin("lock"), wait, unlock,
                        self.invalidate, _('repository %s') % self.origroot)
         self._lockref = weakref.ref(l)
         return l
@@ -823,8 +910,14 @@
             l.lock()
             return l
 
-        l = self._lock(self.join("wlock"), wait, self.dirstate.write,
-                       self.dirstate.invalidate, _('working directory of %s') %
+        def unlock():
+            self.dirstate.write()
+            ce = self._filecache.get('dirstate')
+            if ce:
+                ce.refresh()
+
+        l = self._lock(self.join("wlock"), wait, unlock,
+                       self.invalidatedirstate, _('working directory of %s') %
                        self.origroot)
         self._wlockref = weakref.ref(l)
         return l
--- a/mercurial/lsprof.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/lsprof.py	Sat Oct 15 14:30:50 2011 -0500
@@ -86,9 +86,7 @@
         for k, v in list(sys.modules.iteritems()):
             if v is None:
                 continue
-            if not hasattr(v, '__file__'):
-                continue
-            if not isinstance(v.__file__, str):
+            if not isinstance(getattr(v, '__file__', None), str):
                 continue
             if v.__file__.startswith(code.co_filename):
                 mname = _fn2mod[code.co_filename] = k
--- a/mercurial/mail.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/mail.py	Sat Oct 15 14:30:50 2011 -0500
@@ -37,7 +37,7 @@
     # backward compatible: when tls = true, we use starttls.
     starttls = tls == 'starttls' or util.parsebool(tls)
     smtps = tls == 'smtps'
-    if (starttls or smtps) and not hasattr(socket, 'ssl'):
+    if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
         raise util.Abort(_("can't use TLS: Python SSL support not installed"))
     if smtps:
         ui.note(_('(using smtps)\n'))
--- a/mercurial/match.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/match.py	Sat Oct 15 14:30:50 2011 -0500
@@ -49,7 +49,6 @@
         '<something>' - a pattern of the specified default type
         """
 
-        self._ctx = None
         self._root = root
         self._cwd = cwd
         self._files = []
--- a/mercurial/mdiff.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/mdiff.py	Sat Oct 15 14:30:50 2011 -0500
@@ -157,6 +157,7 @@
             return 0
         return ret
 
+    lastfunc = [0, '']
     def yieldhunk(hunk):
         (astart, a2, bstart, b2, delta) = hunk
         aend = contextend(a2, len(l1))
@@ -165,13 +166,19 @@
 
         func = ""
         if opts.showfunc:
-            # walk backwards from the start of the context
-            # to find a line starting with an alphanumeric char.
-            for x in xrange(astart - 1, -1, -1):
-                t = l1[x].rstrip()
-                if funcre.match(t):
-                    func = ' ' + t[:40]
+            lastpos, func = lastfunc
+            # walk backwards from the start of the context up to the start of
+            # the previous hunk context until we find a line starting with an
+            # alphanumeric char.
+            for i in xrange(astart - 1, lastpos - 1, -1):
+                if l1[i][0].isalnum():
+                    func = ' ' + l1[i].rstrip()[:40]
+                    lastfunc[1] = func
                     break
+            # by recording this hunk's starting point as the next place to
+            # start looking for function lines, we avoid reading any line in
+            # the file more than once.
+            lastfunc[0] = astart
 
         yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
                                            bstart + 1, blen, func)
@@ -180,9 +187,6 @@
         for x in xrange(a2, aend):
             yield ' ' + l1[x]
 
-    if opts.showfunc:
-        funcre = re.compile('\w')
-
     # bdiff.blocks gives us the matching sequences in the files.  The loop
     # below finds the spaces between those matching sequences and translates
     # them into diff output.
--- a/mercurial/merge.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/merge.py	Sat Oct 15 14:30:50 2011 -0500
@@ -273,7 +273,6 @@
     action.sort(key=actionkey)
 
     # prescan for merges
-    u = repo.ui
     for a in action:
         f, m = a[:2]
         if m == 'm': # merge
@@ -308,8 +307,8 @@
     numupdates = len(action)
     for i, a in enumerate(action):
         f, m = a[:2]
-        u.progress(_('updating'), i + 1, item=f, total=numupdates,
-                   unit=_('files'))
+        repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
+                         unit=_('files'))
         if f and f[0] == "/":
             continue
         if m == "r": # remove
@@ -377,7 +376,7 @@
             repo.wopener.audit(f)
             util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
     ms.commit()
-    u.progress(_('updating'), None, total=numupdates, unit=_('files'))
+    repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
 
     return updated, merged, removed, unresolved
 
--- a/mercurial/minirst.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/minirst.py	Sat Oct 15 14:30:50 2011 -0500
@@ -18,17 +18,14 @@
 when adding support for new constructs.
 """
 
-import re, sys
+import re
 import util, encoding
 from i18n import _
 
-
 def replace(text, substs):
-    utext = text.decode(encoding.encoding)
     for f, t in substs:
-        utext = utext.replace(f, t)
-    return utext.encode(encoding.encoding)
-
+        text = text.replace(f, t)
+    return text
 
 _blockre = re.compile(r"\n(?:\s*\n)+")
 
@@ -39,14 +36,14 @@
     has an 'indent' field and a 'lines' field.
     """
     blocks = []
-    for b in _blockre.split(text.strip()):
+    for b in _blockre.split(text.lstrip('\n').rstrip()):
         lines = b.splitlines()
-        indent = min((len(l) - len(l.lstrip())) for l in lines)
-        lines = [l[indent:] for l in lines]
-        blocks.append(dict(indent=indent, lines=lines))
+        if lines:
+            indent = min((len(l) - len(l.lstrip())) for l in lines)
+            lines = [l[indent:] for l in lines]
+            blocks.append(dict(indent=indent, lines=lines))
     return blocks
 
-
 def findliteralblocks(blocks):
     """Finds literal blocks and adds a 'type' field to the blocks.
 
@@ -103,6 +100,7 @@
                        r'((.*)  +)(.*)$')
 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
 _definitionre = re.compile(r'[^ ]')
+_tablere = re.compile(r'(=+\s+)*=+')
 
 def splitparagraphs(blocks):
     """Split paragraphs into lists."""
@@ -146,7 +144,6 @@
         i += 1
     return blocks
 
-
 _fieldwidth = 12
 
 def updatefieldlists(blocks):
@@ -173,7 +170,6 @@
 
     return blocks
 
-
 def updateoptionlists(blocks):
     i = 0
     while i < len(blocks):
@@ -238,18 +234,67 @@
             # Always delete "..container:: type" block
             del blocks[i]
             j = i
+            i -= 1
             while j < len(blocks) and blocks[j]['indent'] > indent:
                 if prune:
                     del blocks[j]
-                    i -= 1 # adjust outer index
                 else:
                     blocks[j]['indent'] -= adjustment
                     j += 1
         i += 1
     return blocks, pruned
 
+_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
 
-_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
+def findtables(blocks):
+    '''Find simple tables
+
+       Only simple one-line table elements are supported
+    '''
+
+    for block in blocks:
+        # Searching for a block that looks like this:
+        #
+        # === ==== ===
+        #  A    B   C
+        # === ==== ===  <- optional
+        #  1    2   3
+        #  x    y   z
+        # === ==== ===
+        if (block['type'] == 'paragraph' and
+            len(block['lines']) > 2 and
+            _tablere.match(block['lines'][0]) and
+            block['lines'][0] == block['lines'][-1]):
+            block['type'] = 'table'
+            block['header'] = False
+            div = block['lines'][0]
+
+            # column markers are ASCII so we can calculate column
+            # position in bytes
+            columns = [x for x in xrange(len(div))
+                       if div[x] == '=' and (x == 0 or div[x - 1] == ' ')]
+            rows = []
+            for l in block['lines'][1:-1]:
+                if l == div:
+                    block['header'] = True
+                    continue
+                row = []
+                # we measure columns not in bytes or characters but in
+                # colwidth which makes things tricky
+                pos = columns[0] # leading whitespace is bytes
+                for n, start in enumerate(columns):
+                    if n + 1 < len(columns):
+                        width = columns[n + 1] - start
+                        v = encoding.getcols(l, pos, width) # gather columns
+                        pos += len(v) # calculate byte position of end
+                        row.append(v.strip())
+                    else:
+                        row.append(l[pos:].strip())
+                rows.append(row)
+
+            block['table'] = rows
+
+    return blocks
 
 def findsections(blocks):
     """Finds sections.
@@ -273,7 +318,6 @@
             del block['lines'][1]
     return blocks
 
-
 def inlineliterals(blocks):
     substs = [('``', '"')]
     for b in blocks:
@@ -281,7 +325,6 @@
             b['lines'] = [replace(l, substs) for l in b['lines']]
     return blocks
 
-
 def hgrole(blocks):
     substs = [(':hg:`', '"hg '), ('`', '"')]
     for b in blocks:
@@ -293,7 +336,6 @@
             b['lines'] = [replace(l, substs) for l in b['lines']]
     return blocks
 
-
 def addmargins(blocks):
     """Adds empty blocks for vertical spacing.
 
@@ -366,7 +408,7 @@
     hanging = block['optstrwidth']
     initindent = '%s%s  ' % (block['optstr'], ' ' * ((hanging - colwidth)))
     hangindent = ' ' * (encoding.colwidth(initindent) + 1)
-    return ' %s' % (util.wrap(desc, usablewidth,
+    return ' %s\n' % (util.wrap(desc, usablewidth,
                                            initindent=initindent,
                                            hangindent=hangindent))
 
@@ -381,25 +423,47 @@
 
         defindent = indent + hang * ' '
         text = ' '.join(map(str.strip, block['lines']))
-        return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
-                                           initindent=defindent,
-                                           hangindent=defindent))
+        return '%s\n%s\n' % (indent + admonition,
+                             util.wrap(text, width=width,
+                                       initindent=defindent,
+                                       hangindent=defindent))
     if block['type'] == 'margin':
-        return ''
+        return '\n'
     if block['type'] == 'literal':
         indent += '  '
-        return indent + ('\n' + indent).join(block['lines'])
+        return indent + ('\n' + indent).join(block['lines']) + '\n'
     if block['type'] == 'section':
         underline = encoding.colwidth(block['lines'][0]) * block['underline']
-        return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
+        return "%s%s\n%s%s\n" % (indent, block['lines'][0],indent, underline)
+    if block['type'] == 'table':
+        table = block['table']
+        # compute column widths
+        widths = [max([encoding.colwidth(e) for e in c]) for c in zip(*table)]
+        text = ''
+        span = sum(widths) + len(widths) - 1
+        indent = ' ' * block['indent']
+        hang = ' ' * (len(indent) + span - widths[-1])
+
+        for row in table:
+            l = []
+            for w, v in zip(widths, row):
+                pad = ' ' * (w - encoding.colwidth(v))
+                l.append(v + pad)
+            l = ' '.join(l)
+            l = util.wrap(l, width=width, initindent=indent, hangindent=hang)
+            if not text and block['header']:
+                text = l + '\n' + indent + '-' * (min(width, span)) + '\n'
+            else:
+                text += l + "\n"
+        return text
     if block['type'] == 'definition':
         term = indent + block['lines'][0]
         hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
         defindent = indent + hang * ' '
         text = ' '.join(map(str.strip, block['lines'][1:]))
-        return '%s\n%s' % (term, util.wrap(text, width=width,
-                                           initindent=defindent,
-                                           hangindent=defindent))
+        return '%s\n%s\n' % (term, util.wrap(text, width=width,
+                                             initindent=defindent,
+                                             hangindent=defindent))
     subindent = indent
     if block['type'] == 'bullet':
         if block['lines'][0].startswith('| '):
@@ -431,15 +495,103 @@
     text = ' '.join(map(str.strip, block['lines']))
     return util.wrap(text, width=width,
                      initindent=indent,
-                     hangindent=subindent)
+                     hangindent=subindent) + '\n'
+
+def formathtml(blocks):
+    """Format RST blocks as HTML"""
+
+    out = []
+    headernest = ''
+    listnest = []
 
+    def openlist(start, level):
+        if not listnest or listnest[-1][0] != start:
+            listnest.append((start, level))
+            out.append('<%s>\n' % start)
+
+    blocks = [b for b in blocks if b['type'] != 'margin']
+
+    for pos, b in enumerate(blocks):
+        btype = b['type']
+        level = b['indent']
+        lines = b['lines']
 
-def format(text, width, indent=0, keep=None):
-    """Parse and format the text according to width."""
+        if btype == 'admonition':
+            admonition = _admonitiontitles[b['admonitiontitle']]
+            text = ' '.join(map(str.strip, lines))
+            out.append('<p>\n<b>%s</b> %s\n</p>\n' % (admonition, text))
+        elif btype == 'paragraph':
+            out.append('<p>\n%s\n</p>\n' % '\n'.join(lines))
+        elif btype == 'margin':
+            pass
+        elif btype == 'literal':
+            out.append('<pre>\n%s\n</pre>\n' % '\n'.join(lines))
+        elif btype == 'section':
+            i = b['underline']
+            if i not in headernest:
+                headernest += i
+            level = headernest.index(i) + 1
+            out.append('<h%d>%s</h%d>\n' % (level, lines[0], level))
+        elif btype == 'table':
+            table = b['table']
+            t = []
+            for row in table:
+                l = []
+                for v in zip(row):
+                    if not t:
+                        l.append('<th>%s</th>' % v)
+                    else:
+                        l.append('<td>%s</td>' % v)
+                t.append(' <tr>%s</tr>\n' % ''.join(l))
+            out.append('<table>\n%s</table>\n' % ''.join(t))
+        elif btype == 'definition':
+            openlist('dl', level)
+            term = lines[0]
+            text = ' '.join(map(str.strip, lines[1:]))
+            out.append(' <dt>%s\n <dd>%s\n' % (term, text))
+        elif btype == 'bullet':
+            bullet, head = lines[0].split(' ', 1)
+            if bullet == '-':
+                openlist('ul', level)
+            else:
+                openlist('ol', level)
+            out.append(' <li> %s\n' % ' '.join([head] + lines[1:]))
+        elif btype == 'field':
+            openlist('dl', level)
+            key = b['key']
+            text = ' '.join(map(str.strip, lines))
+            out.append(' <dt>%s\n <dd>%s\n' % (key, text))
+        elif btype == 'option':
+            openlist('dl', level)
+            opt = b['optstr']
+            desc = ' '.join(map(str.strip, lines))
+            out.append(' <dt>%s\n <dd>%s\n' % (opt, desc))
+
+        # close lists if indent level of next block is lower
+        if listnest:
+            start, level = listnest[-1]
+            if pos == len(blocks) - 1:
+                out.append('</%s>\n' % start)
+                listnest.pop()
+            else:
+                nb = blocks[pos + 1]
+                ni = nb['indent']
+                if (ni < level or
+                    (ni == level and
+                     nb['type'] not in 'definition bullet field option')):
+                    out.append('</%s>\n' % start)
+                    listnest.pop()
+
+    return ''.join(out)
+
+def parse(text, indent=0, keep=None):
+    """Parse text into a list of blocks"""
+    pruned = []
     blocks = findblocks(text)
     for b in blocks:
         b['indent'] += indent
     blocks = findliteralblocks(blocks)
+    blocks = findtables(blocks)
     blocks, pruned = prunecontainers(blocks, keep or [])
     blocks = findsections(blocks)
     blocks = inlineliterals(blocks)
@@ -450,33 +602,68 @@
     blocks = addmargins(blocks)
     blocks = prunecomments(blocks)
     blocks = findadmonitions(blocks)
-    text = '\n'.join(formatblock(b, width) for b in blocks)
+    return blocks, pruned
+
+def formatblocks(blocks, width):
+    text = ''.join(formatblock(b, width) for b in blocks)
+    return text
+
+def format(text, width=80, indent=0, keep=None, style='plain'):
+    """Parse and format the text according to width."""
+    blocks, pruned = parse(text, indent, keep or [])
+    if style == 'html':
+        text = formathtml(blocks)
+    else:
+        text = ''.join(formatblock(b, width) for b in blocks)
     if keep is None:
         return text
     else:
         return text, pruned
 
-
-if __name__ == "__main__":
-    from pprint import pprint
-
-    def debug(func, *args):
-        blocks = func(*args)
-        print "*** after %s:" % func.__name__
-        pprint(blocks)
-        print
-        return blocks
+def getsections(blocks):
+    '''return a list of (section name, nesting level, blocks) tuples'''
+    nest = ""
+    level = 0
+    secs = []
+    for b in blocks:
+        if b['type'] == 'section':
+            i = b['underline']
+            if i not in nest:
+                nest += i
+            level = nest.index(i) + 1
+            nest = nest[:level]
+            secs.append((b['lines'][0], level, [b]))
+        else:
+            if not secs:
+                # add an initial empty section
+                secs = [('', 0, [])]
+            secs[-1][2].append(b)
+    return secs
 
-    text = sys.stdin.read()
-    blocks = debug(findblocks, text)
-    blocks = debug(findliteralblocks, blocks)
-    blocks, pruned = debug(prunecontainers, blocks, sys.argv[1:])
-    blocks = debug(inlineliterals, blocks)
-    blocks = debug(splitparagraphs, blocks)
-    blocks = debug(updatefieldlists, blocks)
-    blocks = debug(updateoptionlists, blocks)
-    blocks = debug(findsections, blocks)
-    blocks = debug(addmargins, blocks)
-    blocks = debug(prunecomments, blocks)
-    blocks = debug(findadmonitions, blocks)
-    print '\n'.join(formatblock(b, 30) for b in blocks)
+def decorateblocks(blocks, width):
+    '''generate a list of (section name, line text) pairs for search'''
+    lines = []
+    for s in getsections(blocks):
+        section = s[0]
+        text = formatblocks(s[2], width)
+        lines.append([(section, l) for l in text.splitlines(True)])
+    return lines
+
+def maketable(data, indent=0, header=False):
+    '''Generate an RST table for the given table data'''
+
+    widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)]
+    indent = ' ' * indent
+    div = indent + ' '.join('=' * w for w in widths) + '\n'
+
+    out = [div]
+    for row in data:
+        l = []
+        for w, v in zip(widths, row):
+            pad = ' ' * (w - encoding.colwidth(v))
+            l.append(v + pad)
+        out.append(indent + ' '.join(l) + "\n")
+    if header and len(data) > 1:
+        out.insert(2, div)
+    out.append(div)
+    return ''.join(out)
--- a/mercurial/osutil.c	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/osutil.c	Sat Oct 15 14:30:50 2011 -0500
@@ -12,6 +12,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 
 #ifdef _WIN32
 #include <windows.h>
@@ -288,7 +289,8 @@
 #endif
 
 	if (pathlen >= PATH_MAX) {
-		PyErr_SetString(PyExc_ValueError, "path too long");
+		errno = ENAMETOOLONG;
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
 		goto error_value;
 	}
 	strncpy(fullpath, path, PATH_MAX);
--- a/mercurial/patch.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/patch.py	Sat Oct 15 14:30:50 2011 -0500
@@ -126,7 +126,7 @@
 
     mimeheaders = ['content-type']
 
-    if not hasattr(stream, 'next'):
+    if not util.safehasattr(stream, 'next'):
         # http responses, for example, have readline but not next
         stream = fiter(stream)
 
@@ -1619,27 +1619,36 @@
 
 def difflabel(func, *args, **kw):
     '''yields 2-tuples of (output, label) based on the output of func()'''
-    prefixes = [('diff', 'diff.diffline'),
-                ('copy', 'diff.extended'),
-                ('rename', 'diff.extended'),
-                ('old', 'diff.extended'),
-                ('new', 'diff.extended'),
-                ('deleted', 'diff.extended'),
-                ('---', 'diff.file_a'),
-                ('+++', 'diff.file_b'),
-                ('@@', 'diff.hunk'),
-                ('-', 'diff.deleted'),
-                ('+', 'diff.inserted')]
-
+    headprefixes = [('diff', 'diff.diffline'),
+                    ('copy', 'diff.extended'),
+                    ('rename', 'diff.extended'),
+                    ('old', 'diff.extended'),
+                    ('new', 'diff.extended'),
+                    ('deleted', 'diff.extended'),
+                    ('---', 'diff.file_a'),
+                    ('+++', 'diff.file_b')]
+    textprefixes = [('@', 'diff.hunk'),
+                    ('-', 'diff.deleted'),
+                    ('+', 'diff.inserted')]
+    head = False
     for chunk in func(*args, **kw):
         lines = chunk.split('\n')
         for i, line in enumerate(lines):
             if i != 0:
                 yield ('\n', '')
+            if head:
+                if line.startswith('@'):
+                    head = False
+            else:
+                if line and not line[0] in ' +-@':
+                    head = True
             stripline = line
-            if line and line[0] in '+-':
+            if not head and line and line[0] in '+-':
                 # highlight trailing whitespace, but only in changed lines
                 stripline = line.rstrip()
+            prefixes = textprefixes
+            if head:
+                prefixes = headprefixes
             for prefix, label in prefixes:
                 if stripline.startswith(prefix):
                     yield (stripline, label)
--- a/mercurial/posix.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/posix.py	Sat Oct 15 14:30:50 2011 -0500
@@ -84,6 +84,21 @@
         # Turn off all +x bits
         os.chmod(f, s & 0666)
 
+def copymode(src, dst, mode=None):
+    '''Copy the file mode from the file at path src to dst.
+    If src doesn't exist, we're using mode instead. If mode is None, we're
+    using umask.'''
+    try:
+        st_mode = os.lstat(src).st_mode & 0777
+    except OSError, inst:
+        if inst.errno != errno.ENOENT:
+            raise
+        st_mode = mode
+        if st_mode is None:
+            st_mode = ~umask
+        st_mode &= 0666
+    os.chmod(dst, st_mode)
+
 def checkexec(path):
     """
     Check whether the given path is on a filesystem with UNIX-like exec flags
@@ -241,7 +256,9 @@
     for path in os.environ.get('PATH', '').split(os.pathsep):
         executable = findexisting(os.path.join(path, command))
         if executable is not None:
-            return executable
+            st = os.stat(executable)
+            if (st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)):
+                return executable
     return None
 
 def setsignalhandler():
@@ -325,3 +342,45 @@
     except ImportError:
         pass
     return 80
+
+def makedir(path, notindexed):
+    os.mkdir(path)
+
+def unlinkpath(f):
+    """unlink and remove the directory if it is empty"""
+    os.unlink(f)
+    # try removing directories that might now be empty
+    try:
+        os.removedirs(os.path.dirname(f))
+    except OSError:
+        pass
+
+def lookupreg(key, name=None, scope=None):
+    return None
+
+def hidewindow():
+    """Hide current shell window.
+
+    Used to hide the window opened when starting asynchronous
+    child process under Windows, unneeded on other systems.
+    """
+    pass
+
+class cachestat(object):
+    def __init__(self, path):
+        self.stat = os.stat(path)
+
+    def cacheable(self):
+        return bool(self.stat.st_ino)
+
+    def __eq__(self, other):
+        try:
+            return self.stat == other.stat
+        except AttributeError:
+            return False
+
+    def __ne__(self, other):
+        return not self == other
+
+def executablepath():
+    return None # available on Windows only
--- a/mercurial/pure/parsers.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/pure/parsers.py	Sat Oct 15 14:30:50 2011 -0500
@@ -36,7 +36,7 @@
     s = struct.calcsize(indexformatng)
     index = []
     cache = None
-    n = off = 0
+    off = 0
 
     l = len(data) - s
     append = index.append
@@ -45,7 +45,6 @@
         while off <= l:
             e = _unpack(indexformatng, data[off:off + s])
             append(e)
-            n += 1
             if e[1] < 0:
                 break
             off += e[1] + s
@@ -53,7 +52,6 @@
         while off <= l:
             e = _unpack(indexformatng, data[off:off + s])
             append(e)
-            n += 1
             off += s
 
     if off != len(data):
--- a/mercurial/repair.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/repair.py	Sat Oct 15 14:30:50 2011 -0500
@@ -11,9 +11,8 @@
 from mercurial.i18n import _
 import os
 
-def _bundle(repo, bases, heads, node, suffix, compress=True):
+def _bundle(repo, cg, node, suffix, compress=True):
     """create a bundle with the specified revisions as a backup"""
-    cg = repo.changegroupsubset(bases, heads, 'strip')
     backupdir = repo.join("strip-backup")
     if not os.path.isdir(backupdir):
         os.mkdir(backupdir)
@@ -83,11 +82,9 @@
             saveheads.add(r)
     saveheads = [cl.node(r) for r in saveheads]
 
-    # compute base nodes
-    if saverevs:
-        descendants = set(cl.descendants(*saverevs))
-        saverevs.difference_update(descendants)
-    savebases = [cl.node(r) for r in saverevs]
+    # compute common nodes
+    savecommon = set(cl.node(p) for r in saverevs for p in cl.parentrevs(r)
+                     if p not in saverevs and p not in tostrip)
 
     bm = repo._bookmarks
     updatebm = []
@@ -99,12 +96,14 @@
     # create a changegroup for all the branches we need to keep
     backupfile = None
     if backup == "all":
-        backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
+        allnodes=[cl.node(r) for r in xrange(striprev, len(cl))]
+        cg = repo._changegroup(allnodes, 'strip')
+        backupfile = _bundle(repo, cg, node, 'backup')
         repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
-    if saveheads or savebases:
+    if saveheads or savecommon:
         # do not compress partial bundle if we remove it from disk later
-        chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
-                            compress=keeppartialbundle)
+        cg = repo.getbundle('strip', common=savecommon, heads=saveheads)
+        chgrpfile = _bundle(repo, cg, node, 'temp', compress=keeppartialbundle)
 
     mfst = repo.manifest
 
@@ -128,7 +127,7 @@
             tr.abort()
             raise
 
-        if saveheads or savebases:
+        if saveheads or savecommon:
             ui.note(_("adding branch\n"))
             f = open(chgrpfile, "rb")
             gen = changegroup.readbundle(f, chgrpfile)
--- a/mercurial/revlog.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/revlog.py	Sat Oct 15 14:30:50 2011 -0500
@@ -226,9 +226,10 @@
         self._nodepos = None
 
         v = REVLOG_DEFAULT_VERSION
-        if hasattr(opener, 'options'):
-            if 'revlogv1' in opener.options:
-                if 'generaldelta' in opener.options:
+        opts = getattr(opener, 'options', None)
+        if opts is not None:
+            if 'revlogv1' in opts:
+                if 'generaldelta' in opts:
                     v |= REVLOGGENERALDELTA
             else:
                 v = 0
@@ -945,9 +946,9 @@
             e = self._io.packentry(self.index[i], self.node, self.version, i)
             fp.write(e)
 
-        # if we don't call rename, the temp file will never replace the
+        # if we don't call close, the temp file will never replace the
         # real index
-        fp.rename()
+        fp.close()
 
         tr.replace(self.indexfile, trindex * self._io.size)
         self._chunkclear()
--- a/mercurial/revset.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/revset.py	Sat Oct 15 14:30:50 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import re
-import parser, util, error, discovery, hbisect
+import parser, util, error, discovery, hbisect, node
 import bookmarks as bookmarksmod
 import match as matchmod
 from i18n import _
@@ -235,15 +235,24 @@
     n = getstring(x, _("author requires a string")).lower()
     return [r for r in subset if n in repo[r].user().lower()]
 
-def bisected(repo, subset, x):
-    """``bisected(string)``
-    Changesets marked in the specified bisect state (good, bad, skip).
+def bisect(repo, subset, x):
+    """``bisect(string)``
+    Changesets marked in the specified bisect status:
+
+    - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
+    - ``goods``, ``bads``      : csets topologicaly good/bad
+    - ``range``              : csets taking part in the bisection
+    - ``pruned``             : csets that are goods, bads or skipped
+    - ``untested``           : csets whose fate is yet unknown
+    - ``ignored``            : csets ignored due to DAG topology
     """
-    state = getstring(x, _("bisect requires a string")).lower()
-    if state not in ('good', 'bad', 'skip', 'unknown'):
-        raise error.ParseError(_('invalid bisect state'))
-    marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
-    return [r for r in subset if r in marked]
+    status = getstring(x, _("bisect requires a string")).lower()
+    return [r for r in subset if r in hbisect.get(repo, status)]
+
+# Backward-compatibility
+# - no help entry so that we do not advertise it any more
+def bisected(repo, subset, x):
+    return bisect(repo, subset, x)
 
 def bookmark(repo, subset, x):
     """``bookmark([name])``
@@ -407,6 +416,12 @@
 
     return [r for r in subset if r in s]
 
+def first(repo, subset, x):
+    """``first(set, [n])``
+    An alias for limit().
+    """
+    return limit(repo, subset, x)
+
 def follow(repo, subset, x):
     """``follow([file])``
     An alias for ``::.`` (ancestors of the working copy's first parent).
@@ -513,14 +528,16 @@
     return l
 
 def limit(repo, subset, x):
-    """``limit(set, n)``
-    First n members of set.
+    """``limit(set, [n])``
+    First n members of set, defaulting to 1.
     """
     # i18n: "limit" is a keyword
-    l = getargs(x, 2, 2, _("limit requires two arguments"))
+    l = getargs(x, 1, 2, _("limit requires one or two arguments"))
     try:
-        # i18n: "limit" is a keyword
-        lim = int(getstring(l[1], _("limit requires a number")))
+        lim = 1
+        if len(l) == 2:
+            # i18n: "limit" is a keyword
+            lim = int(getstring(l[1], _("limit requires a number")))
     except (TypeError, ValueError):
         # i18n: "limit" is a keyword
         raise error.ParseError(_("limit expects a number"))
@@ -529,14 +546,16 @@
     return [r for r in os if r in ss]
 
 def last(repo, subset, x):
-    """``last(set, n)``
-    Last n members of set.
+    """``last(set, [n])``
+    Last n members of set, defaulting to 1.
     """
     # i18n: "last" is a keyword
-    l = getargs(x, 2, 2, _("last requires two arguments"))
+    l = getargs(x, 1, 2, _("last requires one or two arguments"))
     try:
-        # i18n: "last" is a keyword
-        lim = int(getstring(l[1], _("last requires a number")))
+        lim = 1
+        if len(l) == 2:
+            # i18n: "last" is a keyword
+            lim = int(getstring(l[1], _("last requires a number")))
     except (TypeError, ValueError):
         # i18n: "last" is a keyword
         raise error.ParseError(_("last expects a number"))
@@ -827,6 +846,7 @@
     "ancestor": ancestor,
     "ancestors": ancestors,
     "author": author,
+    "bisect": bisect,
     "bisected": bisected,
     "bookmark": bookmark,
     "branch": branch,
@@ -838,6 +858,7 @@
     "descendants": descendants,
     "file": hasfile,
     "filelog": filelog,
+    "first": first,
     "follow": follow,
     "grep": grep,
     "head": head,
@@ -951,7 +972,7 @@
             w = 100 # very slow
         elif f == "ancestor":
             w = 1 * smallbonus
-        elif f in "reverse limit":
+        elif f in "reverse limit first":
             w = 0
         elif f in "sort":
             w = 10 # assume most sorts look at changelog
@@ -1019,11 +1040,87 @@
     tree, pos = parse(spec)
     if (pos != len(spec)):
         raise error.ParseError(_("invalid token"), pos)
-    tree = findaliases(ui, tree)
+    if ui:
+        tree = findaliases(ui, tree)
     weight, tree = optimize(tree, True)
     def mfunc(repo, subset):
         return getset(repo, subset, tree)
     return mfunc
 
+def formatspec(expr, *args):
+    '''
+    This is a convenience function for using revsets internally, and
+    escapes arguments appropriately. Aliases are intentionally ignored
+    so that intended expression behavior isn't accidentally subverted.
+
+    Supported arguments:
+
+    %r = revset expression, parenthesized
+    %d = int(arg), no quoting
+    %s = string(arg), escaped and single-quoted
+    %b = arg.branch(), escaped and single-quoted
+    %n = hex(arg), single-quoted
+    %% = a literal '%'
+
+    Prefixing the type with 'l' specifies a parenthesized list of that type.
+
+    >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
+    '(10 or 11):: and ((this()) or (that()))'
+    >>> formatspec('%d:: and not %d::', 10, 20)
+    '10:: and not 20::'
+    >>> formatspec('keyword(%s)', 'foo\\xe9')
+    "keyword('foo\\\\xe9')"
+    >>> b = lambda: 'default'
+    >>> b.branch = b
+    >>> formatspec('branch(%b)', b)
+    "branch('default')"
+    >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
+    "root(('a' or 'b' or 'c' or 'd'))"
+    '''
+
+    def quote(s):
+        return repr(str(s))
+
+    def argtype(c, arg):
+        if c == 'd':
+            return str(int(arg))
+        elif c == 's':
+            return quote(arg)
+        elif c == 'r':
+            parse(arg) # make sure syntax errors are confined
+            return '(%s)' % arg
+        elif c == 'n':
+            return quote(node.hex(arg))
+        elif c == 'b':
+            return quote(arg.branch())
+
+    ret = ''
+    pos = 0
+    arg = 0
+    while pos < len(expr):
+        c = expr[pos]
+        if c == '%':
+            pos += 1
+            d = expr[pos]
+            if d == '%':
+                ret += d
+            elif d in 'dsnbr':
+                ret += argtype(d, args[arg])
+                arg += 1
+            elif d == 'l':
+                # a list of some type
+                pos += 1
+                d = expr[pos]
+                lv = ' or '.join(argtype(d, e) for e in args[arg])
+                ret += '(%s)' % lv
+                arg += 1
+            else:
+                raise util.Abort('unexpected revspec format character %s' % d)
+        else:
+            ret += c
+        pos += 1
+
+    return ret
+
 # tell hggettext to extract docstrings from these functions:
 i18nfunctions = symbols.values()
--- a/mercurial/scmutil.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/scmutil.py	Sat Oct 15 14:30:50 2011 -0500
@@ -324,10 +324,10 @@
     def errhandler(err):
         if err.filename == path:
             raise err
-    if followsym and hasattr(os.path, 'samestat'):
+    samestat = getattr(os.path, 'samestat', None)
+    if followsym and samestat is not None:
         def adddir(dirlst, dirname):
             match = False
-            samestat = os.path.samestat
             dirstat = os.stat(dirname)
             for lstdirstat in dirlst:
                 if samestat(dirstat, lstdirstat):
@@ -709,3 +709,95 @@
         raise error.RequirementError(_("unknown repository format: "
             "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
     return requirements
+
+class filecacheentry(object):
+    def __init__(self, path):
+        self.path = path
+        self.cachestat = filecacheentry.stat(self.path)
+
+        if self.cachestat:
+            self._cacheable = self.cachestat.cacheable()
+        else:
+            # None means we don't know yet
+            self._cacheable = None
+
+    def refresh(self):
+        if self.cacheable():
+            self.cachestat = filecacheentry.stat(self.path)
+
+    def cacheable(self):
+        if self._cacheable is not None:
+            return self._cacheable
+
+        # we don't know yet, assume it is for now
+        return True
+
+    def changed(self):
+        # no point in going further if we can't cache it
+        if not self.cacheable():
+            return True
+
+        newstat = filecacheentry.stat(self.path)
+
+        # we may not know if it's cacheable yet, check again now
+        if newstat and self._cacheable is None:
+            self._cacheable = newstat.cacheable()
+
+            # check again
+            if not self._cacheable:
+                return True
+
+        if self.cachestat != newstat:
+            self.cachestat = newstat
+            return True
+        else:
+            return False
+
+    @staticmethod
+    def stat(path):
+        try:
+            return util.cachestat(path)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+
+class filecache(object):
+    '''A property like decorator that tracks a file under .hg/ for updates.
+
+    Records stat info when called in _filecache.
+
+    On subsequent calls, compares old stat info with new info, and recreates
+    the object when needed, updating the new stat info in _filecache.
+
+    Mercurial either atomic renames or appends for files under .hg,
+    so to ensure the cache is reliable we need the filesystem to be able
+    to tell us if a file has been replaced. If it can't, we fallback to
+    recreating the object on every call (essentially the same behaviour as
+    propertycache).'''
+    def __init__(self, path, instore=False):
+        self.path = path
+        self.instore = instore
+
+    def __call__(self, func):
+        self.func = func
+        self.name = func.__name__
+        return self
+
+    def __get__(self, obj, type=None):
+        entry = obj._filecache.get(self.name)
+
+        if entry:
+            if entry.changed():
+                entry.obj = self.func(obj)
+        else:
+            path = self.instore and obj.sjoin(self.path) or obj.join(self.path)
+
+            # We stat -before- creating the object so our cache doesn't lie if
+            # a writer modified between the time we read and stat
+            entry = filecacheentry(path)
+            entry.obj = self.func(obj)
+
+            obj._filecache[self.name] = entry
+
+        setattr(obj, self.name, entry.obj)
+        return entry.obj
--- a/mercurial/simplemerge.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/simplemerge.py	Sat Oct 15 14:30:50 2011 -0500
@@ -445,7 +445,7 @@
         out.write(line)
 
     if not opts.get('print'):
-        out.rename()
+        out.close()
 
     if m3.conflicts:
         if not opts.get('quiet'):
--- a/mercurial/sshrepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/sshrepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -164,6 +164,17 @@
 
     def _recv(self):
         l = self.pipei.readline()
+        if l == '\n':
+            err = []
+            while True:
+                line = self.pipee.readline()
+                if line == '-\n':
+                    break
+                err.extend([line])
+            if len(err) > 0:
+                # strip the trailing newline added to the last line server-side
+                err[-1] = err[-1][:-1]
+            self._abort(error.OutOfBandError(*err))
         self.readerr()
         try:
             l = int(l)
--- a/mercurial/sshserver.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/sshserver.py	Sat Oct 15 14:30:50 2011 -0500
@@ -82,6 +82,12 @@
     def sendpusherror(self, rsp):
         self.sendresponse(rsp.res)
 
+    def sendooberror(self, rsp):
+        self.ui.ferr.write('%s\n-\n' % rsp.message)
+        self.ui.ferr.flush()
+        self.fout.write('\n')
+        self.fout.flush()
+
     def serve_forever(self):
         try:
             while self.serve_one():
@@ -96,6 +102,7 @@
         wireproto.streamres: sendstream,
         wireproto.pushres: sendpushresponse,
         wireproto.pusherr: sendpusherror,
+        wireproto.ooberror: sendooberror,
     }
 
     def serve_one(self):
--- a/mercurial/sslutil.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/sslutil.py	Sat Oct 15 14:30:50 2011 -0500
@@ -22,6 +22,8 @@
 
     def ssl_wrap_socket(sock, key_file, cert_file,
                         cert_reqs=CERT_REQUIRED, ca_certs=None):
+        if not util.safehasattr(socket, 'ssl'):
+            raise util.Abort(_('Python SSL support not found'))
         if ca_certs:
             raise util.Abort(_(
                 'certificate checking requires Python 2.6'))
--- a/mercurial/statichttprepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/statichttprepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -31,15 +31,11 @@
         try:
             f = self.opener.open(req)
             data = f.read()
-            if hasattr(f, 'getcode'):
-                # python 2.6+
-                code = f.getcode()
-            elif hasattr(f, 'code'):
-                # undocumented attribute, seems to be set in 2.4 and 2.5
-                code = f.code
-            else:
-                # Don't know how to check, hope for the best.
-                code = 206
+            # Python 2.6+ defines a getcode() function, and 2.4 and
+            # 2.5 appear to always have an undocumented code attribute
+            # set. If we can't read either of those, fall back to 206
+            # and hope for the best.
+            code = getattr(f, 'getcode', lambda : getattr(f, 'code', 206))()
         except urllib2.HTTPError, inst:
             num = inst.code == 404 and errno.ENOENT or None
             raise IOError(num, inst)
@@ -125,6 +121,7 @@
         self.encodepats = None
         self.decodepats = None
         self.capabilities.difference_update(["pushkey"])
+        self._filecache = {}
 
     def url(self):
         return self._url
--- a/mercurial/store.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/store.py	Sat Oct 15 14:30:50 2011 -0500
@@ -345,7 +345,7 @@
         fp = self.opener('fncache', mode='wb', atomictemp=True)
         for p in self.entries:
             fp.write(encodedir(p) + '\n')
-        fp.rename()
+        fp.close()
         self._dirty = False
 
     def add(self, fn):
--- a/mercurial/subrepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/subrepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -50,15 +50,7 @@
             if err.errno != errno.ENOENT:
                 raise
 
-    state = {}
-    for path, src in p[''].items():
-        kind = 'hg'
-        if src.startswith('['):
-            if ']' not in src:
-                raise util.Abort(_('missing ] in subrepo source'))
-            kind, src = src.split(']', 1)
-            kind = kind[1:]
-
+    def remap(src):
         for pattern, repl in p.items('subpaths'):
             # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
             # does a string decode.
@@ -72,7 +64,34 @@
             except re.error, e:
                 raise util.Abort(_("bad subrepository pattern in %s: %s")
                                  % (p.source('subpaths', pattern), e))
+        return src
 
+    state = {}
+    for path, src in p[''].items():
+        kind = 'hg'
+        if src.startswith('['):
+            if ']' not in src:
+                raise util.Abort(_('missing ] in subrepo source'))
+            kind, src = src.split(']', 1)
+            kind = kind[1:]
+            src = src.lstrip() # strip any extra whitespace after ']'
+
+        if not util.url(src).isabs():
+            parent = _abssource(ctx._repo, abort=False)
+            if parent:
+                parent = util.url(parent)
+                parent.path = posixpath.join(parent.path or '', src)
+                parent.path = posixpath.normpath(parent.path)
+                joined = str(parent)
+                # Remap the full joined path and use it if it changes,
+                # else remap the original source.
+                remapped = remap(joined)
+                if remapped == joined:
+                    src = remap(src)
+                else:
+                    src = remapped
+
+        src = remap(src)
         state[path] = (src.strip(), rev.get(path, ''), kind)
 
     return state
@@ -181,23 +200,23 @@
 def reporelpath(repo):
     """return path to this (sub)repo as seen from outermost repo"""
     parent = repo
-    while hasattr(parent, '_subparent'):
+    while util.safehasattr(parent, '_subparent'):
         parent = parent._subparent
     p = parent.root.rstrip(os.sep)
     return repo.root[len(p) + 1:]
 
 def subrelpath(sub):
     """return path to this subrepo as seen from outermost repo"""
-    if hasattr(sub, '_relpath'):
+    if util.safehasattr(sub, '_relpath'):
         return sub._relpath
-    if not hasattr(sub, '_repo'):
+    if not util.safehasattr(sub, '_repo'):
         return sub._path
     return reporelpath(sub._repo)
 
 def _abssource(repo, push=False, abort=True):
     """return pull/push path of repo - either based on parent repo .hgsub info
     or on the top repo config. Abort or return None if no source found."""
-    if hasattr(repo, '_subparent'):
+    if util.safehasattr(repo, '_subparent'):
         source = util.url(repo._subsource)
         if source.isabs():
             return str(source)
@@ -209,7 +228,7 @@
             parent.path = posixpath.normpath(parent.path)
             return str(parent)
     else: # recursion reached top repo
-        if hasattr(repo, '_subtoppath'):
+        if util.safehasattr(repo, '_subtoppath'):
             return repo._subtoppath
         if push and repo.ui.config('paths', 'default-push'):
             return repo.ui.config('paths', 'default-push')
@@ -530,9 +549,13 @@
         self._state = state
         self._ctx = ctx
         self._ui = ctx._repo.ui
+        self._exe = util.findexe('svn')
+        if not self._exe:
+            raise util.Abort(_("'svn' executable not found for subrepo '%s'")
+                             % self._path)
 
     def _svncommand(self, commands, filename='', failok=False):
-        cmd = ['svn']
+        cmd = [self._exe]
         extrakw = {}
         if not self._ui.interactive():
             # Making stdin be a pipe should prevent svn from behaving
@@ -810,9 +833,10 @@
         for b in branches:
             if b.startswith('refs/remotes/'):
                 continue
-            remote = self._gitcommand(['config', 'branch.%s.remote' % b])
+            bname = b.split('/', 2)[2]
+            remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
             if remote:
-                ref = self._gitcommand(['config', 'branch.%s.merge' % b])
+                ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
                 tracking['refs/remotes/%s/%s' %
                          (remote, ref.split('/', 2)[2])] = b
         return tracking
--- a/mercurial/tags.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/tags.py	Sat Oct 15 14:30:50 2011 -0500
@@ -287,6 +287,6 @@
         cachefile.write("%s %s\n" % (hex(node), name))
 
     try:
-        cachefile.rename()
+        cachefile.close()
     except (OSError, IOError):
         pass
--- a/mercurial/templatefilters.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templatefilters.py	Sat Oct 15 14:30:50 2011 -0500
@@ -7,6 +7,7 @@
 
 import cgi, re, os, time, urllib
 import encoding, node, util
+import hbisect
 
 def addbreaks(text):
     """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
@@ -188,13 +189,13 @@
         return '"%s"' % jsonescape(u)
     elif isinstance(obj, unicode):
         return '"%s"' % jsonescape(obj)
-    elif hasattr(obj, 'keys'):
+    elif util.safehasattr(obj, 'keys'):
         out = []
         for k, v in obj.iteritems():
             s = '%s: %s' % (json(k), json(v))
             out.append(s)
         return '{' + ', '.join(out) + '}'
-    elif hasattr(obj, '__iter__'):
+    elif util.safehasattr(obj, '__iter__'):
         out = []
         for i in obj:
             out.append(json(i))
@@ -268,6 +269,14 @@
     """
     return text[:12]
 
+def shortbisect(text):
+    """:shortbisect: Any text. Treats `text` as a bisection status, and
+    returns a single-character representing the status (G: good, B: bad,
+    S: skipped, U: untested, I: ignored). Returns single space if `text`
+    is not a valid bisection status.
+    """
+    return hbisect.shortlabel(text) or ' '
+
 def shortdate(text):
     """:shortdate: Date. Returns a date like "2006-09-18"."""
     return util.shortdate(text)
@@ -279,7 +288,7 @@
     """:stringify: Any type. Turns the value into text by converting values into
     text and concatenating them.
     """
-    if hasattr(thing, '__iter__') and not isinstance(thing, str):
+    if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
         return "".join([stringify(t) for t in thing if t is not None])
     return str(thing)
 
@@ -347,6 +356,7 @@
     "rfc3339date": rfc3339date,
     "rfc822date": rfc822date,
     "short": short,
+    "shortbisect": shortbisect,
     "shortdate": shortdate,
     "stringescape": stringescape,
     "stringify": stringify,
--- a/mercurial/templatekw.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templatekw.py	Sat Oct 15 14:30:50 2011 -0500
@@ -7,6 +7,7 @@
 
 from node import hex
 import patch, util, error
+import hbisect
 
 def showlist(name, values, plural=None, **args):
     '''expand set of values.
@@ -145,6 +146,10 @@
     """:author: String. The unmodified author of the changeset."""
     return ctx.user()
 
+def showbisect(repo, ctx, templ, **args):
+    """:bisect: String. The changeset bisection status."""
+    return hbisect.label(repo, ctx.node())
+
 def showbranch(**args):
     """:branch: String. The name of the branch on which the changeset was
     committed.
@@ -288,6 +293,7 @@
 # revcache - a cache dictionary for the current revision
 keywords = {
     'author': showauthor,
+    'bisect': showbisect,
     'branch': showbranch,
     'branches': showbranches,
     'bookmarks': showbookmarks,
--- a/mercurial/templater.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templater.py	Sat Oct 15 14:30:50 2011 -0500
@@ -135,7 +135,7 @@
     v = mapping.get(key)
     if v is None:
         v = context._defaults.get(key, '')
-    if hasattr(v, '__call__'):
+    if util.safehasattr(v, '__call__'):
         return v(**mapping)
     return v
 
@@ -172,14 +172,14 @@
 def buildfunc(exp, context):
     n = getsymbol(exp[1])
     args = [compileexp(x, context) for x in getlist(exp[2])]
+    if n in funcs:
+        f = funcs[n]
+        return (f, args)
     if n in context._filters:
         if len(args) != 1:
             raise error.ParseError(_("filter %s expects one argument") % n)
         f = context._filters[n]
         return (runfilter, (args[0][0], args[0][1], f))
-    elif n in context._funcs:
-        f = context._funcs[n]
-        return (f, args)
 
 methods = {
     "string": lambda e, c: (runstring, e[1]),
@@ -191,6 +191,9 @@
     "func": buildfunc,
     }
 
+funcs = {
+}
+
 # template engine
 
 path = ['templates', '../templates']
@@ -200,14 +203,14 @@
     '''yield a single stream from a possibly nested set of iterators'''
     if isinstance(thing, str):
         yield thing
-    elif not hasattr(thing, '__iter__'):
+    elif not util.safehasattr(thing, '__iter__'):
         if thing is not None:
             yield str(thing)
     else:
         for i in thing:
             if isinstance(i, str):
                 yield i
-            elif not hasattr(i, '__iter__'):
+            elif not util.safehasattr(i, '__iter__'):
                 if i is not None:
                     yield str(i)
             elif i is not None:
@@ -338,7 +341,7 @@
     normpaths = []
 
     # executable version (py2exe) doesn't support __file__
-    if hasattr(sys, 'frozen'):
+    if util.mainfrozen():
         module = sys.executable
     else:
         module = __file__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/map-cmdline.bisect	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,25 @@
+changeset = 'changeset:   {rev}:{node|short}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}user:        {author}\ndate:        {date|date}\nsummary:     {desc|firstline}\n\n'
+changeset_quiet = '{bisect|shortbisect} {rev}:{node|short}\n'
+changeset_verbose = 'changeset:   {rev}:{node|short}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}user:        {author}\ndate:        {date|date}\n{files}{file_copies_switch}description:\n{desc|strip}\n\n\n'
+changeset_debug = 'changeset:   {rev}:{node}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies_switch}{extras}description:\n{desc|strip}\n\n\n'
+start_files = 'files:      '
+file = ' {file}'
+end_files = '\n'
+start_file_mods = 'files:      '
+file_mod = ' {file_mod}'
+end_file_mods = '\n'
+start_file_adds = 'files+:     '
+file_add = ' {file_add}'
+end_file_adds = '\n'
+start_file_dels = 'files-:     '
+file_del = ' {file_del}'
+end_file_dels = '\n'
+start_file_copies = 'copies:     '
+file_copy = ' {name} ({source})'
+end_file_copies = '\n'
+parent = 'parent:      {rev}:{node|formatnode}\n'
+manifest = 'manifest:    {rev}:{node}\n'
+branch = 'branch:      {branch}\n'
+tag = 'tag:         {tag}\n'
+bookmark = 'bookmark:    {bookmark}\n'
+extra = 'extra:       {key}={value|stringescape}\n'
--- a/mercurial/templates/monoblue/footer.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/monoblue/footer.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -9,7 +9,7 @@
     </div>
 
     <div id="powered-by">
-        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
     </div>
 
     <div id="corner-top-left"></div>
--- a/mercurial/templates/monoblue/index.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/monoblue/index.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -26,7 +26,7 @@
     </div>
 
     <div id="powered-by">
-        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
     </div>
 
     <div id="corner-top-left"></div>
--- a/mercurial/templates/paper/bookmarks.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/bookmarks.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/branches.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/branches.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/changeset.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/changeset.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -6,7 +6,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
  <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/error.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/error.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/fileannotate.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/fileannotate.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filediff.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/filediff.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filelog.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/filelog.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filerevision.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/filerevision.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/graph.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/graph.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -12,7 +12,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/help.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/help.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/helptopics.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/helptopics.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/index.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/index.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -6,7 +6,7 @@
 <div class="container">
 <div class="menu">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
 </div>
 <div class="main">
 <h2>Mercurial Repositories</h2>
--- a/mercurial/templates/paper/manifest.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/manifest.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/search.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/search.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/shortlog.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/shortlog.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li class="active">log</li>
--- a/mercurial/templates/paper/tags.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/paper/tags.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/spartan/footer.tmpl	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/templates/spartan/footer.tmpl	Sat Oct 15 14:30:50 2011 -0500
@@ -2,7 +2,7 @@
 {motd}
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 
 </body>
--- a/mercurial/ui.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/ui.py	Sat Oct 15 14:30:50 2011 -0500
@@ -46,7 +46,7 @@
     def copy(self):
         return self.__class__(self)
 
-    def _is_trusted(self, fp, f):
+    def _trusted(self, fp, f):
         st = util.fstat(fp)
         if util.isowner(st):
             return True
@@ -75,7 +75,7 @@
             raise
 
         cfg = config.config()
-        trusted = sections or trust or self._is_trusted(fp, filename)
+        trusted = sections or trust or self._trusted(fp, filename)
 
         try:
             cfg.read(filename, fp, sections=sections, remap=remap)
@@ -155,7 +155,19 @@
         return self._data(untrusted).source(section, name) or 'none'
 
     def config(self, section, name, default=None, untrusted=False):
-        value = self._data(untrusted).get(section, name, default)
+        if isinstance(name, list):
+            alternates = name
+        else:
+            alternates = [name]
+
+        for n in alternates:
+            value = self._data(untrusted).get(section, name, None)
+            if value is not None:
+                name = n
+                break
+        else:
+            value = default
+
         if self.debugflag and not untrusted and self._reportuntrusted:
             uvalue = self._ucfg.get(section, name)
             if uvalue is not None and uvalue != value:
@@ -164,12 +176,14 @@
         return value
 
     def configpath(self, section, name, default=None, untrusted=False):
-        'get a path config item, expanded relative to config file'
+        'get a path config item, expanded relative to repo root or config file'
         v = self.config(section, name, default, untrusted)
+        if v is None:
+            return None
         if not os.path.isabs(v) or "://" not in v:
             src = self.configsource(section, name, untrusted)
             if ':' in src:
-                base = os.path.dirname(src.rsplit(':'))
+                base = os.path.dirname(src.rsplit(':')[0])
                 v = os.path.join(base, os.path.expanduser(v))
         return v
 
--- a/mercurial/url.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/url.py	Sat Oct 15 14:30:50 2011 -0500
@@ -135,7 +135,7 @@
             orgsend(self, data)
     return _sendfile
 
-has_https = hasattr(urllib2, 'HTTPSHandler')
+has_https = util.safehasattr(urllib2, 'HTTPSHandler')
 if has_https:
     try:
         _create_connection = socket.create_connection
@@ -192,8 +192,8 @@
 # general transaction handler to support different ways to handle
 # HTTPS proxying before and after Python 2.6.3.
 def _generic_start_transaction(handler, h, req):
-    if hasattr(req, '_tunnel_host') and req._tunnel_host:
-        tunnel_host = req._tunnel_host
+    tunnel_host = getattr(req, '_tunnel_host', None)
+    if tunnel_host:
         if tunnel_host[:7] not in ['http://', 'https:/']:
             tunnel_host = 'https://' + tunnel_host
         new_tunnel = True
--- a/mercurial/util.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/util.py	Sat Oct 15 14:30:50 2011 -0500
@@ -19,6 +19,58 @@
 import os, time, calendar, textwrap, signal
 import imp, socket, urllib
 
+if os.name == 'nt':
+    import windows as platform
+else:
+    import posix as platform
+
+cachestat = platform.cachestat
+checkexec = platform.checkexec
+checklink = platform.checklink
+copymode = platform.copymode
+executablepath = platform.executablepath
+expandglobs = platform.expandglobs
+explainexit = platform.explainexit
+findexe = platform.findexe
+gethgcmd = platform.gethgcmd
+getuser = platform.getuser
+groupmembers = platform.groupmembers
+groupname = platform.groupname
+hidewindow = platform.hidewindow
+isexec = platform.isexec
+isowner = platform.isowner
+localpath = platform.localpath
+lookupreg = platform.lookupreg
+makedir = platform.makedir
+nlinks = platform.nlinks
+normpath = platform.normpath
+nulldev = platform.nulldev
+openhardlinks = platform.openhardlinks
+oslink = platform.oslink
+parsepatchoutput = platform.parsepatchoutput
+pconvert = platform.pconvert
+popen = platform.popen
+posixfile = platform.posixfile
+quotecommand = platform.quotecommand
+realpath = platform.realpath
+rename = platform.rename
+samedevice = platform.samedevice
+samefile = platform.samefile
+samestat = platform.samestat
+setbinary = platform.setbinary
+setflags = platform.setflags
+setsignalhandler = platform.setsignalhandler
+shellquote = platform.shellquote
+spawndetached = platform.spawndetached
+sshargs = platform.sshargs
+statfiles = platform.statfiles
+termwidth = platform.termwidth
+testpid = platform.testpid
+umask = platform.umask
+unlink = platform.unlink
+unlinkpath = platform.unlinkpath
+username = platform.username
+
 # Python compatibility
 
 def sha1(s):
@@ -307,8 +359,8 @@
     The code supports py2exe (most common, Windows only) and tools/freeze
     (portable, not much used).
     """
-    return (hasattr(sys, "frozen") or # new py2exe
-            hasattr(sys, "importers") or # old py2exe
+    return (safehasattr(sys, "frozen") or # new py2exe
+            safehasattr(sys, "importers") or # old py2exe
             imp.is_frozen("__main__")) # tools/freeze
 
 def hgexecutable():
@@ -318,10 +370,13 @@
     """
     if _hgexecutable is None:
         hg = os.environ.get('HG')
+        mainmod = sys.modules['__main__']
         if hg:
             _sethgexecutable(hg)
         elif mainfrozen():
             _sethgexecutable(sys.executable)
+        elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
+            _sethgexecutable(mainmod.__file__)
         else:
             exe = findexe('hg') or os.path.basename(sys.argv[0])
             _sethgexecutable(exe)
@@ -394,18 +449,6 @@
 
     return check
 
-def makedir(path, notindexed):
-    os.mkdir(path)
-
-def unlinkpath(f):
-    """unlink and remove the directory if it is empty"""
-    os.unlink(f)
-    # try removing directories that might now be empty
-    try:
-        os.removedirs(os.path.dirname(f))
-    except OSError:
-        pass
-
 def copyfile(src, dest):
     "copy a file, preserving mode and atime/mtime"
     if os.path.islink(src):
@@ -491,22 +534,10 @@
             return _("filename ends with '%s', which is not allowed "
                      "on Windows") % t
 
-def lookupreg(key, name=None, scope=None):
-    return None
-
-def hidewindow():
-    """Hide current shell window.
-
-    Used to hide the window opened when starting asynchronous
-    child process under Windows, unneeded on other systems.
-    """
-    pass
-
 if os.name == 'nt':
     checkosfilename = checkwinfilename
-    from windows import *
 else:
-    from posix import *
+    checkosfilename = platform.checkosfilename
 
 def makelock(info, pathname):
     try:
@@ -690,16 +721,7 @@
     # Temporary files are created with mode 0600, which is usually not
     # what we want.  If the original file already exists, just copy
     # its mode.  Otherwise, manually obey umask.
-    try:
-        st_mode = os.lstat(name).st_mode & 0777
-    except OSError, inst:
-        if inst.errno != errno.ENOENT:
-            raise
-        st_mode = createmode
-        if st_mode is None:
-            st_mode = ~umask
-        st_mode &= 0666
-    os.chmod(temp, st_mode)
+    copymode(name, temp, createmode)
     if emptyok:
         return temp
     try:
@@ -726,11 +748,10 @@
     '''writeable file object that atomically updates a file
 
     All writes will go to a temporary copy of the original file. Call
-    rename() when you are done writing, and atomictempfile will rename
-    the temporary copy to the original name, making the changes visible.
-
-    Unlike other file-like objects, close() discards your writes by
-    simply deleting the temporary file.
+    close() when you are done writing, and atomictempfile will rename
+    the temporary copy to the original name, making the changes
+    visible. If the object is destroyed without being closed, all your
+    writes are discarded.
     '''
     def __init__(self, name, mode='w+b', createmode=None):
         self.__name = name      # permanent name
@@ -742,12 +763,12 @@
         self.write = self._fp.write
         self.fileno = self._fp.fileno
 
-    def rename(self):
+    def close(self):
         if not self._fp.closed:
             self._fp.close()
             rename(self._tempname, localpath(self.__name))
 
-    def close(self):
+    def discard(self):
         if not self._fp.closed:
             try:
                 os.unlink(self._tempname)
@@ -756,24 +777,25 @@
             self._fp.close()
 
     def __del__(self):
-        if hasattr(self, '_fp'): # constructor actually did something
-            self.close()
+        if safehasattr(self, '_fp'): # constructor actually did something
+            self.discard()
 
 def makedirs(name, mode=None):
     """recursive directory creation with parent mode inheritance"""
-    parent = os.path.abspath(os.path.dirname(name))
     try:
         os.mkdir(name)
-        if mode is not None:
-            os.chmod(name, mode)
-        return
     except OSError, err:
         if err.errno == errno.EEXIST:
             return
-        if not name or parent == name or err.errno != errno.ENOENT:
+        if err.errno != errno.ENOENT or not name:
+            raise
+        parent = os.path.dirname(os.path.abspath(name))
+        if parent == name:
             raise
-    makedirs(parent, mode)
-    makedirs(name, mode)
+        makedirs(parent, mode)
+        os.mkdir(name)
+    if mode is not None:
+        os.chmod(name, mode)
 
 def readfile(path):
     fp = open(path, 'rb')
@@ -1303,8 +1325,9 @@
     def handler(signum, frame):
         terminated.add(os.wait())
     prevhandler = None
-    if hasattr(signal, 'SIGCHLD'):
-        prevhandler = signal.signal(signal.SIGCHLD, handler)
+    SIGCHLD = getattr(signal, 'SIGCHLD', None)
+    if SIGCHLD is not None:
+        prevhandler = signal.signal(SIGCHLD, handler)
     try:
         pid = spawndetached(args)
         while not condfn():
@@ -1648,8 +1671,10 @@
             self.user, self.passwd = user, passwd
         if not self.user:
             return (s, None)
-        # authinfo[1] is passed to urllib2 password manager, and its URIs
-        # must not contain credentials.
+        # authinfo[1] is passed to urllib2 password manager, and its
+        # URIs must not contain credentials. The host is passed in the
+        # URIs list because Python < 2.4.3 uses only that to search for
+        # a password.
         return (s, (None, (s, self.host),
                     self.user, self.passwd or ''))
 
--- a/mercurial/windows.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/windows.py	Sat Oct 15 14:30:50 2011 -0500
@@ -9,6 +9,22 @@
 import osutil
 import errno, msvcrt, os, re, sys
 
+import win32
+executablepath = win32.executablepath
+getuser = win32.getuser
+hidewindow = win32.hidewindow
+lookupreg = win32.lookupreg
+makedir = win32.makedir
+nlinks = win32.nlinks
+oslink = win32.oslink
+samedevice = win32.samedevice
+samefile = win32.samefile
+setsignalhandler = win32.setsignalhandler
+spawndetached = win32.spawndetached
+termwidth = win32.termwidth
+testpid = win32.testpid
+unlink = win32.unlink
+
 nulldev = 'NUL:'
 umask = 002
 
@@ -90,6 +106,9 @@
 def setflags(f, l, x):
     pass
 
+def copymode(src, dst, mode=None):
+    pass
+
 def checkexec(path):
     return False
 
@@ -99,8 +118,9 @@
 def setbinary(fd):
     # When run without console, pipes may expose invalid
     # fileno(), usually set to -1.
-    if hasattr(fd, 'fileno') and fd.fileno() >= 0:
-        msvcrt.setmode(fd.fileno(), os.O_BINARY)
+    fno = getattr(fd, 'fileno', None)
+    if fno is not None and fno() >= 0:
+        msvcrt.setmode(fno(), os.O_BINARY)
 
 def pconvert(path):
     return '/'.join(path.split(os.sep))
@@ -281,6 +301,14 @@
     # Don't support groups on Windows for now
     raise KeyError()
 
-from win32 import *
+def isexec(f):
+    return False
+
+class cachestat(object):
+    def __init__(self, path):
+        pass
+
+    def cacheable(self):
+        return False
 
 expandglobs = True
--- a/mercurial/wireproto.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/mercurial/wireproto.py	Sat Oct 15 14:30:50 2011 -0500
@@ -10,14 +10,13 @@
 from node import bin, hex
 import changegroup as changegroupmod
 import repo, error, encoding, util, store
-import pushkey as pushkeymod
 
 # abstract batching support
 
 class future(object):
     '''placeholder for a value to be set later'''
     def set(self, value):
-        if hasattr(self, 'value'):
+        if util.safehasattr(self, 'value'):
             raise error.RepoError("future is already set")
         self.value = value
 
@@ -58,8 +57,9 @@
         req, rsp = [], []
         for name, args, opts, resref in self.calls:
             mtd = getattr(self.remote, name)
-            if hasattr(mtd, 'batchable'):
-                batchable = getattr(mtd, 'batchable')(mtd.im_self, *args, **opts)
+            batchablefn = getattr(mtd, 'batchable', None)
+            if batchablefn is not None:
+                batchable = batchablefn(mtd.im_self, *args, **opts)
                 encargsorres, encresref = batchable.next()
                 if encresref:
                     req.append((name, encargsorres,))
@@ -334,6 +334,10 @@
     def __init__(self, res):
         self.res = res
 
+class ooberror(object):
+    def __init__(self, message):
+        self.message = message
+
 def dispatch(repo, proto, command):
     func, spec = commands[command]
     args = proto.getargs(spec)
@@ -375,6 +379,8 @@
             result = func(repo, proto, *[data[k] for k in keys])
         else:
             result = func(repo, proto)
+        if isinstance(result, ooberror):
+            return result
         res.append(escapearg(result))
     return ';'.join(res)
 
@@ -454,7 +460,7 @@
     return "capabilities: %s\n" % (capabilities(repo, proto))
 
 def listkeys(repo, proto, namespace):
-    d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
+    d = repo.listkeys(encoding.tolocal(namespace)).items()
     t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
                    for k, v in d])
     return t
@@ -484,9 +490,8 @@
     else:
         new = encoding.tolocal(new) # normal path
 
-    r = pushkeymod.push(repo,
-                        encoding.tolocal(namespace), encoding.tolocal(key),
-                        encoding.tolocal(old), new)
+    r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
+                     encoding.tolocal(old), new)
     return '%s\n' % int(r)
 
 def _allowstream(ui):
--- a/setup.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/setup.py	Sat Oct 15 14:30:50 2011 -0500
@@ -5,7 +5,7 @@
 # 'python setup.py --help' for more options
 
 import sys, platform
-if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
@@ -342,7 +342,8 @@
 
 packages = ['mercurial', 'mercurial.hgweb',
             'mercurial.httpclient', 'mercurial.httpclient.tests',
-            'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf']
+            'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
+            'hgext.largefiles']
 
 pymodules = []
 
Binary file tests/bundles/rebase-revset.hg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/heredoctest.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,13 @@
+import doctest, tempfile, os, sys
+
+if __name__ == "__main__":
+    fd, name = tempfile.mkstemp(suffix='hg-tst')
+
+    try:
+        os.write(fd, sys.stdin.read())
+        os.close(fd)
+        failures, _ = doctest.testfile(name, module_relative=False)
+        if failures:
+            sys.exit(1)
+    finally:
+        os.remove(name)
--- a/tests/hghave	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/hghave	Sat Oct 15 14:30:50 2011 -0500
@@ -101,6 +101,16 @@
 def has_fifo():
     return hasattr(os, "mkfifo")
 
+def has_cacheable_fs():
+    from mercurial import util
+
+    fd, path = tempfile.mkstemp(prefix=tempprefix)
+    os.close(fd)
+    try:
+        return util.cachestat(path).cacheable()
+    finally:
+        os.remove(path)
+
 def has_lsprof():
     try:
         import _lsprof
@@ -200,6 +210,7 @@
     "baz": (has_baz, "GNU Arch baz client"),
     "bzr": (has_bzr, "Canonical's Bazaar client"),
     "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
+    "cacheable": (has_cacheable_fs, "cacheable filesystem"),
     "cvs": (has_cvs, "cvs client/server"),
     "darcs": (has_darcs, "darcs client"),
     "docutils": (has_docutils, "Docutils text processing library"),
--- a/tests/run-tests.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/run-tests.py	Sat Oct 15 14:30:50 2011 -0500
@@ -340,10 +340,7 @@
     """Terminate subprocess (with fallback for Python versions < 2.6)"""
     vlog('# Terminating process %d' % proc.pid)
     try:
-        if hasattr(proc, 'terminate'):
-            proc.terminate()
-        else:
-            os.kill(proc.pid, signal.SIGTERM)
+        getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
     except OSError:
         pass
 
@@ -524,6 +521,26 @@
 def stringescape(s):
     return escapesub(escapef, s)
 
+def transformtst(lines):
+    inblock = False
+    for l in lines:
+        if inblock:
+            if l.startswith('  $ ') or not l.startswith('  '):
+                inblock = False
+                yield '  > EOF\n'
+                yield l
+            else:
+                yield '  > ' + l[2:]
+        else:
+            if l.startswith('  >>> '):
+                inblock = True
+                yield '  $ %s -m heredoctest <<EOF\n' % PYTHON
+                yield '  > ' + l[2:]
+            else:
+                yield l
+    if inblock:
+        yield '  > EOF\n'
+
 def tsttest(test, wd, options, replacements):
     t = open(test)
     out = []
@@ -533,7 +550,7 @@
     pos = prepos = -1
     after = {}
     expected = {}
-    for n, l in enumerate(t):
+    for n, l in enumerate(transformtst(t)):
         if not l.endswith('\n'):
             l += '\n'
         if l.startswith('  $ '): # commands
@@ -726,6 +743,7 @@
                     rename(testpath + ".err", testpath)
                 else:
                     rename(testpath + ".err", testpath + ".out")
+                result('p', test)
                 return
         result('f', (test, msg))
 
@@ -835,7 +853,7 @@
         refout = None                   # to match "out is None"
     elif os.path.exists(ref):
         f = open(ref, "r")
-        refout = splitnewlines(f.read())
+        refout = list(transformtst(splitnewlines(f.read())))
         f.close()
     else:
         refout = []
--- a/tests/sitecustomize.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/sitecustomize.py	Sat Oct 15 14:30:50 2011 -0500
@@ -1,6 +1,5 @@
 try:
     import coverage
-    if hasattr(coverage, 'process_startup'):
-        coverage.process_startup()
+    getattr(coverage, 'process_startup', lambda: None)()
 except ImportError:
     pass
--- a/tests/test-acl.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-acl.t	Sat Oct 15 14:30:50 2011 -0500
@@ -121,7 +121,6 @@
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -179,7 +178,6 @@
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -234,20 +232,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -302,16 +300,16 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 0 entries for user fred
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: user fred not allowed on foo/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset ef1ea85a6374
+  abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   no rollback information available
   0:6675d58eff77
   
@@ -367,20 +365,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: user fred not allowed on quux/file.py
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset 911600dab2ae
+  abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
   no rollback information available
   0:6675d58eff77
   
@@ -437,16 +435,16 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "barney"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 0 entries for user barney
   acl: acl.deny enabled, 0 entries for user barney
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: user barney not allowed on foo/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+  error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset ef1ea85a6374
+  abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   no rollback information available
   0:6675d58eff77
   
@@ -504,20 +502,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny enabled, 1 entries for user fred
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: user fred not allowed on quux/file.py
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset 911600dab2ae
+  abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
   no rollback information available
   0:6675d58eff77
   
@@ -576,18 +574,18 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny enabled, 2 entries for user fred
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: user fred denied on foo/Bar/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset f9cafe1212c8
+  abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   no rollback information available
   0:6675d58eff77
   
@@ -645,16 +643,16 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "barney"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 0 entries for user barney
   acl: acl.deny enabled, 0 entries for user barney
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: user barney not allowed on foo/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+  error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset ef1ea85a6374
+  abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
   no rollback information available
   0:6675d58eff77
   
@@ -716,20 +714,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "barney"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user barney
   acl: acl.deny enabled, 0 entries for user barney
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -791,20 +789,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "wilma"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user wilma
   acl: acl.deny enabled, 0 entries for user wilma
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: user wilma not allowed on quux/file.py
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+  error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset 911600dab2ae
+  abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
   no rollback information available
   0:6675d58eff77
   
@@ -869,6 +867,7 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "barney"
   error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
   transaction abort!
   rollback completed
@@ -941,20 +940,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "betty"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user betty
   acl: acl.deny enabled, 0 entries for user betty
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: user betty not allowed on quux/file.py
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+  error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset 911600dab2ae
+  abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
   no rollback information available
   0:6675d58eff77
   
@@ -1025,20 +1024,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "barney"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user barney
   acl: acl.deny enabled, 0 entries for user barney
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -1101,20 +1100,20 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -1173,18 +1172,18 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny enabled, 1 entries for user fred
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: user fred denied on foo/Bar/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset f9cafe1212c8
+  abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   no rollback information available
   0:6675d58eff77
   
@@ -1247,21 +1246,21 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: "group1" not defined in [acl.groups]
   acl: acl.allow enabled, 1 entries for user fred
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 0 (undo push)
-  working directory now based on revision 0
   0:6675d58eff77
   
 
@@ -1320,6 +1319,7 @@
   files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: "group1" not defined in [acl.groups]
@@ -1327,13 +1327,12 @@
   acl: "group1" not defined in [acl.groups]
   acl: acl.deny enabled, 1 entries for user fred
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: user fred denied on foo/Bar/file.txt
-  error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
+  error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   transaction abort!
   rollback completed
-  abort: acl: access denied for changeset f9cafe1212c8
+  abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
   no rollback information available
   0:6675d58eff77
   
@@ -1441,22 +1440,22 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "astro"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   acl: branch access granted: "e8fc755d4d82" on branch "foobar"
-  acl: allowing changeset e8fc755d4d82
+  acl: path access granted: "e8fc755d4d82"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 2 (undo push)
-  working directory now based on revision 2
   2:fb35475503ef
   
 
@@ -1521,16 +1520,17 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "astro"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches enabled, 1 entries for user astro
   acl: acl.allow not enabled
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
   transaction abort!
   rollback completed
@@ -1598,6 +1598,7 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "astro"
   acl: acl.allow.branches enabled, 0 entries for user astro
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
@@ -1671,6 +1672,7 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "astro"
   acl: acl.allow.branches enabled, 0 entries for user astro
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
@@ -1738,22 +1740,22 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "george"
   acl: acl.allow.branches enabled, 1 entries for user george
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   acl: branch access granted: "e8fc755d4d82" on branch "foobar"
-  acl: allowing changeset e8fc755d4d82
+  acl: path access granted: "e8fc755d4d82"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 2 (undo push)
-  working directory now based on revision 2
   2:fb35475503ef
   
 
@@ -1823,22 +1825,22 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "george"
   acl: acl.allow.branches enabled, 1 entries for user george
   acl: acl.deny.branches not enabled
   acl: acl.allow not enabled
   acl: acl.deny not enabled
   acl: branch access granted: "ef1ea85a6374" on branch "default"
-  acl: allowing changeset ef1ea85a6374
+  acl: path access granted: "ef1ea85a6374"
   acl: branch access granted: "f9cafe1212c8" on branch "default"
-  acl: allowing changeset f9cafe1212c8
+  acl: path access granted: "f9cafe1212c8"
   acl: branch access granted: "911600dab2ae" on branch "default"
-  acl: allowing changeset 911600dab2ae
+  acl: path access granted: "911600dab2ae"
   acl: branch access granted: "e8fc755d4d82" on branch "foobar"
-  acl: allowing changeset e8fc755d4d82
+  acl: path access granted: "e8fc755d4d82"
   updating the branch cache
   checking for updated bookmarks
   repository tip rolled back to revision 2 (undo push)
-  working directory now based on revision 2
   2:fb35475503ef
   
 Branch acl conflicting deny
@@ -1907,6 +1909,7 @@
   files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "george"
   acl: acl.allow.branches not enabled
   acl: acl.deny.branches enabled, 1 entries for user george
   acl: acl.allow not enabled
--- a/tests/test-alias.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-alias.t	Sat Oct 15 14:30:50 2011 -0500
@@ -4,6 +4,8 @@
   > graphlog=
   > 
   > [alias]
+  > # should clobber ci but not commit (issue2993)
+  > ci = version
   > myinit = init
   > cleanstatus = status -c
   > unknown = bargle
@@ -113,7 +115,7 @@
   no rollback information available
 
   $ echo foo > foo
-  $ hg ci -Amfoo
+  $ hg commit -Amfoo
   adding foo
 
 
@@ -195,7 +197,7 @@
   $ hg echo2 foo
   
   $ echo bar > bar
-  $ hg ci -qA -m bar
+  $ hg commit -qA -m bar
   $ hg count .
   1
   $ hg count 'branch(default)'
@@ -251,7 +253,7 @@
   $ hg --cwd .. count 'branch(default)'
   2
   $ hg echo --cwd ..
-  --cwd ..
+  
 
 
 repo specific shell aliases
@@ -305,7 +307,7 @@
 
   $ hg rt foo
   hg rt: invalid arguments
-  hg rt 
+  hg rt
   
   alias for: hg root
   
--- a/tests/test-atomictempfile.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-atomictempfile.py	Sat Oct 15 14:30:50 2011 -0500
@@ -12,22 +12,21 @@
     assert basename in glob.glob('.foo-*')
 
     file.write('argh\n')
-    file.rename()
+    file.close()
 
     assert os.path.isfile('foo')
     assert basename not in glob.glob('.foo-*')
     print 'OK'
 
-# close() removes the temp file but does not make the write
-# permanent -- essentially discards your work (WTF?!)
-def test2_close():
+# discard() removes the temp file without making the write permanent
+def test2_discard():
     if os.path.exists('foo'):
         os.remove('foo')
     file = atomictempfile('foo')
     (dir, basename) = os.path.split(file._tempname)
 
     file.write('yo\n')
-    file.close()
+    file.discard()
 
     assert not os.path.isfile('foo')
     assert basename not in os.listdir('.')
@@ -45,5 +44,5 @@
 
 if __name__ == '__main__':
     test1_simple()
-    test2_close()
+    test2_discard()
     test3_oops()
--- a/tests/test-backout.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-backout.t	Sat Oct 15 14:30:50 2011 -0500
@@ -182,7 +182,7 @@
 backout of merge should fail
 
   $ hg backout 4
-  abort: cannot backout a merge changeset without --parent
+  abort: cannot backout a merge changeset
   [255]
 
 backout of merge with bad parent should fail
--- a/tests/test-bisect.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-bisect.t	Sat Oct 15 14:30:50 2011 -0500
@@ -377,7 +377,7 @@
   date:        Thu Jan 01 00:00:06 1970 +0000
   summary:     msg 6
   
-  $ hg log -r "bisected(good)"
+  $ hg log -r "bisect(good)"
   changeset:   0:b99c7b9c8e11
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -388,13 +388,13 @@
   date:        Thu Jan 01 00:00:05 1970 +0000
   summary:     msg 5
   
-  $ hg log -r "bisected(bad)"
+  $ hg log -r "bisect(bad)"
   changeset:   6:a3d5c6fdf0d3
   user:        test
   date:        Thu Jan 01 00:00:06 1970 +0000
   summary:     msg 6
   
-  $ hg log -r "bisected(skip)"
+  $ hg log -r "bisect(skip)"
   changeset:   1:5cd978ea5149
   user:        test
   date:        Thu Jan 01 00:00:01 1970 +0000
@@ -416,6 +416,15 @@
   summary:     msg 4
   
 
+test legacy bisected() keyword
+
+  $ hg log -r "bisected(bad)"
+  changeset:   6:a3d5c6fdf0d3
+  user:        test
+  date:        Thu Jan 01 00:00:06 1970 +0000
+  summary:     msg 6
+  
+
   $ set +e
 
 test invalid command
--- a/tests/test-bisect2.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-bisect2.t	Sat Oct 15 14:30:50 2011 -0500
@@ -252,6 +252,25 @@
   $ hg bisect -b 17   # -> update to rev 6
   Testing changeset 6:a214d5d3811a (15 changesets remaining, ~3 tests)
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  17:228c06deef46
+  $ hg log -q -r 'bisect(untested)'
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  $ hg log -q -r 'bisect(ignored)'
   $ hg bisect -g      # -> update to rev 13
   Testing changeset 13:b0a32c86eb31 (9 changesets remaining, ~3 tests)
   3 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -271,6 +290,58 @@
   date:        Thu Jan 01 00:00:09 1970 +0000
   summary:     9
   
+  $ hg log -q -r 'bisect(range)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
+  $ hg log -q -r 'bisect(untested)'
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  $ hg log -q -r 'bisect(goods)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  $ hg log -q -r 'bisect(bads)'
+  9:3c77083deb4a
+  10:429fcd26f52d
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
 
 complex bisect test 2  # first good rev is 13
 
@@ -282,9 +353,31 @@
   $ hg bisect -s      # -> update to rev 10
   Testing changeset 10:429fcd26f52d (13 changesets remaining, ~3 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  6:a214d5d3811a
+  18:d42e18c7bc9b
   $ hg bisect -b      # -> update to rev 12
   Testing changeset 12:9f259202bbe7 (5 changesets remaining, ~2 tests)
   3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  18:d42e18c7bc9b
+  $ hg log -q -r 'bisect(untested)'
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
   $ hg bisect -b      # -> update to rev 13
   Testing changeset 13:b0a32c86eb31 (3 changesets remaining, ~1 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -295,6 +388,21 @@
   date:        Thu Jan 01 00:00:13 1970 +0000
   summary:     13
   
+  $ hg log -q -r 'bisect(range)'
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  18:d42e18c7bc9b
 
 complex bisect test 3
 
@@ -306,6 +414,11 @@
   $ hg bisect -b 16   # -> update to rev 6
   Testing changeset 6:a214d5d3811a (13 changesets remaining, ~3 tests)
   2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  16:609d82a7ebae
+  17:228c06deef46
   $ hg bisect -g      # -> update to rev 13
   Testing changeset 13:b0a32c86eb31 (8 changesets remaining, ~3 tests)
   3 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -315,12 +428,25 @@
   $ hg bisect -s      # -> update to rev 12
   Testing changeset 12:9f259202bbe7 (8 changesets remaining, ~3 tests)
   3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  10:429fcd26f52d
+  13:b0a32c86eb31
+  16:609d82a7ebae
+  17:228c06deef46
   $ hg bisect -g      # -> update to rev 9
   Testing changeset 9:3c77083deb4a (5 changesets remaining, ~2 tests)
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg bisect -s      # -> update to rev 15
   Testing changeset 15:857b178a7cf3 (5 changesets remaining, ~2 tests)
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(ignored)'
   $ hg bisect -b
   Due to skipped revisions, the first bad revision could be any of:
   changeset:   9:3c77083deb4a
@@ -347,6 +473,22 @@
   date:        Thu Jan 01 00:00:15 1970 +0000
   summary:     merge 10,13
   
+  $ hg log -q -r 'bisect(range)'
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  $ hg log -q -r 'bisect(ignored)'
 
 complex bisect test 4
 
@@ -364,9 +506,40 @@
   $ hg bisect -b      # -> update to rev 15
   Testing changeset 15:857b178a7cf3 (3 changesets remaining, ~1 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  17:228c06deef46
   $ hg bisect -s      # -> update to rev 16
   Testing changeset 16:609d82a7ebae (3 changesets remaining, ~1 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  17:228c06deef46
   $ hg bisect -s
   Due to skipped revisions, the first good revision could be any of:
   changeset:   15:857b178a7cf3
@@ -386,6 +559,33 @@
   date:        Thu Jan 01 00:00:17 1970 +0000
   summary:     17
   
+  $ hg log -q -r 'bisect(range)'
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
 
 test unrelated revs:
 
@@ -394,6 +594,15 @@
   $ hg bisect -g 14
   abort: starting revisions are not directly related
   [255]
+  $ hg log -q -r 'bisect(range)'
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  7:50c76098bbf2
+  14:faa450606157
   $ hg bisect --reset
 
 end at merge: 17 bad, 11 good (but 9 is first bad)
@@ -403,6 +612,14 @@
   $ hg bisect -g 11
   Testing changeset 13:b0a32c86eb31 (5 changesets remaining, ~2 tests)
   3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(ignored)'
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  9:3c77083deb4a
+  10:429fcd26f52d
   $ hg bisect -g
   Testing changeset 15:857b178a7cf3 (3 changesets remaining, ~1 tests)
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -418,12 +635,69 @@
   Not all ancestors of this changeset have been checked.
   Use bisect --extend to continue the bisection from
   the common ancestor, dab8161ac8fc.
+  $ hg log -q -r 'bisect(range)'
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  8:dab8161ac8fc
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
+  $ hg log -q -r 'bisect(untested)'
+  $ hg log -q -r 'bisect(ignored)'
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  9:3c77083deb4a
+  10:429fcd26f52d
   $ hg bisect --extend
   Extending search to changeset 8:dab8161ac8fc
   2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(untested)'
+  $ hg log -q -r 'bisect(ignored)'
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  9:3c77083deb4a
+  10:429fcd26f52d
   $ hg bisect -g # dab8161ac8fc
   Testing changeset 9:3c77083deb4a (3 changesets remaining, ~1 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(untested)'
+  9:3c77083deb4a
+  10:429fcd26f52d
+  $ hg log -q -r 'bisect(ignored)'
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  $ hg log -q -r 'bisect(goods)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  8:dab8161ac8fc
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  $ hg log -q -r 'bisect(bads)'
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
   $ hg bisect -b
   The first bad revision is:
   changeset:   9:3c77083deb4a
@@ -431,3 +705,91 @@
   date:        Thu Jan 01 00:00:09 1970 +0000
   summary:     9
   
+  $ hg log -q -r 'bisect(range)'
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  8:dab8161ac8fc
+  9:3c77083deb4a
+  10:429fcd26f52d
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
+  $ hg log -q -r 'bisect(untested)'
+  $ hg log -q -r 'bisect(ignored)'
+  2:051e12f87bf1
+  3:0950834f0a9c
+  4:5c668c22234f
+  5:385a529b6670
+  6:a214d5d3811a
+  $ hg log -q -r 'bisect(goods)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  8:dab8161ac8fc
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  $ hg log -q -r 'bisect(bads)'
+  9:3c77083deb4a
+  10:429fcd26f52d
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
+
+user adds irrelevant but consistent information (here: -g 2) to bisect state
+
+  $ hg bisect -r
+  $ hg bisect -b 13
+  $ hg bisect -g 8
+  Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(untested)'
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  $ hg bisect -g 2
+  Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -q -r 'bisect(untested)'
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  $ hg bisect -b
+  The first bad revision is:
+  changeset:   11:82ca6f06eccd
+  parent:      8:dab8161ac8fc
+  user:        test
+  date:        Thu Jan 01 00:00:11 1970 +0000
+  summary:     11
+  
+  $ hg log -q -r 'bisect(range)'
+  8:dab8161ac8fc
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  $ hg log -q -r 'bisect(pruned)'
+  0:33b1f9bc8bc5
+  1:4ca5088da217
+  2:051e12f87bf1
+  8:dab8161ac8fc
+  11:82ca6f06eccd
+  12:9f259202bbe7
+  13:b0a32c86eb31
+  14:faa450606157
+  15:857b178a7cf3
+  16:609d82a7ebae
+  17:228c06deef46
+  18:d42e18c7bc9b
+  $ hg log -q -r 'bisect(untested)'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bisect3.t	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,232 @@
+# Here we create a simple DAG which has just enough of the required
+# topology to test all the bisection status labels:
+#
+#           13--14
+#          /
+#   0--1--2--3---------9--10--11--12
+#       \             /
+#        4--5--6--7--8
+
+
+  $ hg init
+
+  $ echo '0' >a
+  $ hg add a
+  $ hg ci -u test -d '0 0' -m '0'
+  $ echo '1' >a
+  $ hg ci -u test -d '0 1' -m '1'
+
+branch 2-3
+
+  $ echo '2' >b
+  $ hg add b
+  $ hg ci -u test -d '0 2' -m '2'
+  $ echo '3' >b
+  $ hg ci -u test -d '0 3' -m '3'
+
+branch 4-8
+
+  $ hg up -r 1
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo '4' >c
+  $ hg add c
+  $ hg ci -u test -d '0 4' -m '4'
+  created new head
+  $ echo '5' >c
+  $ hg ci -u test -d '0 5' -m '5'
+  $ echo '6' >c
+  $ hg ci -u test -d '0 6' -m '6'
+  $ echo '7' >c
+  $ hg ci -u test -d '0 7' -m '7'
+  $ echo '8' >c
+  $ hg ci -u test -d '0 8' -m '8'
+
+merge
+
+  $ hg merge -r 3
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -u test -d '0 9' -m '9=8+3'
+
+  $ echo '10' >a
+  $ hg ci -u test -d '0 10' -m '10'
+  $ echo '11' >a
+  $ hg ci -u test -d '0 11' -m '11'
+  $ echo '12' >a
+  $ hg ci -u test -d '0 12' -m '12'
+
+unrelated branch
+
+  $ hg up -r 3
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo '13' >d
+  $ hg add d
+  $ hg ci -u test -d '0 13' -m '13'
+  created new head
+  $ echo '14' >d
+  $ hg ci -u test -d '0 14' -m '14'
+
+mark changesets
+
+  $ hg bisect --reset
+  $ hg bisect --good 4
+  $ hg bisect --good 6
+  $ hg bisect --bad 12
+  Testing changeset 9:8bcbdb072033 (6 changesets remaining, ~2 tests)
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg bisect --bad 10
+  Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg bisect --skip 7
+  Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+test template
+
+  $ hg log --template '{rev}:{node|short} {bisect}\n'
+  14:cecd84203acc 
+  13:86f7c8cdb6df 
+  12:a76089b5f47c bad
+  11:5c3eb122d29c bad (implicit)
+  10:b097cef2be03 bad
+  9:8bcbdb072033 untested
+  8:3cd112f87d77 untested
+  7:577e237a73bd skipped
+  6:e597fa2707c5 good
+  5:b9cea37a76bc good (implicit)
+  4:da6b357259d7 good
+  3:e7f031aee8ca ignored
+  2:b1ad1b6bcc5c ignored
+  1:37f42ae8b45e good (implicit)
+  0:b4e73ffab476 good (implicit)
+  $ hg log --template '{bisect|shortbisect} {rev}:{node|short}\n'
+    14:cecd84203acc
+    13:86f7c8cdb6df
+  B 12:a76089b5f47c
+  B 11:5c3eb122d29c
+  B 10:b097cef2be03
+  U 9:8bcbdb072033
+  U 8:3cd112f87d77
+  S 7:577e237a73bd
+  G 6:e597fa2707c5
+  G 5:b9cea37a76bc
+  G 4:da6b357259d7
+  I 3:e7f031aee8ca
+  I 2:b1ad1b6bcc5c
+  G 1:37f42ae8b45e
+  G 0:b4e73ffab476
+
+test style
+
+  $ hg log --style bisect
+  changeset:   14:cecd84203acc
+  bisect:      
+  tag:         tip
+  user:        test
+  date:        Wed Dec 31 23:59:46 1969 -0000
+  summary:     14
+  
+  changeset:   13:86f7c8cdb6df
+  bisect:      
+  parent:      3:e7f031aee8ca
+  user:        test
+  date:        Wed Dec 31 23:59:47 1969 -0000
+  summary:     13
+  
+  changeset:   12:a76089b5f47c
+  bisect:      bad
+  user:        test
+  date:        Wed Dec 31 23:59:48 1969 -0000
+  summary:     12
+  
+  changeset:   11:5c3eb122d29c
+  bisect:      bad (implicit)
+  user:        test
+  date:        Wed Dec 31 23:59:49 1969 -0000
+  summary:     11
+  
+  changeset:   10:b097cef2be03
+  bisect:      bad
+  user:        test
+  date:        Wed Dec 31 23:59:50 1969 -0000
+  summary:     10
+  
+  changeset:   9:8bcbdb072033
+  bisect:      untested
+  parent:      8:3cd112f87d77
+  parent:      3:e7f031aee8ca
+  user:        test
+  date:        Wed Dec 31 23:59:51 1969 -0000
+  summary:     9=8+3
+  
+  changeset:   8:3cd112f87d77
+  bisect:      untested
+  user:        test
+  date:        Wed Dec 31 23:59:52 1969 -0000
+  summary:     8
+  
+  changeset:   7:577e237a73bd
+  bisect:      skipped
+  user:        test
+  date:        Wed Dec 31 23:59:53 1969 -0000
+  summary:     7
+  
+  changeset:   6:e597fa2707c5
+  bisect:      good
+  user:        test
+  date:        Wed Dec 31 23:59:54 1969 -0000
+  summary:     6
+  
+  changeset:   5:b9cea37a76bc
+  bisect:      good (implicit)
+  user:        test
+  date:        Wed Dec 31 23:59:55 1969 -0000
+  summary:     5
+  
+  changeset:   4:da6b357259d7
+  bisect:      good
+  parent:      1:37f42ae8b45e
+  user:        test
+  date:        Wed Dec 31 23:59:56 1969 -0000
+  summary:     4
+  
+  changeset:   3:e7f031aee8ca
+  bisect:      ignored
+  user:        test
+  date:        Wed Dec 31 23:59:57 1969 -0000
+  summary:     3
+  
+  changeset:   2:b1ad1b6bcc5c
+  bisect:      ignored
+  user:        test
+  date:        Wed Dec 31 23:59:58 1969 -0000
+  summary:     2
+  
+  changeset:   1:37f42ae8b45e
+  bisect:      good (implicit)
+  user:        test
+  date:        Wed Dec 31 23:59:59 1969 -0000
+  summary:     1
+  
+  changeset:   0:b4e73ffab476
+  bisect:      good (implicit)
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     0
+  
+  $ hg log --quiet --style bisect
+    14:cecd84203acc
+    13:86f7c8cdb6df
+  B 12:a76089b5f47c
+  B 11:5c3eb122d29c
+  B 10:b097cef2be03
+  U 9:8bcbdb072033
+  U 8:3cd112f87d77
+  S 7:577e237a73bd
+  G 6:e597fa2707c5
+  G 5:b9cea37a76bc
+  G 4:da6b357259d7
+  I 3:e7f031aee8ca
+  I 2:b1ad1b6bcc5c
+  G 1:37f42ae8b45e
+  G 0:b4e73ffab476
--- a/tests/test-bookmarks.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-bookmarks.t	Sat Oct 15 14:30:50 2011 -0500
@@ -239,9 +239,10 @@
 test summary
 
   $ hg summary
-  parent: 2:db815d6d32e6 tip Y Z x  y
+  parent: 2:db815d6d32e6 tip
    2
   branch: default
+  bookmarks: *Z Y x  y
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
 
@@ -342,3 +343,19 @@
    * Z                         3:125c9a1d6df6
      x  y                      2:db815d6d32e6
 
+test wrongly formated bookmark
+
+  $ echo '' >> .hg/bookmarks
+  $ hg bookmarks
+     X2                        1:925d80f479bb
+     Y                         2:db815d6d32e6
+   * Z                         3:125c9a1d6df6
+     x  y                      2:db815d6d32e6
+  $ echo "Ican'thasformatedlines" >> .hg/bookmarks
+  $ hg bookmarks
+  malformed line in .hg/bookmarks: "Ican'thasformatedlines"
+     X2                        1:925d80f479bb
+     Y                         2:db815d6d32e6
+   * Z                         3:125c9a1d6df6
+     x  y                      2:db815d6d32e6
+
--- a/tests/test-bundle-r.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-bundle-r.t	Sat Oct 15 14:30:50 2011 -0500
@@ -154,7 +154,6 @@
   4 files, 9 changesets, 7 total revisions
   $ hg rollback
   repository tip rolled back to revision 4 (undo pull)
-  working directory now based on revision -1
   $ cd ..
 
 should fail
@@ -232,7 +231,6 @@
   4 files, 9 changesets, 7 total revisions
   $ hg rollback
   repository tip rolled back to revision 2 (undo unbundle)
-  working directory now based on revision 2
 
 revision 2
 
@@ -257,7 +255,6 @@
   2 files, 5 changesets, 5 total revisions
   $ hg rollback
   repository tip rolled back to revision 2 (undo unbundle)
-  working directory now based on revision 2
   $ hg unbundle ../test-bundle-branch2.hg
   adding changesets
   adding manifests
@@ -277,7 +274,6 @@
   3 files, 7 changesets, 6 total revisions
   $ hg rollback
   repository tip rolled back to revision 2 (undo unbundle)
-  working directory now based on revision 2
   $ hg unbundle ../test-bundle-cset-7.hg
   adding changesets
   adding manifests
--- a/tests/test-bundle.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-bundle.t	Sat Oct 15 14:30:50 2011 -0500
@@ -90,7 +90,6 @@
 
   $ hg -R empty rollback
   repository tip rolled back to revision -1 (undo pull)
-  working directory now based on revision -1
 
 Pull full.hg into empty again (using --cwd)
 
@@ -121,7 +120,6 @@
 
   $ hg -R empty rollback
   repository tip rolled back to revision -1 (undo pull)
-  working directory now based on revision -1
 
 Pull full.hg into empty again (using -R)
 
@@ -219,7 +217,6 @@
 
   $ hg rollback
   repository tip rolled back to revision -1 (undo pull)
-  working directory now based on revision -1
   $ cd ..
 
 Log -R bundle:empty+full.hg
--- a/tests/test-check-pyflakes.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-check-pyflakes.t	Sat Oct 15 14:30:50 2011 -0500
@@ -1,14 +1,6 @@
   $ "$TESTDIR/hghave" pyflakes || exit 80
   $ cd $(dirname $TESTDIR)
   $ pyflakes mercurial hgext 2>&1 | $TESTDIR/filterpyflakes.py
-  mercurial/hgweb/server.py:*: 'activeCount' imported but unused (glob)
-  mercurial/commands.py:*: 'base85' imported but unused (glob)
-  mercurial/commands.py:*: 'bdiff' imported but unused (glob)
-  mercurial/commands.py:*: 'mpatch' imported but unused (glob)
-  mercurial/commands.py:*: 'osutil' imported but unused (glob)
   hgext/inotify/linux/__init__.py:*: 'from _inotify import *' used; unable to detect undefined names (glob)
-  mercurial/util.py:*: 'from posix import *' used; unable to detect undefined names (glob)
-  mercurial/windows.py:*: 'from win32 import *' used; unable to detect undefined names (glob)
-  mercurial/util.py:*: 'from windows import *' used; unable to detect undefined names (glob)
   
 
--- a/tests/test-commandserver.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-commandserver.py	Sat Oct 15 14:30:50 2011 -0500
@@ -154,6 +154,30 @@
                         'hooks.pre-identify=python:test-commandserver.hook', 'id'],
                input=cStringIO.StringIO('some input'))
 
+def outsidechanges(server):
+    readchannel(server)
+    os.system('echo a >> a && hg ci -Am2')
+    runcommand(server, ['tip'])
+
+def bookmarks(server):
+    readchannel(server)
+    runcommand(server, ['bookmarks'])
+
+    # changes .hg/bookmarks
+    os.system('hg bookmark -i bm1')
+    os.system('hg bookmark -i bm2')
+    runcommand(server, ['bookmarks'])
+
+    # changes .hg/bookmarks.current
+    os.system('hg upd bm1 -q')
+    runcommand(server, ['bookmarks'])
+
+def tagscache(server):
+    readchannel(server)
+    runcommand(server, ['id', '-t', '-r', '0'])
+    os.system('hg tag -r 0 foo')
+    runcommand(server, ['id', '-t', '-r', '0'])
+
 if __name__ == '__main__':
     os.system('hg init')
 
@@ -169,3 +193,6 @@
     hgrc.close()
     check(localhgrc)
     check(hookoutput)
+    check(outsidechanges)
+    check(bookmarks)
+    check(tagscache)
--- a/tests/test-commandserver.py.out	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-commandserver.py.out	Sat Oct 15 14:30:50 2011 -0500
@@ -52,3 +52,16 @@
 hook talking
 now try to read something: 'some input'
 eff892de26ec tip
+changeset:   1:d3a0a68be6de
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     2
+
+no bookmarks set
+   bm1                       1:d3a0a68be6de
+   bm2                       1:d3a0a68be6de
+ * bm1                       1:d3a0a68be6de
+   bm2                       1:d3a0a68be6de
+
+foo
--- a/tests/test-convert-authormap.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-authormap.t	Sat Oct 15 14:30:50 2011 -0500
@@ -27,7 +27,7 @@
   sorting...
   converting...
   0 foo
-  Writing author map file new/.hg/authormap
+  Writing author map file $TESTTMP/new/.hg/authormap
   $ cat new/.hg/authormap
   user name=Long User Name
   $ hg -Rnew log
@@ -44,7 +44,7 @@
   $ hg init new
   $ mv authormap.txt new/.hg/authormap
   $ hg convert orig new
-  Ignoring bad line in author map file new/.hg/authormap: this line is ignored
+  Ignoring bad line in author map file $TESTTMP/new/.hg/authormap: this line is ignored
   scanning source...
   sorting...
   converting...
--- a/tests/test-convert-cvs.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-cvs.t	Sat Oct 15 14:30:50 2011 -0500
@@ -112,7 +112,6 @@
   1 import
   filtering out empty revision
   repository tip rolled back to revision 0 (undo commit)
-  working directory now based on revision -1
   0 ci0
   updating tags
   $ hgcat b/c
--- a/tests/test-convert-filemap.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-filemap.t	Sat Oct 15 14:30:50 2011 -0500
@@ -86,8 +86,7 @@
   bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644   quux
   $ hg debugrename copied
   copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
-  $ echo
-  
+
   $ cd ..
   $ splitrepo()
   > {
--- a/tests/test-convert-git.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-git.t	Sat Oct 15 14:30:50 2011 -0500
@@ -198,8 +198,6 @@
   354ae8da6e890359ef49ade27b68bbc361f3ca88 644   baz
   9277c9cc8dd4576fc01a17939b4351e5ada93466 644   foo
   88dfeab657e8cf2cef3dec67b914f49791ae76b1 644   quux
-  $ echo
-  
 
 test binary conversion (issue 1359)
 
@@ -226,8 +224,6 @@
   $ python -c 'print len(file("b", "rb").read())'
   4096
   $ cd ..
-  $ echo
-  
 
 test author vs committer
 
--- a/tests/test-convert-svn-move.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-svn-move.t	Sat Oct 15 14:30:50 2011 -0500
@@ -167,6 +167,7 @@
   > [progress]
   > assume-tty = 1
   > delay = 0
+  > changedelay = 0
   > format = topic bar number
   > refresh = 0
   > width = 60
--- a/tests/test-convert-svn-sink.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert-svn-sink.t	Sat Oct 15 14:30:50 2011 -0500
@@ -219,8 +219,9 @@
   newlink
 
   $ hg --cwd a rm b
-  $ echo % remove
-  % remove
+
+Remove
+
   $ hg --cwd a ci -d '4 0' -m 'remove a file'
   $ hg --cwd a tip -q
   4:07b2e34a5b17
--- a/tests/test-convert.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-convert.t	Sat Oct 15 14:30:50 2011 -0500
@@ -249,18 +249,18 @@
   
   options:
   
-   -s --source-type TYPE  source repository type
-   -d --dest-type TYPE    destination repository type
-   -r --rev REV           import up to target revision REV
-   -A --authormap FILE    remap usernames using this file
-      --filemap FILE      remap file names using contents of file
-      --splicemap FILE    splice synthesized history into place
-      --branchmap FILE    change branch names while converting
-      --branchsort        try to sort changesets by branches
-      --datesort          try to sort changesets by date
-      --sourcesort        preserve source changesets order
+   -s --source-type TYPE source repository type
+   -d --dest-type TYPE   destination repository type
+   -r --rev REV          import up to target revision REV
+   -A --authormap FILE   remap usernames using this file
+      --filemap FILE     remap file names using contents of file
+      --splicemap FILE   splice synthesized history into place
+      --branchmap FILE   change branch names while converting
+      --branchsort       try to sort changesets by branches
+      --datesort         try to sort changesets by date
+      --sourcesort       preserve source changesets order
   
-  use "hg -v help convert" to show global options
+  use "hg -v help convert" to show more info
   $ hg init a
   $ cd a
   $ echo a > a
--- a/tests/test-debugcomplete.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-debugcomplete.t	Sat Oct 15 14:30:50 2011 -0500
@@ -17,6 +17,7 @@
   diff
   export
   forget
+  graft
   grep
   heads
   help
@@ -196,7 +197,7 @@
   forget: include, exclude
   init: ssh, remotecmd, insecure
   log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude
-  merge: force, tool, rev, preview
+  merge: force, rev, preview, tool
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
   remove: after, force, include, exclude
@@ -206,7 +207,7 @@
   update: clean, check, date, rev
   addremove: similarity, include, exclude, dry-run
   archive: no-decode, prefix, rev, type, subrepos, include, exclude
-  backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
+  backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user
   bisect: reset, good, bad, skip, extend, command, noupdate
   bookmarks: force, rev, delete, rename, inactive
   branch: force, clean
@@ -242,11 +243,12 @@
   debugsub: rev
   debugwalk: include, exclude
   debugwireargs: three, four, five, ssh, remotecmd, insecure
+  graft: continue, edit, currentdate, currentuser, date, user, tool
   grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
   heads: rev, topo, active, closed, style, template
   help: extension, command
   identify: rev, num, id, branch, tags, bookmarks
-  import: strip, base, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity
+  import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity
   incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
   locate: rev, print0, fullpath, include, exclude
   manifest: rev, all
@@ -255,9 +257,9 @@
   paths: 
   recover: 
   rename: after, force, include, exclude, dry-run
-  resolve: all, list, mark, unmark, tool, no-status, include, exclude
+  resolve: all, list, mark, unmark, no-status, tool, include, exclude
   revert: all, date, rev, no-backup, include, exclude, dry-run
-  rollback: dry-run
+  rollback: dry-run, force
   root: 
   showconfig: untrusted
   tag: force, local, rev, remove, edit, message, date, user
--- a/tests/test-diff-color.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-diff-color.t	Sat Oct 15 14:30:50 2011 -0500
@@ -94,8 +94,7 @@
    a
    c
   \x1b[0;33mrecord this change to 'a'? [Ynsfdaq?]\x1b[0m  (esc)
-  $ echo
-  
+
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mq=" >> $HGRCPATH
   $ hg rollback
@@ -123,5 +122,3 @@
    a
    c
   \x1b[0;33mrecord this change to 'a'? [Ynsfdaq?]\x1b[0m  (esc)
-  $ echo
-  
--- a/tests/test-dispatch.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-dispatch.t	Sat Oct 15 14:30:50 2011 -0500
@@ -25,11 +25,11 @@
   
   options:
   
-   -o --output FORMAT        print output to file with formatted name
-   -r --rev REV              print the given revision
-      --decode               apply any matching decode filter
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -o --output FORMAT       print output to file with formatted name
+   -r --rev REV             print the given revision
+      --decode              apply any matching decode filter
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
   
   [+] marked option can be specified multiple times
   
--- a/tests/test-doctest.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-doctest.py	Sat Oct 15 14:30:50 2011 -0500
@@ -33,3 +33,6 @@
 
 import hgext.convert.cvsps
 doctest.testmod(hgext.convert.cvsps)
+
+import mercurial.revset
+doctest.testmod(mercurial.revset)
--- a/tests/test-duplicateoptions.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-duplicateoptions.py	Sat Oct 15 14:30:50 2011 -0500
@@ -19,9 +19,15 @@
 u = ui.ui()
 extensions.loadall(u)
 
+globalshort = set()
+globallong = set()
+for option in commands.globalopts:
+    option[0] and globalshort.add(option[0])
+    option[1] and globallong.add(option[1])
+
 for cmd, entry in commands.table.iteritems():
-    seenshort = set()
-    seenlong = set()
+    seenshort = globalshort.copy()
+    seenlong = globallong.copy()
     for option in entry[1]:
         if (option[0] and option[0] in seenshort) or \
            (option[1] and option[1] in seenlong):
--- a/tests/test-encoding-align.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-encoding-align.t	Sat Oct 15 14:30:50 2011 -0500
@@ -46,20 +46,20 @@
 check alignment of option descriptions in help
 
   $ hg help showoptlist
-  hg showoptlist 
+  hg showoptlist
   
   dummy command to show option descriptions
   
   options:
   
-   -s --opt1 \xe7\x9f\xad\xe5\x90\x8d          short width \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d (esc)
-   -m --opt2 MIDDLE_       middle width MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_
-                           MIDDLE_ MIDDLE_ MIDDLE_
-   -l --opt3 \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d  long width \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
-                           \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
-                           \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+   -s --opt1 \xe7\x9f\xad\xe5\x90\x8d         short width \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d (esc)
+   -m --opt2 MIDDLE_      middle width MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_
+                          MIDDLE_ MIDDLE_ MIDDLE_
+   -l --opt3 \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d long width \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+                          \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+                          \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
   
-  use "hg -v help showoptlist" to show global options
+  use "hg -v help showoptlist" to show more info
 
 
   $ rm -f s; touch s
--- a/tests/test-encoding-textwrap.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-encoding-textwrap.t	Sat Oct 15 14:30:50 2011 -0500
@@ -57,7 +57,7 @@
 (1-1) display Japanese full-width characters in cp932
 
   $ COLUMNS=60 hg --encoding cp932 --config extensions.show=./show.py help show_full_ja
-  hg show_full_ja 
+  hg show_full_ja
   
   \x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf \x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf \x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf (esc)
   
@@ -67,12 +67,12 @@
       \x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf\x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf\x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf (esc)
       \x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8\x82\xa9\x82\xab\x82\xad\x82\xaf (esc)
   
-  use "hg -v help show_full_ja" to show global options
+  use "hg -v help show_full_ja" to show more info
 
 (1-2) display Japanese full-width characters in utf-8
 
   $ COLUMNS=60 hg --encoding utf-8 --config extensions.show=./show.py help show_full_ja
-  hg show_full_ja 
+  hg show_full_ja
   
   \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91 (esc)
   
@@ -82,13 +82,13 @@
       \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91 (esc)
       \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91 (esc)
   
-  use "hg -v help show_full_ja" to show global options
+  use "hg -v help show_full_ja" to show more info
 
 
 (1-3) display Japanese half-width characters in cp932
 
   $ COLUMNS=60 hg --encoding cp932 --config extensions.show=./show.py help show_half_ja
-  hg show_half_ja 
+  hg show_half_ja
   
   \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 (esc)
   
@@ -98,12 +98,12 @@
       \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 (esc)
       \xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9 (esc)
   
-  use "hg -v help show_half_ja" to show global options
+  use "hg -v help show_half_ja" to show more info
 
 (1-4) display Japanese half-width characters in utf-8
 
   $ COLUMNS=60 hg --encoding utf-8 --config extensions.show=./show.py help show_half_ja
-  hg show_half_ja 
+  hg show_half_ja
   
   \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 (esc)
   
@@ -113,7 +113,7 @@
       \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 (esc)
       \xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9\xef\xbd\xb1\xef\xbd\xb2\xef\xbd\xb3\xef\xbd\xb4\xef\xbd\xb5\xef\xbd\xb6\xef\xbd\xb7\xef\xbd\xb8\xef\xbd\xb9 (esc)
   
-  use "hg -v help show_half_ja" to show global options
+  use "hg -v help show_half_ja" to show more info
 
 
 
@@ -124,7 +124,7 @@
 (2-1-1) display Japanese ambiguous-width characters in cp932
 
   $ COLUMNS=60 hg --encoding cp932 --config extensions.show=./show.py help show_ambig_ja
-  hg show_ambig_ja 
+  hg show_ambig_ja
   
   \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
   
@@ -134,12 +134,12 @@
       \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
       \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
   
-  use "hg -v help show_ambig_ja" to show global options
+  use "hg -v help show_ambig_ja" to show more info
 
 (2-1-2) display Japanese ambiguous-width characters in utf-8
 
   $ COLUMNS=60 hg --encoding utf-8 --config extensions.show=./show.py help show_ambig_ja
-  hg show_ambig_ja 
+  hg show_ambig_ja
   
   \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
   
@@ -149,12 +149,12 @@
       \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
       \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
   
-  use "hg -v help show_ambig_ja" to show global options
+  use "hg -v help show_ambig_ja" to show more info
 
 (2-1-3) display Russian ambiguous-width characters in cp1251
 
   $ COLUMNS=60 hg --encoding cp1251 --config extensions.show=./show.py help show_ambig_ru
-  hg show_ambig_ru 
+  hg show_ambig_ru
   
   \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
   
@@ -164,12 +164,12 @@
       \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
       \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
   
-  use "hg -v help show_ambig_ru" to show global options
+  use "hg -v help show_ambig_ru" to show more info
 
 (2-1-4) display Russian ambiguous-width characters in utf-8
 
   $ COLUMNS=60 hg --encoding utf-8 --config extensions.show=./show.py help show_ambig_ru
-  hg show_ambig_ru 
+  hg show_ambig_ru
   
   \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
   
@@ -179,7 +179,7 @@
       \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
       \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
   
-  use "hg -v help show_ambig_ru" to show global options
+  use "hg -v help show_ambig_ru" to show more info
 
 
 (2-2) treat width of ambiguous characters as wide
@@ -187,7 +187,7 @@
 (2-2-1) display Japanese ambiguous-width characters in cp932
 
   $ COLUMNS=60 HGENCODINGAMBIGUOUS=wide hg --encoding cp932 --config extensions.show=./show.py help show_ambig_ja
-  hg show_ambig_ja 
+  hg show_ambig_ja
   
   \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
   
@@ -200,12 +200,12 @@
       \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b\x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
       \x83\xbf\x83\xc0\x83\xc1\x83\xc2\x83\xd2\x83\xc4\x83\xc5\x83\xc6\x81\x9b (esc)
   
-  use "hg -v help show_ambig_ja" to show global options
+  use "hg -v help show_ambig_ja" to show more info
 
 (2-2-2) display Japanese ambiguous-width characters in utf-8
 
   $ COLUMNS=60 HGENCODINGAMBIGUOUS=wide hg --encoding utf-8 --config extensions.show=./show.py help show_ambig_ja
-  hg show_ambig_ja 
+  hg show_ambig_ja
   
   \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
   
@@ -218,12 +218,12 @@
       \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b\xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
       \xce\xb1\xce\xb2\xce\xb3\xce\xb4\xcf\x85\xce\xb6\xce\xb7\xce\xb8\xe2\x97\x8b (esc)
   
-  use "hg -v help show_ambig_ja" to show global options
+  use "hg -v help show_ambig_ja" to show more info
 
 (2-2-3) display Russian ambiguous-width characters in cp1251
 
   $ COLUMNS=60 HGENCODINGAMBIGUOUS=wide hg --encoding cp1251 --config extensions.show=./show.py help show_ambig_ru
-  hg show_ambig_ru 
+  hg show_ambig_ru
   
   \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
   \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
@@ -236,12 +236,12 @@
       \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8\xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
       \xcd\xe0\xf1\xf2\xf0\xee\xe9\xea\xe8 (esc)
   
-  use "hg -v help show_ambig_ru" to show global options
+  use "hg -v help show_ambig_ru" to show more info
 
 (2-2-4) display Russian ambiguous-width charactes in utf-8
 
   $ COLUMNS=60 HGENCODINGAMBIGUOUS=wide hg --encoding utf-8 --config extensions.show=./show.py help show_ambig_ru
-  hg show_ambig_ru 
+  hg show_ambig_ru
   
   \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
   \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
@@ -254,4 +254,4 @@
       \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
       \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc)
   
-  use "hg -v help show_ambig_ru" to show global options
+  use "hg -v help show_ambig_ru" to show more info
--- a/tests/test-eol-hook.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-eol-hook.t	Sat Oct 15 14:30:50 2011 -0500
@@ -161,7 +161,6 @@
   added 3 changesets with 3 changes to 2 files (+1 heads)
   $ hg -R ../main rollback
   repository tip rolled back to revision 5 (undo push)
-  working directory now based on revision -1
 
 Test it still fails with checkallhook
 
--- a/tests/test-eol.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-eol.t	Sat Oct 15 14:30:50 2011 -0500
@@ -441,3 +441,82 @@
   warning: ignoring .hgeol file due to parse error at .hgeol:1: bad
   $ hg status
   ? .hgeol.orig
+
+Test eol.only-consistent can be specified in .hgeol
+
+  $ cd $TESTTMP
+  $ hg init only-consistent
+  $ cd only-consistent
+  $ printf "first\nsecond\r\n" > a.txt
+  $ hg add a.txt
+  $ cat > .hgeol << EOF
+  > [eol]
+  > only-consistent = True
+  > EOF
+  $ hg commit -m 'inconsistent'
+  abort: inconsistent newline style in a.txt
+  
+  [255]
+  $ cat > .hgeol << EOF
+  > [eol]
+  > only-consistent = False
+  > EOF
+  $ hg commit -m 'consistent'
+
+
+Test trailing newline
+
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > eol=
+  > EOF
+
+setup repository
+
+  $ cd $TESTTMP
+  $ hg init trailing
+  $ cd trailing
+  $ cat > .hgeol <<EOF
+  > [patterns]
+  > **.txt = native
+  > [eol]
+  > fix-trailing-newline = False
+  > EOF
+
+add text without trailing newline
+
+  $ printf "first\nsecond" > a.txt
+  $ hg commit --addremove -m 'checking in'
+  adding .hgeol
+  adding a.txt
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  first
+  second (no-eol)
+
+  $ cat > .hgeol <<EOF
+  > [patterns]
+  > **.txt = native
+  > [eol]
+  > fix-trailing-newline = True
+  > EOF
+  $ printf "third\nfourth" > a.txt
+  $ hg commit -m 'checking in with newline fix'
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  third
+  fourth
+
+append a line without trailing newline
+
+  $ printf "fifth" >> a.txt
+  $ hg commit -m 'adding another line line'
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  third
+  fourth
+  fifth
+
--- a/tests/test-export.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-export.t	Sat Oct 15 14:30:50 2011 -0500
@@ -7,7 +7,7 @@
   >    hg ci -m "foo-$i"
   > done
 
-  $ for out in "%nof%N" "%%%H" "%b-%R" "%h" "%r"; do
+  $ for out in "%nof%N" "%%%H" "%b-%R" "%h" "%r" "%m"; do
   >    echo
   >    echo "# foo-$out.patch"
   >    hg export -v -o "foo-$out.patch" 2:tip
@@ -77,6 +77,19 @@
   foo-09.patch
   foo-10.patch
   foo-11.patch
+  
+  # foo-%m.patch
+  exporting patches:
+  foo-foo_2.patch
+  foo-foo_3.patch
+  foo-foo_4.patch
+  foo-foo_5.patch
+  foo-foo_6.patch
+  foo-foo_7.patch
+  foo-foo_8.patch
+  foo-foo_9.patch
+  foo-foo_10.patch
+  foo-foo_11.patch
 
 Exporting 4 changesets to a file:
 
@@ -108,3 +121,11 @@
    foo-9
   +foo-10
 
+Checking if only alphanumeric characters are used in the file name (%m option):
+
+  $ echo "line" >> foo
+  $ hg commit -m " !\"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~" 
+  $ hg export -v -o %m.patch tip
+  exporting patch:
+  ____________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz____.patch
+
--- a/tests/test-extdiff.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-extdiff.t	Sat Oct 15 14:30:50 2011 -0500
@@ -39,15 +39,15 @@
   
   options:
   
-   -o --option OPT [+]       pass option to comparison program
-   -r --rev REV [+]          revision
-   -c --change REV           change made by revision
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -o --option OPT [+]      pass option to comparison program
+   -r --rev REV [+]         revision
+   -c --change REV          change made by revision
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help falabala" to show global options
+  use "hg -v help falabala" to show more info
 
   $ hg ci -d '0 0' -mtest1
 
--- a/tests/test-extension.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-extension.t	Sat Oct 15 14:30:50 2011 -0500
@@ -182,23 +182,24 @@
         yet another foo command
   
   global options:
-   -R --repository REPO    repository root directory or name of overlay bundle
-                           file
-      --cwd DIR            change working directory
-   -y --noninteractive     do not prompt, automatically pick the first choice
-                           for all prompts
-   -q --quiet              suppress output
-   -v --verbose            enable additional output
-      --config CONFIG [+]  set/override config option (use 'section.name=value')
-      --debug              enable debugging output
-      --debugger           start debugger
-      --encoding ENCODE    set the charset encoding (default: ascii)
-      --encodingmode MODE  set the charset encoding mode (default: strict)
-      --traceback          always print a traceback on exception
-      --time               time how long the command takes
-      --profile            print command execution profile
-      --version            output version information and exit
-   -h --help               display help and exit
+  
+   -R --repository REPO   repository root directory or name of overlay bundle
+                          file
+      --cwd DIR           change working directory
+   -y --noninteractive    do not prompt, automatically pick the first choice for
+                          all prompts
+   -q --quiet             suppress output
+   -v --verbose           enable additional output
+      --config CONFIG [+] set/override config option (use 'section.name=value')
+      --debug             enable debugging output
+      --debugger          start debugger
+      --encoding ENCODE   set the charset encoding (default: ascii)
+      --encodingmode MODE set the charset encoding mode (default: strict)
+      --traceback         always print a traceback on exception
+      --time              time how long the command takes
+      --profile           print command execution profile
+      --version           output version information and exit
+   -h --help              display help and exit
   
   [+] marked option can be specified multiple times
 
@@ -213,23 +214,24 @@
         yet another foo command
   
   global options:
-   -R --repository REPO    repository root directory or name of overlay bundle
-                           file
-      --cwd DIR            change working directory
-   -y --noninteractive     do not prompt, automatically pick the first choice
-                           for all prompts
-   -q --quiet              suppress output
-   -v --verbose            enable additional output
-      --config CONFIG [+]  set/override config option (use 'section.name=value')
-      --debug              enable debugging output
-      --debugger           start debugger
-      --encoding ENCODE    set the charset encoding (default: ascii)
-      --encodingmode MODE  set the charset encoding mode (default: strict)
-      --traceback          always print a traceback on exception
-      --time               time how long the command takes
-      --profile            print command execution profile
-      --version            output version information and exit
-   -h --help               display help and exit
+  
+   -R --repository REPO   repository root directory or name of overlay bundle
+                          file
+      --cwd DIR           change working directory
+   -y --noninteractive    do not prompt, automatically pick the first choice for
+                          all prompts
+   -q --quiet             suppress output
+   -v --verbose           enable additional output
+      --config CONFIG [+] set/override config option (use 'section.name=value')
+      --debug             enable debugging output
+      --debugger          start debugger
+      --encoding ENCODE   set the charset encoding (default: ascii)
+      --encodingmode MODE set the charset encoding mode (default: strict)
+      --traceback         always print a traceback on exception
+      --time              time how long the command takes
+      --profile           print command execution profile
+      --version           output version information and exit
+   -h --help              display help and exit
   
   [+] marked option can be specified multiple times
   $ echo 'debugextension = !' >> $HGRCPATH
@@ -260,16 +262,16 @@
   
   options:
   
-   -p --program CMD          comparison program to run
-   -o --option OPT [+]       pass option to comparison program
-   -r --rev REV [+]          revision
-   -c --change REV           change made by revision
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -p --program CMD         comparison program to run
+   -o --option OPT [+]      pass option to comparison program
+   -r --rev REV [+]         revision
+   -c --change REV          change made by revision
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help extdiff" to show global options
+  use "hg -v help extdiff" to show more info
 
   $ hg help --extension extdiff
   extdiff extension - command to allow external programs to compare revisions
@@ -371,7 +373,7 @@
   
   multirevs command
   
-  use "hg -v help multirevs" to show global options
+  use "hg -v help multirevs" to show more info
 
   $ hg multirevs
   hg multirevs: invalid arguments
--- a/tests/test-fetch.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-fetch.t	Sat Oct 15 14:30:50 2011 -0500
@@ -1,5 +1,3 @@
-adjust to non-default HGPORT, e.g. with run-tests.py -j
-
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "fetch=" >> $HGRCPATH
 
@@ -7,7 +5,7 @@
 
   $ hg init a
   $ echo a > a/a
-  $ hg --cwd a commit -d '1 0' -Ama
+  $ hg --cwd a commit -Ama
   adding a
   $ hg clone a b
   updating to branch default
@@ -16,10 +14,10 @@
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo b > a/b
-  $ hg --cwd a commit -d '2 0' -Amb
+  $ hg --cwd a commit -Amb
   adding b
   $ hg --cwd a parents -q
-  1:97d72e5f12c7
+  1:d2ae7f538514
 
 should pull one change
 
@@ -32,9 +30,9 @@
   added 1 changesets with 1 changes to 1 files
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg --cwd b parents -q
-  1:97d72e5f12c7
+  1:d2ae7f538514
   $ echo c > c/c
-  $ hg --cwd c commit -d '3 0' -Amc
+  $ hg --cwd c commit -Amc
   adding c
   $ hg clone c d
   updating to branch default
@@ -48,39 +46,37 @@
 message, making every commit appear different.
 should merge c into a
 
-  $ hg --cwd c fetch -d '4 0' -m 'automated merge' ../a
+  $ hg --cwd c fetch -d '0 0' -m 'automated merge' ../a
   pulling from ../a
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  updating to 2:97d72e5f12c7
+  updating to 2:d2ae7f538514
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  merging with 1:5e056962225c
+  merging with 1:d36c0562f908
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  new changeset 3:cd3a41621cf0 merges remote changes with local
+  new changeset 3:a323a0c43ec4 merges remote changes with local
   $ ls c
   a
   b
   c
-  $ netstat -tnap 2>/dev/null | grep $HGPORT | grep LISTEN
-  [1]
   $ hg --cwd a serve -a localhost -p $HGPORT -d --pid-file=hg.pid
   $ cat a/hg.pid >> "$DAEMON_PIDS"
 
 fetch over http, no auth
 
-  $ hg --cwd d fetch -d '5 0' http://localhost:$HGPORT/
+  $ hg --cwd d fetch http://localhost:$HGPORT/
   pulling from http://localhost:$HGPORT/
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  updating to 2:97d72e5f12c7
+  updating to 2:d2ae7f538514
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  merging with 1:5e056962225c
+  merging with 1:d36c0562f908
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   new changeset 3:* merges remote changes with local (glob)
   $ hg --cwd d tip --template '{desc}\n'
@@ -88,16 +84,16 @@
 
 fetch over http with auth (should be hidden in desc)
 
-  $ hg --cwd e fetch -d '5 0' http://user:password@localhost:$HGPORT/
+  $ hg --cwd e fetch http://user:password@localhost:$HGPORT/
   pulling from http://user:***@localhost:$HGPORT/
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  updating to 2:97d72e5f12c7
+  updating to 2:d2ae7f538514
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  merging with 1:5e056962225c
+  merging with 1:d36c0562f908
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   new changeset 3:* merges remote changes with local (glob)
   $ hg --cwd e tip --template '{desc}\n'
@@ -109,17 +105,17 @@
   updating to branch default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo f > f/f
-  $ hg --cwd f ci -d '6 0' -Amf
+  $ hg --cwd f ci -Amf
   adding f
   $ echo g > g/g
-  $ hg --cwd g ci -d '6 0' -Amg
+  $ hg --cwd g ci -Amg
   adding g
   $ hg clone -q f h
   $ hg clone -q g i
 
 should merge f into g
 
-  $ hg --cwd g fetch -d '7 0' --switch -m 'automated merge' ../f
+  $ hg --cwd g fetch -d '0 0' --switch -m 'automated merge' ../f
   pulling from ../f
   searching for changes
   adding changesets
@@ -127,9 +123,9 @@
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  merging with 3:cc6a3744834d
+  merging with 3:6343ca3eff20
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  new changeset 4:55aa4f32ec59 merges remote changes with local
+  new changeset 4:f7faa0b7d3c6 merges remote changes with local
   $ rm i/g
 
 should abort, because i is modified
@@ -142,21 +138,19 @@
 
   $ hg init nbase
   $ echo base > nbase/a
-  $ hg -R nbase ci -d '1 0' -Am base
+  $ hg -R nbase ci -Am base
   adding a
   $ hg -R nbase branch a
   marked working directory as branch a
   $ echo a > nbase/a
-  $ hg -R nbase ci -d '2 0' -m a
+  $ hg -R nbase ci -m a
   $ hg -R nbase up -C 0
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg -R nbase branch b
   marked working directory as branch b
   $ echo b > nbase/b
-  $ hg -R nbase ci -Ad '3 0' -m b
+  $ hg -R nbase ci -Am b
   adding b
-  $ echo
-  
 
 pull in change on foreign branch
 
@@ -169,10 +163,10 @@
   $ hg -R n1 up -C a
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo aa > n1/a
-  $ hg -R n1 ci -d '4 0' -m a1
+  $ hg -R n1 ci -m a1
   $ hg -R n2 up -C b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg -R n2 fetch -d '9 0' -m 'merge' n1
+  $ hg -R n2 fetch -m 'merge' n1
   pulling from n1
   searching for changes
   adding changesets
@@ -185,8 +179,6 @@
   $ hg -R n2 parents --template '{rev}\n'
   2
   $ rm -fr n1 n2
-  $ echo
-  
 
 pull in changes on both foreign and local branches
 
@@ -199,14 +191,14 @@
   $ hg -R n1 up -C a
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo aa > n1/a
-  $ hg -R n1 ci -d '4 0' -m a1
+  $ hg -R n1 ci -m a1
   $ hg -R n1 up -C b
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo bb > n1/b
-  $ hg -R n1 ci -d '5 0' -m b1
+  $ hg -R n1 ci -m b1
   $ hg -R n2 up -C b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg -R n2 fetch -d '9 0' -m 'merge' n1
+  $ hg -R n2 fetch -m 'merge' n1
   pulling from n1
   searching for changes
   adding changesets
@@ -220,8 +212,6 @@
   $ hg -R n2 parents --template '{rev}\n'
   4
   $ rm -fr n1 n2
-  $ echo
-  
 
 pull changes on foreign (2 new heads) and local (1 new head) branches
 with a local change
@@ -235,33 +225,33 @@
   $ hg -R n1 up -C a
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo a1 > n1/a
-  $ hg -R n1 ci -d '4 0' -m a1
+  $ hg -R n1 ci -m a1
   $ hg -R n1 up -C b
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo bb > n1/b
-  $ hg -R n1 ci -d '5 0' -m b1
+  $ hg -R n1 ci -m b1
   $ hg -R n1 up -C 1
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo a2 > n1/a
-  $ hg -R n1 ci -d '6 0' -m a2
+  $ hg -R n1 ci -m a2
   created new head
   $ hg -R n2 up -C b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo change >> n2/c
-  $ hg -R n2 ci -Ad '7 0' -m local
+  $ hg -R n2 ci -A -m local
   adding c
-  $ hg -R n2 fetch -d '9 0' -m 'merge' n1
+  $ hg -R n2 fetch -d '0 0' -m 'merge' n1
   pulling from n1
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 3 changesets with 3 changes to 2 files (+2 heads)
-  updating to 5:708c6cce3d26
+  updating to 5:3c4a837a864f
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  merging with 3:d83427717b1f
+  merging with 3:1267f84a9ea5
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  new changeset 7:48f1a33f52af merges remote changes with local
+  new changeset 7:2cf2a1261f21 merges remote changes with local
 
 parent should be 7 (new merge changeset)
 
@@ -283,21 +273,21 @@
   $ hg -R n1 merge b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
-  $ hg -R n1 ci -d '4 0' -m merge
+  $ hg -R n1 ci -m merge
   $ hg -R n1 up -C 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo c > n1/a
-  $ hg -R n1 ci -d '5 0' -m c
+  $ hg -R n1 ci -m c
   $ hg -R n1 up -C 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo cc > n1/a
-  $ hg -R n1 ci -d '6 0' -m cc
+  $ hg -R n1 ci -m cc
   created new head
   $ hg -R n2 up -C b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo change >> n2/b
-  $ hg -R n2 ci -Ad '7 0' -m local
-  $ hg -R n2 fetch -d '9 0' -m 'merge' n1
+  $ hg -R n2 ci -A -m local
+  $ hg -R n2 fetch -m 'merge' n1
   pulling from n1
   searching for changes
   adding changesets
@@ -326,7 +316,7 @@
   $ hg -R n1 ci -m next
   $ hg -R n2 branch topic
   marked working directory as branch topic
-  $ hg -R n2 fetch -d '0 0' -m merge n1
+  $ hg -R n2 fetch -m merge n1
   abort: working dir not at branch tip (use "hg update" to check out branch tip)
   [255]
 
@@ -393,8 +383,6 @@
   new changeset 3:* merges remote changes with local (glob)
   $ hg --cwd i1726r2 heads default --template '{rev}\n'
   3
-  $ echo
-  
 
 test issue2047
 
@@ -415,4 +403,3 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  $ "$TESTDIR/killdaemons.py"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-filecache.py	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,94 @@
+import sys, os, subprocess
+
+if subprocess.call(['%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
+    sys.exit(80)
+
+from mercurial import util, scmutil, extensions
+
+filecache = scmutil.filecache
+
+class fakerepo(object):
+    def __init__(self):
+        self._filecache = {}
+
+    def join(self, p):
+        return p
+
+    def sjoin(self, p):
+        return p
+
+    @filecache('x')
+    def cached(self):
+        print 'creating'
+
+    def invalidate(self):
+        for k in self._filecache:
+            try:
+                delattr(self, k)
+            except AttributeError:
+                pass
+
+def basic(repo):
+    # file doesn't exist, calls function
+    repo.cached
+
+    repo.invalidate()
+    # file still doesn't exist, uses cache
+    repo.cached
+
+    # create empty file
+    f = open('x', 'w')
+    f.close()
+    repo.invalidate()
+    # should recreate the object
+    repo.cached
+
+    f = open('x', 'w')
+    f.write('a')
+    f.close()
+    repo.invalidate()
+    # should recreate the object
+    repo.cached
+
+    repo.invalidate()
+    # stats file again, nothing changed, reuses object
+    repo.cached
+
+    # atomic replace file, size doesn't change
+    # hopefully st_mtime doesn't change as well so this doesn't use the cache
+    # because of inode change
+    f = scmutil.opener('.')('x', 'w', atomictemp=True)
+    f.write('b')
+    f.close()
+
+    repo.invalidate()
+    repo.cached
+
+def fakeuncacheable():
+    def wrapcacheable(orig, *args, **kwargs):
+        return False
+
+    def wrapinit(orig, *args, **kwargs):
+        pass
+
+    originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
+    origcacheable = extensions.wrapfunction(util.cachestat, 'cacheable',
+                                            wrapcacheable)
+
+    try:
+        os.remove('x')
+    except:
+        pass
+
+    basic(fakerepo())
+
+    util.cachestat.cacheable = origcacheable
+    util.cachestat.__init__ = originit
+
+print 'basic:'
+print
+basic(fakerepo())
+print
+print 'fakeuncacheable:'
+print
+fakeuncacheable()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-filecache.py.out	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,15 @@
+basic:
+
+creating
+creating
+creating
+creating
+
+fakeuncacheable:
+
+creating
+creating
+creating
+creating
+creating
+creating
--- a/tests/test-globalopts.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-globalopts.t	Sat Oct 15 14:30:50 2011 -0500
@@ -296,6 +296,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -377,6 +378,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
--- a/tests/test-help.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-help.t	Sat Oct 15 14:30:50 2011 -0500
@@ -66,6 +66,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -141,6 +142,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -199,12 +201,7 @@
 Test short command list with verbose option
 
   $ hg -v help shortlist
-  Mercurial Distributed SCM (version *) (glob)
-  (see http://mercurial.selenic.com for more information)
-  
-  Copyright (C) 2005-2011 Matt Mackall and others
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  Mercurial Distributed SCM
   
   basic commands:
   
@@ -244,23 +241,24 @@
         update working directory (or switch revisions)
   
   global options:
-   -R --repository REPO    repository root directory or name of overlay bundle
-                           file
-      --cwd DIR            change working directory
-   -y --noninteractive     do not prompt, automatically pick the first choice
-                           for all prompts
-   -q --quiet              suppress output
-   -v --verbose            enable additional output
-      --config CONFIG [+]  set/override config option (use 'section.name=value')
-      --debug              enable debugging output
-      --debugger           start debugger
-      --encoding ENCODE    set the charset encoding (default: ascii)
-      --encodingmode MODE  set the charset encoding mode (default: strict)
-      --traceback          always print a traceback on exception
-      --time               time how long the command takes
-      --profile            print command execution profile
-      --version            output version information and exit
-   -h --help               display help and exit
+  
+   -R --repository REPO   repository root directory or name of overlay bundle
+                          file
+      --cwd DIR           change working directory
+   -y --noninteractive    do not prompt, automatically pick the first choice for
+                          all prompts
+   -q --quiet             suppress output
+   -v --verbose           enable additional output
+      --config CONFIG [+] set/override config option (use 'section.name=value')
+      --debug             enable debugging output
+      --debugger          start debugger
+      --encoding ENCODE   set the charset encoding (default: ascii)
+      --encodingmode MODE set the charset encoding mode (default: strict)
+      --traceback         always print a traceback on exception
+      --time              time how long the command takes
+      --profile           print command execution profile
+      --version           output version information and exit
+   -h --help              display help and exit
   
   [+] marked option can be specified multiple times
   
@@ -280,18 +278,16 @@
   
       Returns 0 if all files are successfully added.
   
-  use "hg -v help add" to show verbose help
-  
   options:
   
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
-   -n --dry-run              do not perform actions, just print output
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -S --subrepos            recurse into subrepositories
+   -n --dry-run             do not perform actions, just print output
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help add" to show global options
+  use "hg -v help add" to show more info
 
 Verbose help for add
 
@@ -323,30 +319,32 @@
   
   options:
   
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
-   -n --dry-run              do not perform actions, just print output
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -S --subrepos            recurse into subrepositories
+   -n --dry-run             do not perform actions, just print output
+  
+  [+] marked option can be specified multiple times
   
   global options:
-   -R --repository REPO      repository root directory or name of overlay bundle
-                             file
-      --cwd DIR              change working directory
-   -y --noninteractive       do not prompt, automatically pick the first choice
-                             for all prompts
-   -q --quiet                suppress output
-   -v --verbose              enable additional output
-      --config CONFIG [+]    set/override config option (use
-                             'section.name=value')
-      --debug                enable debugging output
-      --debugger             start debugger
-      --encoding ENCODE      set the charset encoding (default: ascii)
-      --encodingmode MODE    set the charset encoding mode (default: strict)
-      --traceback            always print a traceback on exception
-      --time                 time how long the command takes
-      --profile              print command execution profile
-      --version              output version information and exit
-   -h --help                 display help and exit
+  
+   -R --repository REPO   repository root directory or name of overlay bundle
+                          file
+      --cwd DIR           change working directory
+   -y --noninteractive    do not prompt, automatically pick the first choice for
+                          all prompts
+   -q --quiet             suppress output
+   -v --verbose           enable additional output
+      --config CONFIG [+] set/override config option (use 'section.name=value')
+      --debug             enable debugging output
+      --debugger          start debugger
+      --encoding ENCODE   set the charset encoding (default: ascii)
+      --encodingmode MODE set the charset encoding mode (default: strict)
+      --traceback         always print a traceback on exception
+      --time              time how long the command takes
+      --profile           print command execution profile
+      --version           output version information and exit
+   -h --help              display help and exit
   
   [+] marked option can be specified multiple times
 
@@ -359,32 +357,6 @@
   Copyright (C) 2005-2011 Matt Mackall and others
   This is free software; see the source for copying conditions. There is NO
   warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  
-  hg add [OPTION]... [FILE]...
-  
-  add the specified files on the next commit
-  
-      Schedule files to be version controlled and added to the repository.
-  
-      The files will be added to the repository at the next commit. To undo an
-      add before that, see "hg forget".
-  
-      If no names are given, add all files to the repository.
-  
-      Returns 0 if all files are successfully added.
-  
-  use "hg -v help add" to show verbose help
-  
-  options:
-  
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
-   -n --dry-run              do not perform actions, just print output
-  
-  [+] marked option can be specified multiple times
-  
-  use "hg -v help add" to show global options
 
   $ hg add --skjdfks
   hg add: option --skjdfks not recognized
@@ -394,10 +366,10 @@
   
   options:
   
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
-   -n --dry-run              do not perform actions, just print output
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -S --subrepos            recurse into subrepositories
+   -n --dry-run             do not perform actions, just print output
   
   [+] marked option can be specified multiple times
   
@@ -430,7 +402,7 @@
   
       Returns 0 on success, 1 if errors are encountered.
   
-  use "hg -v help verify" to show global options
+  use "hg -v help verify" to show more info
 
   $ hg help diff
   hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
@@ -465,25 +437,25 @@
   
   options:
   
-   -r --rev REV [+]          revision
-   -c --change REV           change made by revision
-   -a --text                 treat all files as text
-   -g --git                  use git extended diff format
-      --nodates              omit dates from diff headers
-   -p --show-function        show which function each change is in
-      --reverse              produce a diff that undoes the changes
-   -w --ignore-all-space     ignore white space when comparing lines
-   -b --ignore-space-change  ignore changes in the amount of white space
-   -B --ignore-blank-lines   ignore changes whose lines are all blank
-   -U --unified NUM          number of lines of context to show
-      --stat                 output diffstat-style summary of changes
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
+   -r --rev REV [+]         revision
+   -c --change REV          change made by revision
+   -a --text                treat all files as text
+   -g --git                 use git extended diff format
+      --nodates             omit dates from diff headers
+   -p --show-function       show which function each change is in
+      --reverse             produce a diff that undoes the changes
+   -w --ignore-all-space    ignore white space when comparing lines
+   -b --ignore-space-change ignore changes in the amount of white space
+   -B --ignore-blank-lines  ignore changes whose lines are all blank
+   -U --unified NUM         number of lines of context to show
+      --stat                output diffstat-style summary of changes
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -S --subrepos            recurse into subrepositories
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help diff" to show global options
+  use "hg -v help diff" to show more info
 
   $ hg help status
   hg status [OPTION]... [FILE]...
@@ -527,26 +499,26 @@
   
   options:
   
-   -A --all                  show status of all files
-   -m --modified             show only modified files
-   -a --added                show only added files
-   -r --removed              show only removed files
-   -d --deleted              show only deleted (but tracked) files
-   -c --clean                show only files without changes
-   -u --unknown              show only unknown (not tracked) files
-   -i --ignored              show only ignored files
-   -n --no-status            hide status prefix
-   -C --copies               show source of copied files
-   -0 --print0               end filenames with NUL, for use with xargs
-      --rev REV [+]          show difference from revision
-      --change REV           list the changed files of a revision
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -S --subrepos             recurse into subrepositories
+   -A --all                 show status of all files
+   -m --modified            show only modified files
+   -a --added               show only added files
+   -r --removed             show only removed files
+   -d --deleted             show only deleted (but tracked) files
+   -c --clean               show only files without changes
+   -u --unknown             show only unknown (not tracked) files
+   -i --ignored             show only ignored files
+   -n --no-status           hide status prefix
+   -C --copies              show source of copied files
+   -0 --print0              end filenames with NUL, for use with xargs
+      --rev REV [+]         show difference from revision
+      --change REV          list the changed files of a revision
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -S --subrepos            recurse into subrepositories
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help status" to show global options
+  use "hg -v help status" to show more info
 
   $ hg -q help status
   hg status [OPTION]... [FILE]...
@@ -630,7 +602,7 @@
   
   (no help text available)
   
-  use "hg -v help nohelp" to show global options
+  use "hg -v help nohelp" to show more info
 
 Test that default list of commands omits extension commands
 
@@ -656,6 +628,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
--- a/tests/test-highlight.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-highlight.t	Sat Oct 15 14:30:50 2011 -0500
@@ -520,8 +520,6 @@
   $ echo "" >> b
   $ echo "" >> b
   $ diff -u b a
-  $ echo
-  
 
 hgweb filerevision, raw
 
@@ -531,8 +529,6 @@
   $ echo "" >> b
   $ hg cat primes.py >> b
   $ diff -u b a
-  $ echo
-  
 
 hgweb highlightcss friendly
 
--- a/tests/test-hook.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-hook.t	Sat Oct 15 14:30:50 2011 -0500
@@ -277,7 +277,6 @@
   (run 'hg update' to get a working copy)
   $ hg rollback
   repository tip rolled back to revision 3 (undo pull)
-  working directory now based on revision 0
 
 preoutgoing hook can prevent outgoing changes
 
--- a/tests/test-import-bypass.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-import-bypass.t	Sat Oct 15 14:30:50 2011 -0500
@@ -61,8 +61,7 @@
   @  0:07f494440405 test 0 0 - default - adda
   
   $ hg rollback
-  repository tip rolled back to revision 1 (undo commit)
-  working directory now based on revision 0
+  repository tip rolled back to revision 1 (undo import)
 
 Test --import-branch
 
@@ -74,8 +73,7 @@
   @  0:07f494440405 test 0 0 - default - adda
   
   $ hg rollback
-  repository tip rolled back to revision 1 (undo commit)
-  working directory now based on revision 0
+  repository tip rolled back to revision 1 (undo import)
 
 Test --strip
 
@@ -97,8 +95,7 @@
   > EOF
   applying patch from stdin
   $ hg rollback
-  repository tip rolled back to revision 1 (undo commit)
-  working directory now based on revision 0
+  repository tip rolled back to revision 1 (undo import)
 
 Test unsupported combinations
 
@@ -174,7 +171,6 @@
   $ hg import --bypass ../patch1.diff ../patch2.diff
   applying ../patch1.diff
   applying ../patch2.diff
-  applied 16581080145e
   $ shortlog
   o  3:bc8ca3f8a7c4 test 0 0 - default - addf
   |
@@ -199,7 +195,6 @@
   $ hg import --bypass --exact ../patch1.diff ../patch2.diff
   applying ../patch1.diff
   applying ../patch2.diff
-  applied 16581080145e
   $ shortlog
   o  3:d60cb8989666 test 0 0 - foo - addf
   |
--- a/tests/test-import.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-import.t	Sat Oct 15 14:30:50 2011 -0500
@@ -199,7 +199,6 @@
   $ hg init b
   $ hg --cwd a export 0:tip | hg --cwd b import -
   applying patch from stdin
-  applied 80971e65b431
   $ hg --cwd a id
   1d4bd90af0e4 tip
   $ hg --cwd b id
@@ -356,15 +355,20 @@
   $ hg clone -qr0 a b
   $ hg --cwd b parents --template 'parent: {rev}\n'
   parent: 0
-  $ hg --cwd b import ../patch1 ../patch2
+  $ hg --cwd b import -v ../patch1 ../patch2
   applying ../patch1
+  patching file a
+  a
+  created 1d4bd90af0e4
   applying ../patch2
-  applied 1d4bd90af0e4
+  patching file a
+  a
+  created 6d019af21222
   $ hg --cwd b rollback
-  repository tip rolled back to revision 1 (undo commit)
-  working directory now based on revision 1
+  repository tip rolled back to revision 0 (undo import)
+  working directory now based on revision 0
   $ hg --cwd b parents --template 'parent: {rev}\n'
-  parent: 1
+  parent: 0
   $ rm -r b
 
 
@@ -433,6 +437,7 @@
   applying fuzzy-tip.patch
   patching file a
   Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
+  applied to working directory
   $ hg revert -a
   reverting a
 
@@ -449,6 +454,7 @@
   applying fuzzy-tip.patch
   patching file a
   Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
+  applied to working directory
   $ cd ..
 
 
@@ -651,6 +657,7 @@
   removing a
   adding b
   recording removal of a as rename to b (88% similar)
+  applied to working directory
   $ hg st -C
   A b
     a
@@ -665,6 +672,7 @@
   patching file b
   removing a
   adding b
+  applied to working directory
   $ hg st -C
   A b
   R a
@@ -680,6 +688,7 @@
   adding a
   $ hg ci -m "commit"
   $ cat > a.patch <<EOF
+  > add a, b
   > diff --git a/a b/a
   > --- a/a
   > +++ b/a
@@ -690,9 +699,25 @@
   > EOF
   $ hg import --no-commit a.patch
   applying a.patch
+
+apply a good patch followed by an empty patch (mainly to ensure
+that dirstate is *not* updated when import crashes)
+  $ hg update -q -C .
+  $ rm b
+  $ touch empty.patch
+  $ hg import a.patch empty.patch
+  applying a.patch
+  applying empty.patch
+  transaction abort!
+  rollback completed
+  abort: empty.patch: no diffs found
+  [255]
+  $ hg tip --template '{rev}  {desc|firstline}\n'
+  0  commit
+  $ hg -q status
+  M a
   $ cd ..
 
-
 create file when source is not /dev/null
 
   $ cat > create.patch <<EOF
--- a/tests/test-init.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-init.t	Sat Oct 15 14:30:50 2011 -0500
@@ -19,8 +19,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
   $ echo this > local/foo
   $ hg ci --cwd local -A -m "init"
@@ -48,8 +48,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
 
 test failure
 
@@ -145,8 +145,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 prepare test of init of url configured from paths
@@ -162,8 +162,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 verify that clone also expand urls
@@ -175,8 +175,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 clone bookmarks
--- a/tests/test-install.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-install.t	Sat Oct 15 14:30:50 2011 -0500
@@ -2,7 +2,7 @@
   $ hg debuginstall
   Checking encoding (ascii)...
   Checking installed modules (*/mercurial)... (glob)
-  Checking templates...
+  Checking templates (*/mercurial/templates)... (glob)
   Checking commit editor...
   Checking username...
   No problems detected
@@ -11,7 +11,7 @@
   $ HGUSER= hg debuginstall
   Checking encoding (ascii)...
   Checking installed modules (*/mercurial)... (glob)
-  Checking templates...
+  Checking templates (*/mercurial/templates)... (glob)
   Checking commit editor...
   Checking username...
    no username supplied (see "hg help config")
--- a/tests/test-keyword.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-keyword.t	Sat Oct 15 14:30:50 2011 -0500
@@ -825,7 +825,7 @@
   ignore $Id$
 
   $ hg rollback
-  repository tip rolled back to revision 2 (undo commit)
+  repository tip rolled back to revision 2 (undo import)
   working directory now based on revision 2
   $ hg update --clean
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-largefiles.t	Sat Oct 15 14:30:50 2011 -0500
@@ -0,0 +1,445 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > largefiles=
+  > purge=
+  > rebase=
+  > [largefiles]
+  > size=2
+  > patterns=glob:**.dat
+  > EOF
+
+Create the repo with a couple of revisions of both large and normal
+files (testing that status correctly shows largefiles.
+
+  $ hg init a
+  $ cd a
+  $ mkdir sub
+  $ echo normal1 > normal1
+  $ echo normal2 > sub/normal2
+  $ echo large1 > large1
+  $ echo large2 > sub/large2
+  $ hg add normal1 sub/normal2
+  $ hg add --large large1 sub/large2
+  $ hg commit -m "add files"
+  $ echo normal11 > normal1
+  $ echo normal22 > sub/normal2
+  $ echo large11 > large1
+  $ echo large22 > sub/large2
+  $ hg st
+  M large1
+  M normal1
+  M sub/large2
+  M sub/normal2
+  $ hg commit -m "edit files"
+
+Verify that committing new versions of largefiles results in correct
+largefile contents, and also that non-largefiles are not affected
+badly.
+
+  $ cat normal1
+  normal11
+  $ cat large1
+  large11
+  $ cat sub/normal2
+  normal22
+  $ cat sub/large2
+  large22
+
+Verify removing largefiles and normal files works on largefile repos.
+ 
+  $ hg remove normal1 large1
+  $ hg commit -m "remove files"
+  $ ls
+  sub
+
+Test copying largefiles.
+
+  $ hg cp sub/normal2 normal1
+  $ hg cp sub/large2 large1
+  $ hg commit -m "copy files"
+  $ cat normal1
+  normal22
+  $ cat large1
+  large22
+
+Test moving largefiles and verify that normal files are also unaffected.
+
+  $ hg mv normal1 normal3
+  $ hg mv large1 large3
+  $ hg mv sub/normal2 sub/normal4
+  $ hg mv sub/large2 sub/large4
+  $ hg commit -m "move files"
+  $ cat normal3
+  normal22
+  $ cat large3
+  large22
+  $ cat sub/normal4
+  normal22
+  $ cat sub/large4
+  large22
+
+Test archiving the various revisions.  These hit corner cases known with
+archiving.
+
+  $ hg archive -r 0 ../archive0
+  $ hg archive -r 1 ../archive1
+  $ hg archive -r 2 ../archive2
+  $ hg archive -r 3 ../archive3
+  $ hg archive -r 4 ../archive4
+  $ cd ../archive0
+  $ cat normal1 
+  normal1
+  $ cat large1
+  large1
+  $ cat sub/normal2
+  normal2
+  $ cat sub/large2
+  large2
+  $ cd ../archive1
+  $ cat normal1
+  normal11
+  $ cat large1
+  large11
+  $ cat sub/normal2
+  normal22
+  $ cat sub/large2
+  large22
+  $ cd ../archive2
+  $ ls
+  sub
+  $ cat sub/normal2
+  normal22
+  $ cat sub/large2
+  large22
+  $ cd ../archive3
+  $ cat normal1
+  normal22
+  $ cat large1
+  large22
+  $ cat sub/normal2
+  normal22
+  $ cat sub/large2
+  large22
+  $ cd ../archive4
+  $ cat normal3
+  normal22
+  $ cat large3
+  large22
+  $ cat sub/normal4
+  normal22
+  $ cat sub/large4
+  large22
+
+Test a separate commit corner case (specifying files to commit) and check
+that the commited files have the right value.
+
+  $ cd ../a
+  $ echo normal3 > normal3
+  $ echo large3 > large3
+  $ echo normal4 > sub/normal4
+  $ echo large4 > sub/large4
+  $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
+  $ cat normal3
+  normal3
+  $ cat large3
+  large3
+  $ cat sub/normal4
+  normal4
+  $ cat sub/large4
+  large4
+
+Test one more commit corner case that has been known to break (comitting from
+a sub-directory of the repo).
+
+  $ cd ../a
+  $ echo normal33 > normal3
+  $ echo large33 > large3
+  $ echo normal44 > sub/normal4
+  $ echo large44 > sub/large4
+  $ cd sub
+  $ hg commit -m "edit files yet again"
+  $ cat ../normal3
+  normal33
+  $ cat ../large3
+  large33
+  $ cat normal4
+  normal44
+  $ cat large4
+  large44
+
+Check that committing standins is not allowed.
+
+  $ cd ..
+  $ echo large3 > large3
+  $ hg commit .hglf/large3 -m "try to commit standin"
+  abort: file ".hglf/large3" is a largefile standin
+  (commit the largefile itself instead)
+  [255]
+
+Test some cornercases for adding largefiles.
+
+  $ echo large5 > large5
+  $ hg add --large large5
+  $ hg add --large large5
+  large5 already a largefile
+  $ mkdir sub2
+  $ echo large6 > sub2/large6
+  $ echo large7 > sub2/large7
+  $ hg add --large sub2
+  adding sub2/large6 as a largefile
+  adding sub2/large7 as a largefile
+  $ hg st
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+
+Test that files get added as largefiles based on .hgrc settings
+
+  $ echo testdata > test.dat
+  $ dd bs=3145728 count=1 if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
+  $ hg add
+  adding reallylarge as a largefile
+  adding test.dat as a largefile
+  $ dd bs=1048576 count=1 if=/dev/zero of=reallylarge2 > /dev/null 2> /dev/null
+
+Test that specifying the --lsize command on the comand-line works
+
+  $ hg add --lfsize 1
+  adding reallylarge2 as a largefile
+
+Test forget on largefiles.
+
+  $ hg forget large3 large5 test.dat reallylarge reallylarge2
+  $ hg st
+  A sub2/large6
+  A sub2/large7
+  R large3
+  ? large5
+  ? reallylarge
+  ? reallylarge2
+  ? test.dat
+  $ hg commit -m "add/edit more largefiles"
+  $ hg st
+  ? large3
+  ? large5
+  ? reallylarge
+  ? reallylarge2
+  ? test.dat
+
+Test purge with largefiles (verify that largefiles get populated in the
+working copy correctly after a purge)
+
+  $ hg purge --all
+  $ cat sub/large4
+  large44
+  $ cat sub2/large6
+  large6
+  $ cat sub2/large7
+  large7
+
+Test cloning a largefiles repo.
+
+  $ cd ..
+  $ hg clone a b
+  updating to branch default
+  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  getting changed largefiles
+  3 largefiles updated, 0 removed
+  $ cd b
+  $ hg log
+  changeset:   7:daea875e9014
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add/edit more largefiles
+  
+  changeset:   6:4355d653f84f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files yet again
+  
+  changeset:   5:9d5af5072dbd
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files again
+  
+  changeset:   4:74c02385b94c
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     move files
+  
+  changeset:   3:9e8fbc4bce62
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     copy files
+  
+  changeset:   2:51a0ae4d5864
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     remove files
+  
+  changeset:   1:ce8896473775
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files
+  
+  changeset:   0:30d30fe6a5be
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add files
+  
+  $ cat normal3
+  normal33
+  $ cat sub/normal4
+  normal44
+  $ cat sub/large4
+  large44
+  $ cat sub2/large6
+  large6
+  $ cat sub2/large7
+  large7
+  $ cd ..
+  $ hg clone a -r 3 c
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 10 changes to 4 files
+  updating to branch default
+  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  getting changed largefiles
+  2 largefiles updated, 0 removed
+  $ cd c
+  $ hg log
+  changeset:   3:9e8fbc4bce62
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     copy files
+  
+  changeset:   2:51a0ae4d5864
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     remove files
+  
+  changeset:   1:ce8896473775
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files
+  
+  changeset:   0:30d30fe6a5be
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add files
+  
+  $ cat normal1
+  normal22
+  $ cat large1
+  large22
+  $ cat sub/normal2
+  normal22
+  $ cat sub/large2
+  large22
+
+Test that old revisions of a clone have correct largefiles content.  This also
+tsts update.
+
+  $ hg update -r 1 
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  getting changed largefiles
+  1 largefiles updated, 0 removed
+  $ cat large1
+  large11
+  $ cat sub/large2
+  large22
+
+Test that rebasing between two repositories does not revert largefiles to old
+revisions (this was a very bad bug that took a lot of work to fix).
+
+  $ cd ..
+  $ hg clone a d
+  updating to branch default
+  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  getting changed largefiles
+  3 largefiles updated, 0 removed
+  $ cd b
+  $ echo large4-modified > sub/large4
+  $ echo normal3-modified > normal3
+  $ hg commit -m "modify normal file and largefile in repo b"
+  $ cd ../d
+  $ echo large6-modified > sub2/large6
+  $ echo normal4-modified > sub/normal4
+  $ hg commit -m "modify normal file largefile in repo d"
+  $ hg pull --rebase ../b
+  pulling from ../b
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 2 changes to 2 files (+1 heads)
+  getting changed largefiles
+  1 largefiles updated, 0 removed
+  saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
+  nothing to rebase
+  $ hg log
+  changeset:   9:598410d3eb9a
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     modify normal file largefile in repo d
+  
+  changeset:   8:a381d2c8c80e
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     modify normal file and largefile in repo b
+  
+  changeset:   7:daea875e9014
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add/edit more largefiles
+  
+  changeset:   6:4355d653f84f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files yet again
+  
+  changeset:   5:9d5af5072dbd
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files again
+  
+  changeset:   4:74c02385b94c
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     move files
+  
+  changeset:   3:9e8fbc4bce62
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     copy files
+  
+  changeset:   2:51a0ae4d5864
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     remove files
+  
+  changeset:   1:ce8896473775
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     edit files
+  
+  changeset:   0:30d30fe6a5be
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add files
+  
+  $ cat normal3
+  normal3-modified
+  $ cat sub/normal4
+  normal4-modified
+  $ cat sub/large4
+  large4-modified
+  $ cat sub2/large6
+  large6-modified
+  $ cat sub2/large7
+  large7
--- a/tests/test-mactext.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-mactext.t	Sat Oct 15 14:30:50 2011 -0500
@@ -19,13 +19,11 @@
   [hooks]
   pretxncommit.cr = python:hgext.win32text.forbidcr
   pretxnchangegroup.cr = python:hgext.win32text.forbidcr
-  $ echo
-  
+
   $ echo hello > f
   $ hg add f
   $ hg ci -m 1
-  $ echo
-  
+
   $ python unix2mac.py f
   $ hg ci -m 2
   Attempt to commit or push text file(s) using CR line endings
--- a/tests/test-merge-tools.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-merge-tools.t	Sat Oct 15 14:30:50 2011 -0500
@@ -31,8 +31,7 @@
   $ hg commit -Am "revision 3"
   created new head
   $ echo "[merge-tools]" > .hg/hgrc
-  $ echo
-  
+
   $ beforemerge() {
   >   cat .hg/hgrc
   >   echo "# hg update -C 1"
@@ -44,7 +43,6 @@
   >   echo "# hg stat"
   >   hg stat
   >   rm -f f.orig
-  >   echo
   > }
   $ domerge() {
   >   beforemerge
@@ -52,14 +50,9 @@
   >   hg merge $*
   >   aftermerge
   > }
-  $ echo
-  
 
 Tool selection
 
-  $ echo
-  
-
 default is internal merge:
 
   $ beforemerge
@@ -88,7 +81,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 simplest hgrc using false for merge:
 
@@ -108,7 +100,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 true with higher .priority gets precedence:
 
@@ -127,7 +118,6 @@
   space
   # hg stat
   M f
-  
 
 unless lowered on command line:
 
@@ -147,7 +137,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 or false set higher on command line:
 
@@ -167,7 +156,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 or true.executable not found in PATH:
 
@@ -187,7 +175,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 or true.executable with bogus path:
 
@@ -207,7 +194,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 but true.executable set to cat found in PATH works:
 
@@ -233,7 +219,6 @@
   space
   # hg stat
   M f
-  
 
 and true.executable set to cat with path works:
 
@@ -258,15 +243,33 @@
   space
   # hg stat
   M f
-  
-  $ echo
-  
+
+environment variables in true.executable are handled:
+
+  $ cat > $HGTMP/merge.sh <<EOF
+  > #!/bin/sh
+  > echo 'custom merge tool'
+  > EOF
+  $ chmod +x $HGTMP/merge.sh
+  $ domerge -r 2 --config merge-tools.true.executable='$HGTMP/merge.sh'
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  # hg merge -r 2 --config merge-tools.true.executable=$HGTMP/merge.sh
+  merging f
+  custom merge tool
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
 
 Tool selection and merge-patterns
 
-  $ echo
-  
-
 merge-patterns specifies new tool false:
 
   $ domerge -r 2 --config merge-patterns.f=false
@@ -286,7 +289,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 merge-patterns specifies executable not found in PATH and gets warning:
 
@@ -308,7 +310,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 merge-patterns specifies executable with bogus path and gets warning:
 
@@ -330,15 +331,9 @@
   # hg stat
   M f
   ? f.orig
-  
-  $ echo
-  
 
 ui.merge overrules priority
 
-  $ echo
-  
-
 ui.merge specifies false:
 
   $ domerge -r 2 --config ui.merge=false
@@ -358,7 +353,6 @@
   # hg stat
   M f
   ? f.orig
-  
 
 ui.merge specifies internal:fail:
 
@@ -376,7 +370,6 @@
   space
   # hg stat
   M f
-  
 
 ui.merge specifies internal:local:
 
@@ -394,7 +387,6 @@
   space
   # hg stat
   M f
-  
 
 ui.merge specifies internal:other:
 
@@ -412,7 +404,6 @@
   space
   # hg stat
   M f
-  
 
 ui.merge specifies internal:prompt:
 
@@ -432,7 +423,6 @@
   space
   # hg stat
   M f
-  
 
 ui.merge specifies internal:dump:
 
@@ -455,7 +445,6 @@
   ? f.local
   ? f.orig
   ? f.other
-  
 
 f.base:
 
@@ -475,8 +464,6 @@
   revision 2
   space
   $ rm f.base f.local f.other
-  $ echo
-  
 
 ui.merge specifies internal:other but is overruled by pattern for false:
 
@@ -497,15 +484,9 @@
   # hg stat
   M f
   ? f.orig
-  
-  $ echo
-  
 
 Premerge
 
-  $ echo
-  
-
 ui.merge specifies internal:other but is overruled by --tool=false
 
   $ domerge -r 2 --config ui.merge=internal:other --tool=false
@@ -525,7 +506,7 @@
   # hg stat
   M f
   ? f.orig
-  
+
 HGMERGE specifies internal:other but is overruled by --tool=false
 
   $ HGMERGE=internal:other ; export HGMERGE
@@ -546,7 +527,7 @@
   # hg stat
   M f
   ? f.orig
-  
+
   $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
 
 Default is silent simplemerge:
@@ -567,7 +548,6 @@
   revision 3
   # hg stat
   M f
-  
 
 .premerge=True is same:
 
@@ -587,7 +567,6 @@
   revision 3
   # hg stat
   M f
-  
 
 .premerge=False executes merge-tool:
 
@@ -613,16 +592,11 @@
   space
   # hg stat
   M f
-  
-  $ echo
-  
 
 Tool execution
 
-  $ echo
-  
-  $ echo '# set tools.args explicit to include $base $local $other $output:' # default '$local $base $other'
-  # set tools.args explicit to include $base $local $other $output:
+set tools.args explicit to include $base $local $other $output:
+
   $ beforemerge
   [merge-tools]
   false.whatever=
@@ -655,9 +629,9 @@
   space
   # hg stat
   M f
-  
-  $ echo '# Merge with "echo mergeresult > $local":'
-  # Merge with "echo mergeresult > $local":
+
+Merge with "echo mergeresult > $local":
+
   $ beforemerge
   [merge-tools]
   false.whatever=
@@ -673,9 +647,9 @@
   mergeresult
   # hg stat
   M f
-  
-  $ echo '# - and $local is the file f:'
-  # - and $local is the file f:
+
+- and $local is the file f:
+
   $ beforemerge
   [merge-tools]
   false.whatever=
@@ -691,9 +665,9 @@
   mergeresult
   # hg stat
   M f
-  
-  $ echo '# Merge with "echo mergeresult > $output" - the variable is a bit magic:'
-  # Merge with "echo mergeresult > $output" - the variable is a bit magic:
+
+Merge with "echo mergeresult > $output" - the variable is a bit magic:
+
   $ beforemerge
   [merge-tools]
   false.whatever=
@@ -709,7 +683,6 @@
   mergeresult
   # hg stat
   M f
-  
 
 Merge using tool with a path that must be quoted:
 
@@ -739,15 +712,9 @@
   space
   # hg stat
   M f
-  
-  $ echo
-  
 
 Merge post-processing
 
-  $ echo
-  
-
 cat is a bad merge-tool and doesn't change:
 
   $ domerge -y -r 2 --config merge-tools.true.checkchanged=1
@@ -775,4 +742,3 @@
   # hg stat
   M f
   ? f.orig
-  
--- a/tests/test-merge1.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-merge1.t	Sat Oct 15 14:30:50 2011 -0500
@@ -110,8 +110,7 @@
   $ hg merge 2
   abort: outstanding uncommitted changes (use 'hg status' to list changes)
   [255]
-  $ echo %% merge expected!
-  %% merge expected!
+merge expected!
   $ hg merge -f 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
--- a/tests/test-minirst.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-minirst.py	Sat Oct 15 14:30:50 2011 -0500
@@ -1,19 +1,30 @@
 from pprint import pprint
 from mercurial import minirst
 
-def debugformat(title, text, width, **kwargs):
-    print "%s formatted to fit within %d characters:" % (title, width)
+def debugformat(text, form, **kwargs):
+    if form == 'html':
+        print "html format:"
+        out = minirst.format(text, style=form, **kwargs)
+    else:
+        print "%d column format:" % form
+        out = minirst.format(text, width=form, **kwargs)
+
     print "-" * 70
-    formatted = minirst.format(text, width, **kwargs)
-    if type(formatted) == tuple:
-        print formatted[0]
+    if type(out) == tuple:
+        print out[0][:-1]
         print "-" * 70
-        pprint(formatted[1])
+        pprint(out[1])
     else:
-        print formatted
+        print out[:-1]
     print "-" * 70
     print
 
+def debugformats(title, text, **kwargs):
+    print "== %s ==" % title
+    debugformat(text, 60, **kwargs)
+    debugformat(text, 30, **kwargs)
+    debugformat(text, 'html', **kwargs)
+
 paragraphs = """
 This is some text in the first paragraph.
 
@@ -23,9 +34,7 @@
  \n  \n   \nThe third and final paragraph.
 """
 
-debugformat('paragraphs', paragraphs, 60)
-debugformat('paragraphs', paragraphs, 30)
-
+debugformats('paragraphs', paragraphs)
 
 definitions = """
 A Term
@@ -40,9 +49,7 @@
     Definition.
 """
 
-debugformat('definitions', definitions, 60)
-debugformat('definitions', definitions, 30)
-
+debugformats('definitions', definitions)
 
 literals = r"""
 The fully minimized form is the most
@@ -66,9 +73,7 @@
       with '::' disappears in the final output.
 """
 
-debugformat('literals', literals, 60)
-debugformat('literals', literals, 30)
-
+debugformats('literals', literals)
 
 lists = """
 - This is the first list item.
@@ -112,9 +117,7 @@
 | This is the second line.
 """
 
-debugformat('lists', lists, 60)
-debugformat('lists', lists, 30)
-
+debugformats('lists', lists)
 
 options = """
 There is support for simple option lists,
@@ -140,9 +143,7 @@
 --foo bar baz
 """
 
-debugformat('options', options, 60)
-debugformat('options', options, 30)
-
+debugformats('options', options)
 
 fields = """
 :a: First item.
@@ -155,8 +156,7 @@
 :much too large: This key is big enough to get its own line.
 """
 
-debugformat('fields', fields, 60)
-debugformat('fields', fields, 30)
+debugformats('fields', fields)
 
 containers = """
 Normal output.
@@ -174,14 +174,14 @@
       Debug output.
 """
 
-debugformat('containers (normal)', containers, 60)
-debugformat('containers (verbose)', containers, 60, keep=['verbose'])
-debugformat('containers (debug)', containers, 60, keep=['debug'])
-debugformat('containers (verbose debug)', containers, 60,
+debugformats('containers (normal)', containers)
+debugformats('containers (verbose)', containers, keep=['verbose'])
+debugformats('containers (debug)', containers, keep=['debug'])
+debugformats('containers (verbose debug)', containers,
             keep=['verbose', 'debug'])
 
 roles = """Please see :hg:`add`."""
-debugformat('roles', roles, 60)
+debugformats('roles', roles)
 
 
 sections = """
@@ -197,7 +197,7 @@
 Markup: ``foo`` and :hg:`help`
 ------------------------------
 """
-debugformat('sections', sections, 20)
+debugformats('sections', sections)
 
 
 admonitions = """
@@ -214,7 +214,7 @@
    This is danger
 """
 
-debugformat('admonitions', admonitions, 30)
+debugformats('admonitions', admonitions)
 
 comments = """
 Some text.
@@ -230,4 +230,15 @@
 Empty comment above
 """
 
-debugformat('comments', comments, 30)
+debugformats('comments', comments)
+
+
+data = [['a', 'b', 'c'],
+         ['1', '2', '3'],
+         ['foo', 'bar', 'baz this list is very very very long man']]
+
+table = minirst.maketable(data, 2, True)
+
+print table
+
+debugformats('table', table)
--- a/tests/test-minirst.py.out	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-minirst.py.out	Sat Oct 15 14:30:50 2011 -0500
@@ -1,4 +1,5 @@
-paragraphs formatted to fit within 60 characters:
+== paragraphs ==
+60 column format:
 ----------------------------------------------------------------------
 This is some text in the first paragraph.
 
@@ -8,7 +9,7 @@
 The third and final paragraph.
 ----------------------------------------------------------------------
 
-paragraphs formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 This is some text in the first
 paragraph.
@@ -21,7 +22,23 @@
 The third and final paragraph.
 ----------------------------------------------------------------------
 
-definitions formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<p>
+This is some text in the first paragraph.
+</p>
+<p>
+A small indented paragraph.
+It is followed by some lines
+containing random whitespace.
+</p>
+<p>
+The third and final paragraph.
+</p>
+----------------------------------------------------------------------
+
+== definitions ==
+60 column format:
 ----------------------------------------------------------------------
 A Term
   Definition. The indented lines make up the definition.
@@ -35,7 +52,7 @@
     Definition.
 ----------------------------------------------------------------------
 
-definitions formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 A Term
   Definition. The indented
@@ -54,7 +71,20 @@
     Definition.
 ----------------------------------------------------------------------
 
-literals formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<dl>
+ <dt>A Term
+ <dd>Definition. The indented lines make up the definition.
+ <dt>Another Term
+ <dd>Another definition. The final line in the definition determines the indentation, so this will be indented with four spaces.
+ <dt>A Nested/Indented Term
+ <dd>Definition.
+</dl>
+----------------------------------------------------------------------
+
+== literals ==
+60 column format:
 ----------------------------------------------------------------------
 The fully minimized form is the most convenient form:
 
@@ -74,7 +104,7 @@
       with '::' disappears in the final output.
 ----------------------------------------------------------------------
 
-literals formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 The fully minimized form is
 the most convenient form:
@@ -96,7 +126,35 @@
       with '::' disappears in the final output.
 ----------------------------------------------------------------------
 
-lists formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<p>
+The fully minimized form is the most
+convenient form:
+</p>
+<pre>
+Hello
+  literal
+    world
+</pre>
+<p>
+In the partially minimized form a paragraph
+simply ends with space-double-colon.
+</p>
+<pre>
+////////////////////////////////////////
+long un-wrapped line in a literal block
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+</pre>
+<pre>
+This literal block is started with '::',
+  the so-called expanded form. The paragraph
+    with '::' disappears in the final output.
+</pre>
+----------------------------------------------------------------------
+
+== lists ==
+60 column format:
 ----------------------------------------------------------------------
 - This is the first list item.
 
@@ -131,7 +189,7 @@
 This is the second line.
 ----------------------------------------------------------------------
 
-lists formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 - This is the first list item.
 
@@ -175,7 +233,53 @@
 This is the second line.
 ----------------------------------------------------------------------
 
-options formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<ul>
+ <li> This is the first list item.
+<p>
+Second paragraph in the first list item.
+</p>
+ <li> List items need not be separated   by a blank line.
+ <li> And will be rendered without   one in any case.
+</ul>
+<p>
+We can have indented lists:
+</p>
+<ul>
+ <li> This is an indented list item
+ <li> Another indented list item:
+<pre>
+- A literal block in the middle
+      of an indented list.
+</pre>
+<pre>
+(The above is not a list item since we are in the literal block.)
+</pre>
+</ul>
+<pre>
+Literal block with no indentation (apart from
+the two spaces added to all literal blocks).
+</pre>
+<ol>
+ <li> This is an enumerated list (first item).
+ <li> Continuing with the second item.
+ <li> foo
+ <li> bar
+ <li> Another
+ <li> List
+</ol>
+<p>
+Line blocks are also a form of list:
+</p>
+<ol>
+ <li> This is the first line.   The line continues here.
+ <li> This is the second line.
+</ol>
+----------------------------------------------------------------------
+
+== options ==
+60 column format:
 ----------------------------------------------------------------------
 There is support for simple option lists, but only with long
 options:
@@ -202,7 +306,7 @@
 --foo bar baz
 ----------------------------------------------------------------------
 
-options formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 There is support for simple
 option lists, but only with
@@ -274,7 +378,41 @@
 --foo bar baz
 ----------------------------------------------------------------------
 
-fields formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<p>
+There is support for simple option lists,
+but only with long options:
+</p>
+<dl>
+ <dt>-X --exclude filter
+ <dd>an option with a short and long option with an argument
+ <dt>-I --include
+ <dd>an option with both a short option and a long option
+ <dt>   --all
+ <dd>Output all.
+ <dt>   --both
+ <dd>Output both (this description is quite long).
+ <dt>   --long
+ <dd>Output all day long.
+ <dt>   --par
+ <dd>This option has two paragraphs in its description. This is the first.
+<p>
+This is the second.  Blank lines may be omitted between
+options (as above) or left in (as here).
+</p>
+</dl>
+<p>
+The next paragraph looks like an option list, but lacks the two-space
+marker after the option. It is treated as a normal paragraph:
+</p>
+<p>
+--foo bar baz
+</p>
+----------------------------------------------------------------------
+
+== fields ==
+60 column format:
 ----------------------------------------------------------------------
 a   First item.
 ab  Second item. Indentation and wrapping is handled
@@ -288,7 +426,7 @@
             This key is big enough to get its own line.
 ----------------------------------------------------------------------
 
-fields formatted to fit within 30 characters:
+30 column format:
 ----------------------------------------------------------------------
 a   First item.
 ab  Second item. Indentation
@@ -307,12 +445,45 @@
             own line.
 ----------------------------------------------------------------------
 
-containers (normal) formatted to fit within 60 characters:
+html format:
+----------------------------------------------------------------------
+<dl>
+ <dt>a
+ <dd>First item.
+ <dt>ab
+ <dd>Second item. Indentation and wrapping is handled automatically.
+</dl>
+<p>
+Next list:
+</p>
+<dl>
+ <dt>small
+ <dd>The larger key below triggers full indentation here.
+ <dt>much too large
+ <dd>This key is big enough to get its own line.
+</dl>
+----------------------------------------------------------------------
+
+== containers (normal) ==
+60 column format:
 ----------------------------------------------------------------------
 Normal output.
 ----------------------------------------------------------------------
 
-containers (verbose) formatted to fit within 60 characters:
+30 column format:
+----------------------------------------------------------------------
+Normal output.
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<p>
+Normal output.
+</p>
+----------------------------------------------------------------------
+
+== containers (verbose) ==
+60 column format:
 ----------------------------------------------------------------------
 Normal output.
 
@@ -321,7 +492,29 @@
 ['debug', 'debug']
 ----------------------------------------------------------------------
 
-containers (debug) formatted to fit within 60 characters:
+30 column format:
+----------------------------------------------------------------------
+Normal output.
+
+Verbose output.
+----------------------------------------------------------------------
+['debug', 'debug']
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<p>
+Normal output.
+</p>
+<p>
+Verbose output.
+</p>
+----------------------------------------------------------------------
+['debug', 'debug']
+----------------------------------------------------------------------
+
+== containers (debug) ==
+60 column format:
 ----------------------------------------------------------------------
 Normal output.
 
@@ -330,7 +523,29 @@
 ['verbose']
 ----------------------------------------------------------------------
 
-containers (verbose debug) formatted to fit within 60 characters:
+30 column format:
+----------------------------------------------------------------------
+Normal output.
+
+Initial debug output.
+----------------------------------------------------------------------
+['verbose']
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<p>
+Normal output.
+</p>
+<p>
+Initial debug output.
+</p>
+----------------------------------------------------------------------
+['verbose']
+----------------------------------------------------------------------
+
+== containers (verbose debug) ==
+60 column format:
 ----------------------------------------------------------------------
 Normal output.
 
@@ -343,12 +558,57 @@
 []
 ----------------------------------------------------------------------
 
-roles formatted to fit within 60 characters:
+30 column format:
+----------------------------------------------------------------------
+Normal output.
+
+Initial debug output.
+
+Verbose output.
+
+Debug output.
+----------------------------------------------------------------------
+[]
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<p>
+Normal output.
+</p>
+<p>
+Initial debug output.
+</p>
+<p>
+Verbose output.
+</p>
+<p>
+Debug output.
+</p>
+----------------------------------------------------------------------
+[]
+----------------------------------------------------------------------
+
+== roles ==
+60 column format:
 ----------------------------------------------------------------------
 Please see "hg add".
 ----------------------------------------------------------------------
 
-sections formatted to fit within 20 characters:
+30 column format:
+----------------------------------------------------------------------
+Please see "hg add".
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<p>
+Please see "hg add".
+</p>
+----------------------------------------------------------------------
+
+== sections ==
+60 column format:
 ----------------------------------------------------------------------
 Title
 =====
@@ -363,7 +623,46 @@
 ---------------------------
 ----------------------------------------------------------------------
 
-admonitions formatted to fit within 30 characters:
+30 column format:
+----------------------------------------------------------------------
+Title
+=====
+
+Section
+-------
+
+Subsection
+''''''''''
+
+Markup: "foo" and "hg help"
+---------------------------
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<h1>Title</h1>
+<h2>Section</h2>
+<h3>Subsection</h3>
+<h2>Markup: "foo" and "hg help"</h2>
+----------------------------------------------------------------------
+
+== admonitions ==
+60 column format:
+----------------------------------------------------------------------
+Note:
+   This is a note
+
+   - Bullet 1
+   - Bullet 2
+
+   Warning!
+      This is a warning Second input line of warning
+
+!Danger!
+   This is danger
+----------------------------------------------------------------------
+
+30 column format:
 ----------------------------------------------------------------------
 Note:
    This is a note
@@ -379,7 +678,34 @@
    This is danger
 ----------------------------------------------------------------------
 
-comments formatted to fit within 30 characters:
+html format:
+----------------------------------------------------------------------
+<p>
+<b>Note:</b> This is a note
+</p>
+<ul>
+ <li> Bullet 1
+ <li> Bullet 2
+</ul>
+<p>
+<b>Warning!</b> This is a warning Second input line of warning
+</p>
+<p>
+<b>!Danger!</b> This is danger
+</p>
+----------------------------------------------------------------------
+
+== comments ==
+60 column format:
+----------------------------------------------------------------------
+Some text.
+
+   Some indented text.
+
+Empty comment above
+----------------------------------------------------------------------
+
+30 column format:
 ----------------------------------------------------------------------
 Some text.
 
@@ -388,3 +714,51 @@
 Empty comment above
 ----------------------------------------------------------------------
 
+html format:
+----------------------------------------------------------------------
+<p>
+Some text.
+</p>
+<p>
+Some indented text.
+</p>
+<p>
+Empty comment above
+</p>
+----------------------------------------------------------------------
+
+  === === ========================================
+  a   b   c                                       
+  === === ========================================
+  1   2   3                                       
+  foo bar baz this list is very very very long man
+  === === ========================================
+
+== table ==
+60 column format:
+----------------------------------------------------------------------
+  a   b   c
+  ------------------------------------------------
+  1   2   3
+  foo bar baz this list is very very very long man
+----------------------------------------------------------------------
+
+30 column format:
+----------------------------------------------------------------------
+  a   b   c
+  ------------------------------
+  1   2   3
+  foo bar baz this list is
+          very very very long
+          man
+----------------------------------------------------------------------
+
+html format:
+----------------------------------------------------------------------
+<table>
+ <tr><th>a</th><th>b</th><th>c</th></tr>
+ <tr><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td>foo</td><td>bar</td><td>baz this list is very very very long man</td></tr>
+</table>
+----------------------------------------------------------------------
+
--- a/tests/test-mq-guards.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-mq-guards.t	Sat Oct 15 14:30:50 2011 -0500
@@ -434,3 +434,71 @@
 
   $ hg --config extensions.color= --config color.mode=ansi qseries -m --color=always
   \x1b[0;31;1mb.patch\x1b[0m (esc)
+
+
+excercise cornercases in "qselect --reapply"
+
+  $ hg qpop -a
+  popping c.patch
+  popping new.patch
+  patch queue now empty
+  $ hg qguard -- new.patch -not-new
+  $ hg qguard -- c.patch -not-c
+  $ hg qguard -- d.patch -not-d
+  $ hg qpush -a
+  applying new.patch
+  applying c.patch
+  applying d.patch
+  patch d.patch is empty
+  now at: d.patch
+  $ hg qguard -l
+  new.patch: -not-new
+  c.patch: -not-c
+  d.patch: -not-d
+  $ hg qselect --reapply not-d
+  popping guarded patches
+  popping d.patch
+  now at: c.patch
+  reapplying unguarded patches
+  cannot push 'd.patch' - guarded by '-not-d'
+  $ hg qser -v
+  0 A new.patch
+  1 A c.patch
+  2 G d.patch
+  $ hg qselect --reapply -n
+  guards deactivated
+  $ hg qpush
+  applying d.patch
+  patch d.patch is empty
+  now at: d.patch
+  $ hg qser -v
+  0 A new.patch
+  1 A c.patch
+  2 A d.patch
+  $ hg qselect --reapply not-c
+  popping guarded patches
+  popping d.patch
+  popping c.patch
+  now at: new.patch
+  reapplying unguarded patches
+  applying d.patch
+  patch d.patch is empty
+  now at: d.patch
+  $ hg qser -v
+  0 A new.patch
+  1 G c.patch
+  2 A d.patch
+  $ hg qselect --reapply not-new
+  popping guarded patches
+  popping d.patch
+  popping new.patch
+  patch queue now empty
+  reapplying unguarded patches
+  applying c.patch
+  applying d.patch
+  patch d.patch is empty
+  now at: d.patch
+  $ hg qser -v
+  0 G new.patch
+  1 A c.patch
+  2 A d.patch
--- a/tests/test-mq-qimport-fail-cleanup.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-mq-qimport-fail-cleanup.t	Sat Oct 15 14:30:50 2011 -0500
@@ -16,14 +16,10 @@
   >  a
   > +b
   > EOF
-  $ echo
-  
 
 empty series
 
   $ hg qseries
-  $ echo
-  
 
 qimport valid patch followed by invalid patch
 
@@ -31,8 +27,6 @@
   adding b.patch to series file
   abort: unable to read file fakepatch
   [255]
-  $ echo
-  
 
 valid patches before fail added to series
 
--- a/tests/test-mq-qrefresh-interactive.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-mq-qrefresh-interactive.t	Sat Oct 15 14:30:50 2011 -0500
@@ -31,22 +31,22 @@
   
   options:
   
-   -e --edit                 edit commit message
-   -g --git                  use git extended diff format
-   -s --short                refresh only files already in the patch and
-                             specified files
-   -U --currentuser          add/update author field in patch with current user
-   -u --user USER            add/update author field in patch with given user
-   -D --currentdate          add/update date field in patch with current date
-   -d --date DATE            add/update date field in patch with given date
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -m --message TEXT         use text as commit message
-   -l --logfile FILE         read commit message from file
+   -e --edit                edit commit message
+   -g --git                 use git extended diff format
+   -s --short               refresh only files already in the patch and
+                            specified files
+   -U --currentuser         add/update author field in patch with current user
+   -u --user USER           add/update author field in patch with given user
+   -D --currentdate         add/update date field in patch with current date
+   -d --date DATE           add/update date field in patch with given date
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -m --message TEXT        use text as commit message
+   -l --logfile FILE        read commit message from file
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help qrefresh" to show global options
+  use "hg -v help qrefresh" to show more info
 
 help qrefresh (record)
 
@@ -75,23 +75,23 @@
   
   options:
   
-   -e --edit                 edit commit message
-   -g --git                  use git extended diff format
-   -s --short                refresh only files already in the patch and
-                             specified files
-   -U --currentuser          add/update author field in patch with current user
-   -u --user USER            add/update author field in patch with given user
-   -D --currentdate          add/update date field in patch with current date
-   -d --date DATE            add/update date field in patch with given date
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -m --message TEXT         use text as commit message
-   -l --logfile FILE         read commit message from file
-   -i --interactive          interactively select changes to refresh
+   -e --edit                edit commit message
+   -g --git                 use git extended diff format
+   -s --short               refresh only files already in the patch and
+                            specified files
+   -U --currentuser         add/update author field in patch with current user
+   -u --user USER           add/update author field in patch with given user
+   -D --currentdate         add/update date field in patch with current date
+   -d --date DATE           add/update date field in patch with given date
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -m --message TEXT        use text as commit message
+   -l --logfile FILE        read commit message from file
+   -i --interactive         interactively select changes to refresh
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help qrefresh" to show global options
+  use "hg -v help qrefresh" to show more info
 
   $ hg init a
   $ cd a
--- a/tests/test-notify-changegroup.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-notify-changegroup.t	Sat Oct 15 14:30:50 2011 -0500
@@ -72,4 +72,55 @@
   @@ -0,0 +1,2 @@
   +a
   +a
+  $ hg --cwd a rollback
+  repository tip rolled back to revision -1 (undo push)
 
+unbundle with unrelated source
+
+  $ hg --cwd b bundle ../test.hg ../a
+  searching for changes
+  2 changesets found
+  $ hg --cwd a unbundle ../test.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hg --cwd a rollback
+  repository tip rolled back to revision -1 (undo unbundle)
+
+unbundle with correct source
+
+  $ hg --config notify.sources=unbundle --cwd a unbundle ../test.hg 2>&1 |
+  >     python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  Date: * (glob)
+  Subject: * (glob)
+  From: test
+  X-Hg-Notification: changeset cb9a9f314b8b
+  Message-Id: <*> (glob)
+  To: baz, foo@bar
+  
+  changeset cb9a9f314b8b in $TESTTMP/a
+  details: $TESTTMP/a?cmd=changeset;node=cb9a9f314b8b
+  summary: a
+  
+  changeset ba677d0156c1 in $TESTTMP/a
+  details: $TESTTMP/a?cmd=changeset;node=ba677d0156c1
+  summary: b
+  
+  diffs (6 lines):
+  
+  diff -r 000000000000 -r ba677d0156c1 a
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/a	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,2 @@
+  +a
+  +a
+  (run 'hg update' to get a working copy)
--- a/tests/test-notify.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-notify.t	Sat Oct 15 14:30:50 2011 -0500
@@ -17,67 +17,111 @@
   > * = baz
   > EOF
   $ hg help notify
-  notify extension - hooks for sending email notifications at commit/push time
-  
-  Subscriptions can be managed through a hgrc file. Default mode is to print
-  messages to stdout, for testing and configuring.
+  notify extension - hooks for sending email push notifications
   
-  To use, configure the notify extension and enable it in hgrc like this:
+  This extension let you run hooks sending email notifications when changesets
+  are being pushed, from the sending or receiving side.
   
-    [extensions]
-    notify =
+  First, enable the extension as explained in "hg help extensions", and register
+  the hook you want to run. "incoming" and "outgoing" hooks are run by the
+  changesets receiver while the "outgoing" one is for the sender:
   
     [hooks]
     # one email for each incoming changeset
     incoming.notify = python:hgext.notify.hook
-    # batch emails when many changesets incoming at one time
+    # one email for all incoming changesets
     changegroup.notify = python:hgext.notify.hook
-    # batch emails when many changesets outgoing at one time (client side)
+  
+    # one email for all outgoing changesets
     outgoing.notify = python:hgext.notify.hook
   
-    [notify]
-    # config items go here
-  
-  Required configuration items:
-  
-    config = /path/to/file # file containing subscriptions
-  
-  Optional configuration items:
-  
-    test = True            # print messages to stdout for testing
-    strip = 3              # number of slashes to strip for url paths
-    domain = example.com   # domain to use if committer missing domain
-    style = ...            # style file to use when formatting email
-    template = ...         # template to use when formatting email
-    incoming = ...         # template to use when run as incoming hook
-    outgoing = ...         # template to use when run as outgoing hook
-    changegroup = ...      # template to use when run as changegroup hook
-    maxdiff = 300          # max lines of diffs to include (0=none, -1=all)
-    maxsubject = 67        # truncate subject line longer than this
-    diffstat = True        # add a diffstat before the diff content
-    sources = serve        # notify if source of incoming changes in this list
-                           # (serve == ssh or http, push, pull, bundle)
-    merge = False          # send notification for merges (default True)
-    [email]
-    from = user@host.com   # email address to send as if none given
-    [web]
-    baseurl = http://hgserver/... # root of hg web site for browsing commits
-  
-  The notify config file has same format as a regular hgrc file. It has two
-  sections so you can express subscriptions in whatever way is handier for you.
+  Now the hooks are running, subscribers must be assigned to repositories. Use
+  the "[usersubs]" section to map repositories to a given email or the
+  "[reposubs]" section to map emails to a single repository:
   
     [usersubs]
-    # key is subscriber email, value is ","-separated list of glob patterns
+    # key is subscriber email, value is a comma-separated list of glob
+    # patterns
     user@host = pattern
   
     [reposubs]
-    # key is glob pattern, value is ","-separated list of subscriber emails
+    # key is glob pattern, value is a comma-separated list of subscriber
+    # emails
     pattern = user@host
   
-  Glob patterns are matched against path to repository root.
+  Glob patterns are matched against absolute path to repository root. The
+  subscriptions can be defined in their own file and referenced with:
+  
+    [notify]
+    config = /path/to/subscriptionsfile
+  
+  Alternatively, they can be added to Mercurial configuration files by setting
+  the previous entry to an empty value.
+  
+  At this point, notifications should be generated but will not be sent until
+  you set the "notify.test" entry to "False".
+  
+  Notifications content can be tweaked with the following configuration entries:
+  
+  notify.test
+    If "True", print messages to stdout instead of sending them. Default: True.
+  
+  notify.sources
+    Space separated list of change sources. Notifications are sent only if it
+    includes the incoming or outgoing changes source. Incoming sources can be
+    "serve" for changes coming from http or ssh, "pull" for pulled changes,
+    "unbundle" for changes added by "hg unbundle" or "push" for changes being
+    pushed locally. Outgoing sources are the same except for "unbundle" which is
+    replaced by "bundle". Default: serve.
+  
+  notify.strip
+    Number of leading slashes to strip from url paths. By default, notifications
+    references repositories with their absolute path. "notify.strip" let you
+    turn them into relative paths. For example, "notify.strip=3" will change
+    "/long/path/repository" into "repository". Default: 0.
+  
+  notify.domain
+    If subscribers emails or the from email have no domain set, complete them
+    with this value.
   
-  If you like, you can put notify config file in repository that users can push
-  changes to, they can manage their own subscriptions.
+  notify.style
+    Style file to use when formatting emails.
+  
+  notify.template
+    Template to use when formatting emails.
+  
+  notify.incoming
+    Template to use when run as incoming hook, override "notify.template".
+  
+  notify.outgoing
+    Template to use when run as outgoing hook, override "notify.template".
+  
+  notify.changegroup
+    Template to use when running as changegroup hook, override
+    "notify.template".
+  
+  notify.maxdiff
+    Maximum number of diff lines to include in notification email. Set to 0 to
+    disable the diff, -1 to include all of it. Default: 300.
+  
+  notify.maxsubject
+    Maximum number of characters in emails subject line. Default: 67.
+  
+  notify.diffstat
+    Set to True to include a diffstat before diff content. Default: True.
+  
+  notify.merge
+    If True, send notifications for merge changesets. Default: True.
+  
+  If set, the following entries will also be used to customize the
+  notifications:
+  
+  email.from
+    Email "From" address to use if none can be found in generated email content.
+  
+  web.baseurl
+    Root repository browsing URL to combine with repository paths when making
+    references. See also "notify.strip".
   
   no commands defined
   $ hg init a
@@ -156,7 +200,6 @@
 
   $ hg --cwd b rollback
   repository tip rolled back to revision 0 (undo pull)
-  working directory now based on revision 0
   $ hg --cwd b pull ../a 2>&1 | grep 'error.*\.notify\.conf' > /dev/null && echo pull failed
   pull failed
   $ touch ".notify.conf"
@@ -165,7 +208,6 @@
 
   $ hg --cwd b rollback
   repository tip rolled back to revision 0 (undo pull)
-  working directory now based on revision 0
   $ hg --traceback --cwd b pull ../a  | \
   >   python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
   pulling from ../a
@@ -210,7 +252,6 @@
 
   $ hg --cwd b rollback
   repository tip rolled back to revision 0 (undo pull)
-  working directory now based on revision 0
   $ hg --traceback --cwd b pull ../a | \
   >   python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
   pulling from ../a
--- a/tests/test-patch-offset.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-patch-offset.t	Sat Oct 15 14:30:50 2011 -0500
@@ -69,6 +69,7 @@
   Hunk #2 succeeded at 87 (offset 34 lines).
   Hunk #3 succeeded at 109 (offset 34 lines).
   a
+  created 189885cecb41
 
 compare imported changes against reference file
 
--- a/tests/test-patch.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-patch.t	Sat Oct 15 14:30:50 2011 -0500
@@ -37,7 +37,7 @@
   $ hg --cwd b import -v ../a.diff
   applying ../a.diff
   Using custom patch
-
+  applied to working directory
 
 Issue2417: hg import with # comments in description
 
--- a/tests/test-patchbomb.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-patchbomb.t	Sat Oct 15 14:30:50 2011 -0500
@@ -1469,13 +1469,71 @@
   +ff2c9fa2018b15fa74b33363bda9527323e2a99f two
   +ff2c9fa2018b15fa74b33363bda9527323e2a99f two.diff
   
-
+no intro message in non-interactive mode
   $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
-  >  -r 0:1
+  >  -r 0:1 | fixheaders
   This patch series consists of 2 patches.
   
-  abort: Subject: [PATCH 0 of 2] Please enter a valid value
-  [255]
+  Subject: [PATCH 0 of 2] 
+  
+  Displaying [PATCH 1 of 2] a ...
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  Subject: [PATCH 1 of 2] a
+  X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
+  Message-Id: <8580ff50825a50c8f716.60@
+  In-Reply-To: <baz>
+  References: <baz>
+  User-Agent: Mercurial-patchbomb
+  Date: Thu, 01 Jan 1970 00:01:00 +0000
+  From: quux
+  To: foo
+  Cc: bar
+  
+  # HG changeset patch
+  # User test
+  # Date 1 0
+  # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
+  # Parent  0000000000000000000000000000000000000000
+  a
+  
+  diff -r 000000000000 -r 8580ff50825a a
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/a	Thu Jan 01 00:00:01 1970 +0000
+  @@ -0,0 +1,1 @@
+  +a
+  
+  Displaying [PATCH 2 of 2] b ...
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  Subject: [PATCH 2 of 2] b
+  X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+  Message-Id: <97d72e5f12c7e84f8506.61@
+  In-Reply-To: <8580ff50825a50c8f716.60@
+  References: <8580ff50825a50c8f716.60@
+  User-Agent: Mercurial-patchbomb
+  Date: Thu, 01 Jan 1970 00:01:01 +0000
+  From: quux
+  To: foo
+  Cc: bar
+  
+  # HG changeset patch
+  # User test
+  # Date 2 0
+  # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+  # Parent  8580ff50825a50c8f716709acdf8de0deddcd6ab
+  b
+  
+  diff -r 8580ff50825a -r 97d72e5f12c7 b
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/b	Thu Jan 01 00:00:02 1970 +0000
+  @@ -0,0 +1,1 @@
+  +b
+  
+
+
 
   $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
   >  -s test -r 0:1 | fixheaders
--- a/tests/test-progress.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-progress.t	Sat Oct 15 14:30:50 2011 -0500
@@ -9,16 +9,28 @@
   >         total = loops
   >     if opts.get('total', None):
   >         total = int(opts.get('total'))
+  >     nested = False
+  >     if opts.get('nested', None):
+  >         nested = True
   >     loops = abs(loops)
   > 
   >     for i in range(loops):
   >         ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
+  >         if opts.get('parallel'):
+  >             ui.progress('other', i, 'other.%d' % i, 'othernum', total)
+  >         if nested:
+  >             for j in range(2):
+  >                 ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
+  >             ui.progress('nested', None, 'nested.done', 'nestnum', 2)
   >     ui.progress('loop', None, 'loop.done', 'loopnum', total)
   > 
   > commands.norepo += " loop"
   > 
   > cmdtable = {
-  >     "loop": (loop, [('', 'total', '', 'override for total')],
+  >     "loop": (loop, [('', 'total', '', 'override for total'),
+  >                     ('', 'nested', False, 'show nested results'),
+  >                     ('', 'parallel', False, 'show parallel sets of results'),
+  >                    ],
   >              'hg loop LOOPS'),
   > }
   > EOF
@@ -47,6 +59,42 @@
   loop [===============================>                ] 2/3
                                                               \r (esc)
 
+
+test nested short-lived topics (which shouldn't display with nestdelay):
+
+  $ hg -y loop 3 --nested 2>&1 | \
+  > python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  loop [===============>                                ] 1/3
+  loop [===============================>                ] 2/3
+                                                              \r (esc)
+
+
+  $ hg --config progress.changedelay=0 -y loop 3 --nested 2>&1 | \
+  > python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+  loop [===============>                                ] 1/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+  loop [===============================>                ] 2/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+                                                              \r (esc)
+
+
+test two topics being printed in parallel (as when we're doing a local
+--pull clone, where you get the unbundle and bundle progress at the
+same time):
+  $ hg loop 3 --parallel 2>&1 | python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  loop [===============>                                ] 1/3
+  loop [===============================>                ] 2/3
+                                                              \r (esc)
 test refresh is taken in account
 
   $ hg -y --config progress.refresh=100 loop 3 2>&1 | $TESTDIR/filtercr.py
--- a/tests/test-push-http.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-push-http.t	Sat Oct 15 14:30:50 2011 -0500
@@ -64,7 +64,6 @@
   % serve errors
   $ hg rollback
   repository tip rolled back to revision 0 (undo serve)
-  working directory now based on revision 0
 
 expect success, server lacks the httpheader capability
 
@@ -81,7 +80,6 @@
   % serve errors
   $ hg rollback
   repository tip rolled back to revision 0 (undo serve)
-  working directory now based on revision 0
 
 expect success, server lacks the unbundlehash capability
 
@@ -98,7 +96,6 @@
   % serve errors
   $ hg rollback
   repository tip rolled back to revision 0 (undo serve)
-  working directory now based on revision 0
 
 expect authorization error: all users denied
 
--- a/tests/test-qrecord.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-qrecord.t	Sat Oct 15 14:30:50 2011 -0500
@@ -54,23 +54,23 @@
   
   options:
   
-   -A --addremove            mark new/missing files as added/removed before
-                             committing
-      --close-branch         mark a branch as closed, hiding it from the branch
-                             list
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -m --message TEXT         use text as commit message
-   -l --logfile FILE         read commit message from file
-   -d --date DATE            record the specified date as commit date
-   -u --user USER            record the specified user as committer
-   -w --ignore-all-space     ignore white space when comparing lines
-   -b --ignore-space-change  ignore changes in the amount of white space
-   -B --ignore-blank-lines   ignore changes whose lines are all blank
+   -A --addremove           mark new/missing files as added/removed before
+                            committing
+      --close-branch        mark a branch as closed, hiding it from the branch
+                            list
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -m --message TEXT        use text as commit message
+   -l --logfile FILE        read commit message from file
+   -d --date DATE           record the specified date as commit date
+   -u --user USER           record the specified user as committer
+   -w --ignore-all-space    ignore white space when comparing lines
+   -b --ignore-space-change ignore changes in the amount of white space
+   -B --ignore-blank-lines  ignore changes whose lines are all blank
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help record" to show global options
+  use "hg -v help record" to show more info
 
 help (no mq, so no qrecord)
 
@@ -81,7 +81,7 @@
   
       See "hg help qnew" & "hg help record" for more information and usage.
   
-  use "hg -v help qrecord" to show global options
+  use "hg -v help qrecord" to show more info
 
   $ hg init a
 
@@ -113,7 +113,7 @@
   
       See "hg help qnew" & "hg help record" for more information and usage.
   
-  use "hg -v help qrecord" to show global options
+  use "hg -v help qrecord" to show more info
 
 help (mq present)
 
@@ -129,24 +129,24 @@
   
   options:
   
-   -e --edit                 edit commit message
-   -g --git                  use git extended diff format
-   -U --currentuser          add "From: <current user>" to patch
-   -u --user USER            add "From: <USER>" to patch
-   -D --currentdate          add "Date: <current date>" to patch
-   -d --date DATE            add "Date: <DATE>" to patch
-   -I --include PATTERN [+]  include names matching the given patterns
-   -X --exclude PATTERN [+]  exclude names matching the given patterns
-   -m --message TEXT         use text as commit message
-   -l --logfile FILE         read commit message from file
-   -w --ignore-all-space     ignore white space when comparing lines
-   -b --ignore-space-change  ignore changes in the amount of white space
-   -B --ignore-blank-lines   ignore changes whose lines are all blank
-      --mq                   operate on patch repository
+   -e --edit                edit commit message
+   -g --git                 use git extended diff format
+   -U --currentuser         add "From: <current user>" to patch
+   -u --user USER           add "From: <USER>" to patch
+   -D --currentdate         add "Date: <current date>" to patch
+   -d --date DATE           add "Date: <DATE>" to patch
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -m --message TEXT        use text as commit message
+   -l --logfile FILE        read commit message from file
+   -w --ignore-all-space    ignore white space when comparing lines
+   -b --ignore-space-change ignore changes in the amount of white space
+   -B --ignore-blank-lines  ignore changes whose lines are all blank
+      --mq                  operate on patch repository
   
   [+] marked option can be specified multiple times
   
-  use "hg -v help qrecord" to show global options
+  use "hg -v help qrecord" to show more info
 
   $ cd a
 
--- a/tests/test-rebase-collapse.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-rebase-collapse.t	Sat Oct 15 14:30:50 2011 -0500
@@ -74,12 +74,12 @@
   $ cd ..
 
 
-Rebasing G onto H:
+Rebasing E onto H:
 
   $ hg clone -q -u . a a2
   $ cd a2
 
-  $ hg rebase --base 6 --collapse
+  $ hg rebase --source 4 --collapse
   saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -115,7 +115,7 @@
   abort: message can only be specified with collapse
   [255]
 
-  $ hg rebase --base 6 --collapse -m 'custom message'
+  $ hg rebase --source 4 --collapse -m 'custom message'
   saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
--- a/tests/test-rebase-detach.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-rebase-detach.t	Sat Oct 15 14:30:50 2011 -0500
@@ -281,3 +281,25 @@
   |/
   o  0: 'A'
   
+
+  $ hg rebase -d 5 -s 7
+  saved backup bundle to $TESTTMP/a5/.hg/strip-backup/13547172c9c0-backup.hg
+  $ hg tglog
+  @  8: 'D'
+  |
+  o  7: 'C'
+  |
+  | o  6: 'B'
+  |/
+  o  5: 'extra branch'
+  
+  o  4: 'H'
+  |
+  | o  3: 'G'
+  |/|
+  o |  2: 'F'
+  | |
+  | o  1: 'E'
+  |/
+  o  0: 'A'
+  
--- a/tests/test-rebase-parameters.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-rebase-parameters.t	Sat Oct 15 14:30:50 2011 -0500
@@ -51,8 +51,8 @@
   $ cd a1
 
   $ hg rebase -s 8 -d 7
-  abort: source is descendant of destination
-  [255]
+  nothing to rebase
+  [1]
 
   $ hg rebase --continue --abort
   abort: cannot use both abort and continue
@@ -67,7 +67,7 @@
   [255]
 
   $ hg rebase --base 5 --source 4
-  abort: cannot specify both a revision and a base
+  abort: cannot specify both a source and a base
   [255]
 
   $ hg rebase
@@ -76,7 +76,7 @@
 
   $ hg up -q 7
 
-  $ hg rebase
+  $ hg rebase --traceback
   nothing to rebase
   [1]
 
--- a/tests/test-rebase-scenario-global.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-rebase-scenario-global.t	Sat Oct 15 14:30:50 2011 -0500
@@ -212,8 +212,8 @@
   $ cd a7
 
   $ hg rebase -s 6 -d 5
-  abort: source is descendant of destination
-  [255]
+  nothing to rebase
+  [1]
 
 F onto G - rebase onto a descendant:
 
@@ -248,3 +248,261 @@
   nothing to rebase
   [1]
 
+C onto A - rebase onto an ancestor:
+
+  $ hg rebase -d 0 -s 2
+  saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg
+  $ hg tglog
+  @  7: 'D'
+  |
+  o  6: 'C'
+  |
+  | o  5: 'H'
+  | |
+  | | o  4: 'G'
+  | |/|
+  | o |  3: 'F'
+  |/ /
+  | o  2: 'E'
+  |/
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+  $ cd ..
+
+Test for revset
+
+We need a bit different graph
+All destination are B
+
+  $ hg init ah
+  $ cd ah
+  $ hg unbundle $TESTDIR/bundles/rebase-revset.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 9 changesets with 9 changes to 9 files (+2 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg tglog
+  o  8: 'I'
+  |
+  o  7: 'H'
+  |
+  o  6: 'G'
+  |
+  | o  5: 'F'
+  | |
+  | o  4: 'E'
+  |/
+  o  3: 'D'
+  |
+  o  2: 'C'
+  |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+  $ cd ..
+
+
+Simple case with keep:
+
+Source on have two descendant heads but ask for one
+
+  $ hg clone -q -u . ah ah1
+  $ cd ah1
+  $ hg rebase -r '2::8' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '2::8' -d 1 --keep
+  $ hg tglog
+  @  13: 'I'
+  |
+  o  12: 'H'
+  |
+  o  11: 'G'
+  |
+  o  10: 'D'
+  |
+  o  9: 'C'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  | o  6: 'G'
+  | |
+  | | o  5: 'F'
+  | | |
+  | | o  4: 'E'
+  | |/
+  | o  3: 'D'
+  | |
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+Base on have one descendant heads we ask for but common ancestor have two
+
+  $ hg clone -q -u . ah ah2
+  $ cd ah2
+  $ hg rebase -r '3::8' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::8' -d 1 --keep
+  $ hg tglog
+  @  12: 'I'
+  |
+  o  11: 'H'
+  |
+  o  10: 'G'
+  |
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+rebase subset
+
+  $ hg clone -q -u . ah ah3
+  $ cd ah3
+  $ hg rebase -r '3::7' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::7' -d 1 --keep
+  $ hg tglog
+  @  11: 'H'
+  |
+  o  10: 'G'
+  |
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+rebase subset with multiple head
+
+  $ hg clone -q -u . ah ah4
+  $ cd ah4
+  $ hg rebase -r '3::(7+5)' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::(7+5)' -d 1 --keep
+  $ hg tglog
+  @  13: 'H'
+  |
+  o  12: 'G'
+  |
+  | o  11: 'F'
+  | |
+  | o  10: 'E'
+  |/
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+More advanced tests
+
+rebase on ancestor with revset
+
+  $ hg clone -q -u . ah ah5
+  $ cd ah5
+  $ hg rebase -r '6::' -d 2
+  saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg
+  $ hg tglog
+  @  8: 'I'
+  |
+  o  7: 'H'
+  |
+  o  6: 'G'
+  |
+  | o  5: 'F'
+  | |
+  | o  4: 'E'
+  | |
+  | o  3: 'D'
+  |/
+  o  2: 'C'
+  |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+  $ cd ..
+
+
+rebase with multiple root.
+We rebase E and G on B
+We would expect heads are I, F if it was supported
+
+  $ hg clone -q -u . ah ah6
+  $ cd ah6
+  $ hg rebase -r '(4+6)::' -d 1
+  abort: can't rebase multiple roots
+  [255]
+  $ cd ..
--- a/tests/test-remove.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-remove.t	Sat Oct 15 14:30:50 2011 -0500
@@ -29,7 +29,7 @@
   $ echo b > bar
   $ hg add bar
   $ remove bar
-  not removing bar: file has been marked for add (use -f to force removal)
+  not removing bar: file has been marked for add (use forget to undo)
   exit code: 1
   A bar
   ./bar
--- a/tests/test-rollback.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-rollback.t	Sat Oct 15 14:30:50 2011 -0500
@@ -1,9 +1,9 @@
-
+setup repo
   $ hg init t
   $ cd t
   $ echo a > a
-  $ hg add a
-  $ hg commit -m "test"
+  $ hg commit -Am'add a'
+  adding a
   $ hg verify
   checking changesets
   checking manifests
@@ -11,12 +11,14 @@
   checking files
   1 files, 1 changesets, 1 total revisions
   $ hg parents
-  changeset:   0:acb14030fe0a
+  changeset:   0:1f0dee641bb7
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     test
+  summary:     add a
   
+
+rollback to null revision
   $ hg status
   $ hg rollback
   repository tip rolled back to revision -1 (undo commit)
@@ -31,22 +33,23 @@
   $ hg status
   A a
 
-Test issue 902
+Two changesets this time so we rollback to a real changeset
+  $ hg commit -m'add a again'
+  $ echo a >> a
+  $ hg commit -m'modify a'
 
-  $ hg commit -m "test2"
+Test issue 902 (current branch is preserved)
   $ hg branch test
   marked working directory as branch test
   $ hg rollback
-  repository tip rolled back to revision -1 (undo commit)
-  working directory now based on revision -1
+  repository tip rolled back to revision 0 (undo commit)
+  working directory now based on revision 0
   $ hg branch
   default
 
 Test issue 1635 (commit message saved)
-.hg/last-message.txt:
-
   $ cat .hg/last-message.txt ; echo
-  test2
+  modify a
 
 Test rollback of hg before issue 902 was fixed
 
@@ -55,12 +58,41 @@
   marked working directory as branch test
   $ rm .hg/undo.branch
   $ hg rollback
-  repository tip rolled back to revision -1 (undo commit)
-  named branch could not be reset, current branch is still: test
-  working directory now based on revision -1
+  repository tip rolled back to revision 0 (undo commit)
+  named branch could not be reset: current branch is still 'test'
+  working directory now based on revision 0
   $ hg branch
   test
 
+working dir unaffected by rollback: do not restore dirstate et. al.
+  $ hg log --template '{rev}  {branch}  {desc|firstline}\n'
+  0  default  add a again
+  $ hg status
+  M a
+  $ hg bookmark foo
+  $ hg commit -m'modify a again'
+  $ echo b > b
+  $ hg commit -Am'add b'
+  adding b
+  $ hg log --template '{rev}  {branch}  {desc|firstline}\n'
+  2  test  add b
+  1  test  modify a again
+  0  default  add a again
+  $ hg update default
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg bookmark bar
+  $ cat .hg/undo.branch ; echo
+  test
+  $ hg rollback -f
+  repository tip rolled back to revision 1 (undo commit)
+  $ hg id -n
+  0
+  $ hg branch
+  default
+  $ cat .hg/bookmarks.current ; echo
+  bar
+  $ hg bookmark --delete foo
+
 rollback by pretxncommit saves commit message (issue 1635)
 
   $ echo a >> a
@@ -69,9 +101,6 @@
   rollback completed
   abort: pretxncommit hook exited with status * (glob)
   [255]
-
-.hg/last-message.txt:
-
   $ cat .hg/last-message.txt ; echo
   precious commit message
 
@@ -102,18 +131,53 @@
   adding changesets
   adding manifests
   adding file changes
-  added 1 changesets with 1 changes to 1 files
-  updating to branch test
+  added 3 changesets with 2 changes to 1 files (+1 heads)
+  updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd u
   $ hg id default
-  1df294f7b1a2
+  068774709090
 
 now rollback and observe that 'hg serve' reloads the repository and
 presents the correct tip changeset:
 
   $ hg -R ../t rollback
-  repository tip rolled back to revision -1 (undo commit)
-  working directory now based on revision -1
+  repository tip rolled back to revision 1 (undo commit)
+  working directory now based on revision 0
   $ hg id default
-  000000000000
+  791dd2169706
+
+update to older changeset and then refuse rollback, because
+that would lose data (issue2998)
+  $ cd ../t
+  $ hg -q update
+  $ rm `hg status -un`
+  $ template='{rev}:{node|short}  [{branch}]  {desc|firstline}\n'
+  $ echo 'valuable new file' > b
+  $ echo 'valuable modification' >> a
+  $ hg commit -A -m'a valuable change'
+  adding b
+  $ hg update 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg rollback
+  abort: rollback of last commit while not checked out may lose data
+  (use -f to force)
+  [255]
+  $ hg tip -q
+  2:4d9cd3795eea
+  $ hg rollback -f
+  repository tip rolled back to revision 1 (undo commit)
+  $ hg status
+  $ hg log --removed b   # yep, it's gone
+
+same again, but emulate an old client that doesn't write undo.desc
+  $ hg -q update
+  $ echo 'valuable modification redux' >> a
+  $ hg commit -m'a valuable change redux'
+  $ rm .hg/undo.desc
+  $ hg update 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg rollback
+  rolling back unknown transaction
+  $ cat a
+  a
--- a/tests/test-run-tests.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-run-tests.t	Sat Oct 15 14:30:50 2011 -0500
@@ -16,6 +16,20 @@
   $ foo
   bar
 
+Doctest commands:
+
+  >>> print 'foo'
+  foo
+  $ echo interleaved
+  interleaved
+  >>> for c in 'xyz':
+  ...     print c
+  x
+  y
+  z
+  >>> print
+  <BLANKLINE>
+
 Regular expressions:
 
   $ echo foobarbaz
--- a/tests/test-setdiscovery.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-setdiscovery.t	Sat Oct 15 14:30:50 2011 -0500
@@ -288,7 +288,7 @@
   reading DAG from stdin
 
   $ hg heads -t --template . | wc -c
-   *261 (re)
+  \s*261 (re)
 
   $ hg clone -b a . a
   adding changesets
--- a/tests/test-share.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-share.t	Sat Oct 15 14:30:50 2011 -0500
@@ -28,6 +28,15 @@
   $ cat .hg/sharedpath; echo
   $TESTTMP/repo1/.hg
 
+trailing newline on .hg/sharedpath is ok
+  $ hg tip -q
+  0:d3873e73d99e
+  $ echo '' >> .hg/sharedpath
+  $ cat .hg/sharedpath
+  $TESTTMP/repo1/.hg
+  $ hg tip -q
+  0:d3873e73d99e
+
 commit in shared clone
 
   $ echo a >> a
@@ -97,3 +106,21 @@
   -rw-r--r-- 2 b
   
   
+
+test unshare command
+
+  $ hg unshare
+  $ test -d .hg/store
+  $ test -f .hg/sharedpath
+  [1]
+  $ hg unshare
+  abort: this is not a shared repo
+  [255]
+
+check that a change does not propagate
+
+  $ echo b >> b
+  $ hg commit -m'change in unshared'
+  $ cd ../repo1
+  $ hg id -r tip
+  c2e0ac586386 tip
--- a/tests/test-subrepo-paths.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-subrepo-paths.t	Sat Oct 15 14:30:50 2011 -0500
@@ -1,19 +1,22 @@
   $ hg init outer
   $ cd outer
 
+  $ echo '[paths]' >> .hg/hgrc
+  $ echo 'default = http://example.net/' >> .hg/hgrc
+
 hg debugsub with no remapping
 
-  $ echo 'sub = http://example.net/libfoo' > .hgsub
+  $ echo 'sub = libfoo' > .hgsub
   $ hg add .hgsub
 
   $ hg debugsub
   path sub
-   source   http://example.net/libfoo
+   source   libfoo
    revision 
 
 hg debugsub with remapping
 
-  $ echo '[subpaths]' > .hg/hgrc
+  $ echo '[subpaths]' >> .hg/hgrc
   $ printf 'http://example.net/lib(.*) = C:\\libs\\\\1-lib\\\n' >> .hg/hgrc
 
   $ hg debugsub
@@ -30,6 +33,21 @@
    source   C:\libs\bar-lib\
    revision 
 
+test absolute source path -- testing with a URL is important since
+standard os.path.join wont treat that as an absolute path
+
+  $ echo 'abs = http://example.net/abs' > .hgsub
+  $ hg debugsub
+  path abs
+   source   http://example.net/abs
+   revision 
+
+  $ echo 'abs = /abs' > .hgsub
+  $ hg debugsub
+  path abs
+   source   /abs
+   revision 
+
 test bad subpaths pattern
 
   $ cat > .hg/hgrc <<EOF
--- a/tests/test-subrepo.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-subrepo.t	Sat Oct 15 14:30:50 2011 -0500
@@ -34,6 +34,18 @@
   $ hg ci -m1
   committing subrepository s
 
+Revert can't (yet) revert subrepos:
+
+  $ echo b > s/a
+  $ hg revert s
+  s: reverting subrepos is unsupported
+
+Revert currently ignores subrepos by default
+
+  $ hg revert -a
+  $ hg revert -R s -a -C
+  reverting s/a
+
 Issue2022: update -C
 
   $ echo b > s/a
@@ -52,6 +64,14 @@
   commit: (clean)
   update: (current)
 
+commands that require a clean repo should respect subrepos
+
+  $ echo b >> s/a
+  $ hg backout tip
+  abort: uncommitted changes in subrepo s
+  [255]
+  $ hg revert -C -R s s/a
+
 add sub sub
 
   $ echo ss = ss > s/.hgsub
--- a/tests/test-symlink-os-yes-fs-no.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-symlink-os-yes-fs-no.py	Sat Oct 15 14:30:50 2011 -0500
@@ -5,7 +5,7 @@
 BUNDLEPATH = os.path.join(TESTDIR, 'bundles', 'test-no-symlinks.hg')
 
 # only makes sense to test on os which supports symlinks
-if not hasattr(os, "symlink"):
+if not getattr(os, "symlink", False):
     sys.exit(80) # SKIPPED_STATUS defined in run-tests.py
 
 # clone with symlink support
--- a/tests/test-tags.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-tags.t	Sat Oct 15 14:30:50 2011 -0500
@@ -135,8 +135,6 @@
   $ echo >> .hgtags
   $ echo "foo bar" >> .hgtags
   $ echo "a5a5 invalid" >> .hg/localtags
-  $ echo "committing .hgtags:"
-  committing .hgtags:
   $ cat .hgtags 
   acb14030fe0a21b60322c440ad2d20cf7685a376 first
   spam
--- a/tests/test-transplant.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-transplant.t	Sat Oct 15 14:30:50 2011 -0500
@@ -81,6 +81,19 @@
   1 
   0 
 
+rollback the transplant
+  $ hg rollback
+  repository tip rolled back to revision 4 (undo transplant)
+  working directory now based on revision 1
+  $ hg tip -q
+  4:a53251cdf717
+  $ hg parents -q
+  1:d11e3596cc1a
+  $ hg status
+  ? b1
+  ? b2
+  ? b3
+
   $ hg clone ../t ../prune
   updating to branch default
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-url-rev.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-url-rev.t	Sat Oct 15 14:30:50 2011 -0500
@@ -102,7 +102,6 @@
   $ cd clone
   $ hg rollback
   repository tip rolled back to revision 1 (undo push)
-  working directory now based on revision 1
 
   $ hg -q incoming
   2:faba9097cad4
@@ -147,10 +146,6 @@
 
   $ hg rollback
   repository tip rolled back to revision 1 (undo pull)
-  working directory now based on revision 1
-
-  $ hg up -C 0
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg parents -q
   0:1f0dee641bb7
@@ -205,4 +200,8 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     new head of branch foo
   
+Test handling of invalid urls
 
+  $ hg id http://foo/?bar
+  abort: unsupported URL component: "bar"
+  [255]
--- a/tests/test-walkrepo.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-walkrepo.py	Sat Oct 15 14:30:50 2011 -0500
@@ -5,7 +5,7 @@
 from os.path import join as pjoin
 
 u = ui.ui()
-sym = hasattr(os, 'symlink') and hasattr(os.path, 'samestat')
+sym = getattr(os, 'symlink', False) and getattr(os.path, 'samestat', False)
 
 hg.repository(u, 'top1', create=1)
 mkdir('subdir')
--- a/tests/test-win32text.t	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/test-win32text.t	Sat Oct 15 14:30:50 2011 -0500
@@ -16,16 +16,14 @@
   [hooks]
   pretxncommit.crlf = python:hgext.win32text.forbidcrlf
   pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
-  $ echo
-  
+
   $ echo hello > f
   $ hg add f
 
 commit should succeed
 
   $ hg ci -m 1
-  $ echo
-  
+
   $ hg clone . ../zoz
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -41,8 +39,7 @@
   rollback completed
   abort: pretxncommit.crlf hook failed
   [255]
-  $ echo
-  
+
   $ mv .hg/hgrc .hg/hgrc.bak
 
 commits should succeed
@@ -50,8 +47,6 @@
   $ hg ci -m 2
   $ hg cp f g
   $ hg ci -m 2.2
-  $ echo
-  
 
 push should fail
 
@@ -84,8 +79,7 @@
   rollback completed
   abort: pretxnchangegroup.crlf hook failed
   [255]
-  $ echo
-  
+
   $ mv .hg/hgrc.bak .hg/hgrc
   $ echo hello > f
   $ hg rm g
@@ -93,8 +87,6 @@
 commit should succeed
 
   $ hg ci -m 2.3
-  $ echo
-  
 
 push should succeed
 
@@ -105,8 +97,6 @@
   adding manifests
   adding file changes
   added 3 changesets with 3 changes to 2 files
-  $ echo
-  
 
 and now for something completely different
 
@@ -124,12 +114,10 @@
   $ hg revert -a
   forgetting d/f2
   $ rm d/f2
-  $ echo
-  
+
   $ hg rem f
   $ hg ci -m 4
-  $ echo
-  
+
   $ python -c 'file("bin", "wb").write("hello\x00\x0D\x0A")'
   $ hg add bin
   $ hg ci -m 5
@@ -183,13 +171,10 @@
   1
   
   
-  $ echo
-  
   $ hg clone . dupe
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo
-  
+
   $ for x in a b c d; do echo content > dupe/$x; done
   $ hg -R dupe add
   adding dupe/a
@@ -274,8 +259,6 @@
   1
   
   
-  $ echo
-  
   $ hg pull dupe
   pulling from dupe
   searching for changes
@@ -306,8 +289,7 @@
   rollback completed
   abort: pretxnchangegroup.crlf hook failed
   [255]
-  $ echo
-  
+
   $ hg log -v
   changeset:   5:f0b1c8d75fce
   tag:         tip
@@ -358,8 +340,6 @@
   1
   
   
-  $ echo
-  
   $ rm .hg/hgrc
   $ (echo some; echo text) > f3
   $ python -c 'file("f4.bat", "wb").write("rem empty\x0D\x0A")'
@@ -372,8 +352,7 @@
   text
   $ cat f4.bat
   rem empty\r (esc)
-  $ echo
-  
+
   $ echo '[extensions]' >> .hg/hgrc
   $ echo 'win32text = ' >> .hg/hgrc
   $ echo '[decode]' >> .hg/hgrc
@@ -415,8 +394,7 @@
   text\r (esc)
   $ cat f4.bat
   rem empty\r (esc)
-  $ echo
-  
+
   $ python -c 'file("f5.sh", "wb").write("# empty\x0D\x0A")'
   $ hg add f5.sh
   $ hg ci -m 7
--- a/tests/tinyproxy.py	Sun Oct 02 16:41:07 2011 -0500
+++ b/tests/tinyproxy.py	Sat Oct 15 14:30:50 2011 -0500
@@ -23,7 +23,8 @@
 
     def handle(self):
         (ip, port) =  self.client_address
-        if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
+        allowed = getattr(self, 'allowed_clients', None)
+        if allowed is not None and ip not in allowed:
             self.raw_requestline = self.rfile.readline()
             if self.parse_request():
                 self.send_error(403)