changeset 12952:f6b88f3bcc03

merge with stable
author Nicolas Dumazet <nicdumz.commits@gmail.com>
date Tue, 09 Nov 2010 01:33:48 +0900
parents de6a28ff5ffc (diff) 101366ad816c (current diff)
children 85777aab7e08
files
diffstat 18 files changed, 424 insertions(+), 306 deletions(-) [+]
line wrap: on
line diff
--- a/doc/Makefile	Mon Nov 08 22:45:56 2010 +0900
+++ b/doc/Makefile	Tue Nov 09 01:33:48 2010 +0900
@@ -1,7 +1,8 @@
 SOURCES=$(wildcard *.[0-9].txt)
 MAN=$(SOURCES:%.txt=%)
 HTML=$(SOURCES:%.txt=%.html)
-GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py ../mercurial/help/*.txt
+GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py \
+	../mercurial/help/*.txt ../hgext/*.py ../hgext/*/__init__.py
 PREFIX=/usr/local
 MANDIR=$(PREFIX)/share/man
 INSTALL=install -c -m 644
--- a/hgext/convert/__init__.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/hgext/convert/__init__.py	Tue Nov 09 01:33:48 2010 +0900
@@ -59,10 +59,10 @@
     --sourcesort  try to preserve source revisions order, only
                   supported by Mercurial sources.
 
-    If <REVMAP> isn't given, it will be put in a default location
-    (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
-    that maps each source commit ID to the destination ID for that
-    revision, like so::
+    If ``REVMAP`` isn't given, it will be put in a default location
+    (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
+    text file that maps each source commit ID to the destination ID
+    for that revision, like so::
 
       <source ID> <destination ID>
 
@@ -138,15 +138,19 @@
     Mercurial Source
     ''''''''''''''''
 
-    --config convert.hg.ignoreerrors=False    (boolean)
-        ignore integrity errors when reading. Use it to fix Mercurial
-        repositories with missing revlogs, by converting from and to
-        Mercurial.
-    --config convert.hg.saverev=False         (boolean)
-        store original revision ID in changeset (forces target IDs to
-        change)
-    --config convert.hg.startrev=0            (hg revision identifier)
-        convert start revision and its descendants
+    The Mercurial source recognizes the following configuration
+    options, which you can set on the command line with ``--config``:
+
+    :convert.hg.ignoreerrors: ignore integrity errors when reading.
+        Use it to fix Mercurial repositories with missing revlogs, by
+        converting from and to Mercurial. Default is False.
+
+    :convert.hg.saverev: store original. revision ID in changeset
+        (forces target IDs to change). It takes and boolean argument
+        and defaults to False.
+
+    :convert.hg.startrev: convert start revision and its descendants.
+        It takes a hg revision identifier and defaults to 0.
 
     CVS Source
     ''''''''''
@@ -154,42 +158,46 @@
     CVS source will use a sandbox (i.e. a checked-out copy) from CVS
     to indicate the starting point of what will be converted. Direct
     access to the repository files is not needed, unless of course the
-    repository is :local:. The conversion uses the top level directory
-    in the sandbox to find the CVS repository, and then uses CVS rlog
-    commands to find files to convert. This means that unless a
-    filemap is given, all files under the starting directory will be
+    repository is ``:local:``. The conversion uses the top level
+    directory in the sandbox to find the CVS repository, and then uses
+    CVS rlog commands to find files to convert. This means that unless
+    a filemap is given, all files under the starting directory will be
     converted, and that any directory reorganization in the CVS
     sandbox is ignored.
 
-    The options shown are the defaults.
+    The following options can be used with ``--config``:
+
+    :convert.cvsps.cache: Set to False to disable remote log caching,
+        for testing and debugging purposes. Default is True.
+
+    :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
+        allowed between commits with identical user and log message in
+        a single changeset. When very large files were checked in as
+        part of a changeset then the default may not be long enough.
+        The default is 60.
 
-    --config convert.cvsps.cache=True         (boolean)
-        Set to False to disable remote log caching, for testing and
-        debugging purposes.
-    --config convert.cvsps.fuzz=60            (integer)
-        Specify the maximum time (in seconds) that is allowed between
-        commits with identical user and log message in a single
-        changeset. When very large files were checked in as part of a
-        changeset then the default may not be long enough.
-    --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
-        Specify a regular expression to which commit log messages are
-        matched. If a match occurs, then the conversion process will
-        insert a dummy revision merging the branch on which this log
-        message occurs to the branch indicated in the regex.
-    --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
-        Specify a regular expression to which commit log messages are
-        matched. If a match occurs, then the conversion process will
-        add the most recent revision on the branch indicated in the
-        regex as the second parent of the changeset.
-    --config hook.cvslog
-        Specify a Python function to be called at the end of gathering
-        the CVS log. The function is passed a list with the log entries,
-        and can modify the entries in-place, or add or delete them.
-    --config hook.cvschangesets
-        Specify a Python function to be called after the changesets
-        are calculated from the the CVS log. The function is passed
-        a list with the changeset entries, and can modify the changesets
-        in-place, or add or delete them.
+    :convert.cvsps.mergeto: Specify a regular expression to which
+        commit log messages are matched. If a match occurs, then the
+        conversion process will insert a dummy revision merging the
+        branch on which this log message occurs to the branch
+        indicated in the regex. Default is ``{{mergetobranch
+        ([-\\w]+)}}``
+
+    :convert.cvsps.mergefrom: Specify a regular expression to which
+        commit log messages are matched. If a match occurs, then the
+        conversion process will add the most recent revision on the
+        branch indicated in the regex as the second parent of the
+        changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
+
+    :hook.cvslog: Specify a Python function to be called at the end of
+        gathering the CVS log. The function is passed a list with the
+        log entries, and can modify the entries in-place, or add or
+        delete them.
+
+    :hook.cvschangesets: Specify a Python function to be called after
+        the changesets are calculated from the the CVS log. The
+        function is passed a list with the changeset entries, and can
+        modify the changesets in-place, or add or delete them.
 
     An additional "debugcvsps" Mercurial command allows the builtin
     changeset merging code to be run without doing a conversion. Its
@@ -200,29 +208,33 @@
     '''''''''''''''''
 
     Subversion source detects classical trunk/branches/tags layouts.
-    By default, the supplied "svn://repo/path/" source URL is
-    converted as a single branch. If "svn://repo/path/trunk" exists it
-    replaces the default branch. If "svn://repo/path/branches" exists,
-    its subdirectories are listed as possible branches. If
-    "svn://repo/path/tags" exists, it is looked for tags referencing
-    converted branches. Default "trunk", "branches" and "tags" values
-    can be overridden with following options. Set them to paths
+    By default, the supplied ``svn://repo/path/`` source URL is
+    converted as a single branch. If ``svn://repo/path/trunk`` exists
+    it replaces the default branch. If ``svn://repo/path/branches``
+    exists, its subdirectories are listed as possible branches. If
+    ``svn://repo/path/tags`` exists, it is looked for tags referencing
+    converted branches. Default ``trunk``, ``branches`` and ``tags``
+    values can be overridden with following options. Set them to paths
     relative to the source URL, or leave them blank to disable auto
     detection.
 
-    --config convert.svn.branches=branches    (directory name)
-        specify the directory containing branches
-    --config convert.svn.tags=tags            (directory name)
-        specify the directory containing tags
-    --config convert.svn.trunk=trunk          (directory name)
-        specify the name of the trunk branch
+    The following options can be set with ``--config``:
+
+    :convert.svn.branches: specify the directory containing branches.
+        The defaults is ``branches``.
+
+    :convert.svn.tags: specify the directory containing tags. The
+        default is ``tags``.
+
+    :convert.svn.trunk: specify the name of the trunk branch The
+        defauls is ``trunk``.
 
     Source history can be retrieved starting at a specific revision,
     instead of being integrally converted. Only single branch
     conversions are supported.
 
-    --config convert.svn.startrev=0           (svn revision number)
-        specify start Subversion revision.
+    :convert.svn.startrev: specify start Subversion revision number.
+        The default is 0.
 
     Perforce Source
     '''''''''''''''
@@ -232,24 +244,27 @@
     source to a flat Mercurial repository, ignoring labels, branches
     and integrations. Note that when a depot path is given you then
     usually should specify a target directory, because otherwise the
-    target may be named ...-hg.
+    target may be named ``...-hg``.
 
     It is possible to limit the amount of source history to be
-    converted by specifying an initial Perforce revision.
+    converted by specifying an initial Perforce revision:
 
-    --config convert.p4.startrev=0            (perforce changelist number)
-        specify initial Perforce revision.
+    :convert.p4.startrev: specify initial Perforce revision, a
+        Perforce changelist number).
 
     Mercurial Destination
     '''''''''''''''''''''
 
-    --config convert.hg.clonebranches=False   (boolean)
-        dispatch source branches in separate clones.
-    --config convert.hg.tagsbranch=default    (branch name)
-        tag revisions branch name
-    --config convert.hg.usebranchnames=True   (boolean)
-        preserve branch names
+    The following options are supported:
+
+    :convert.hg.clonebranches: dispatch source branches in separate
+        clones. The default is False.
 
+    :convert.hg.tagsbranch: branch name for tag revisions, defaults to
+        ``default``.
+
+    :convert.hg.usebranchnames: preserve branch names. The default is
+        True
     """
     return convcmd.convert(ui, src, dest, revmapfile, **opts)
 
--- a/hgext/keyword.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/hgext/keyword.py	Tue Nov 09 01:33:48 2010 +0900
@@ -170,14 +170,25 @@
                                   for k, v in kwmaps)
         else:
             self.templates = _defaultkwmaps(self.ui)
-        escaped = '|'.join(map(re.escape, self.templates.keys()))
-        self.re_kw = re.compile(r'\$(%s)\$' % escaped)
-        self.re_kwexp = re.compile(r'\$(%s): [^$\n\r]*? \$' % escaped)
-
         templatefilters.filters.update({'utcdate': utcdate,
                                         'svnisodate': svnisodate,
                                         'svnutcdate': svnutcdate})
 
+    @util.propertycache
+    def escape(self):
+        '''Returns bar-separated and escaped keywords.'''
+        return '|'.join(map(re.escape, self.templates.keys()))
+
+    @util.propertycache
+    def rekw(self):
+        '''Returns regex for unexpanded keywords.'''
+        return re.compile(r'\$(%s)\$' % self.escape)
+
+    @util.propertycache
+    def rekwexp(self):
+        '''Returns regex for expanded keywords.'''
+        return re.compile(r'\$(%s): [^$\n\r]*? \$' % self.escape)
+
     def substitute(self, data, path, ctx, subfunc):
         '''Replaces keywords in data with expanded template.'''
         def kwsub(mobj):
@@ -191,11 +202,15 @@
             return '$%s: %s $' % (kw, ekw)
         return subfunc(kwsub, data)
 
+    def linkctx(self, path, fileid):
+        '''Similar to filelog.linkrev, but returns a changectx.'''
+        return self.repo.filectx(path, fileid=fileid).changectx()
+
     def expand(self, path, node, data):
         '''Returns data with keywords expanded.'''
         if not self.restrict and self.match(path) and not util.binary(data):
-            ctx = self.repo.filectx(path, fileid=node).changectx()
-            return self.substitute(data, path, ctx, self.re_kw.sub)
+            ctx = self.linkctx(path, node)
+            return self.substitute(data, path, ctx, self.rekw.sub)
         return data
 
     def iskwfile(self, cand, ctx):
@@ -212,8 +227,8 @@
         kwcmd = self.restrict and lookup # kwexpand/kwshrink
         if self.restrict or expand and lookup:
             mf = ctx.manifest()
-        fctx = ctx
-        subn = (self.restrict or rekw) and self.re_kw.subn or self.re_kwexp.subn
+        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'))
         for f in candidates:
@@ -225,12 +240,12 @@
                 continue
             if expand:
                 if lookup:
-                    fctx = self.repo.filectx(f, fileid=mf[f]).changectx()
-                data, found = self.substitute(data, f, fctx, subn)
+                    lctx = self.linkctx(f, mf[f])
+                data, found = self.substitute(data, f, lctx, re_kw.subn)
             elif self.restrict:
-                found = self.re_kw.search(data)
+                found = re_kw.search(data)
             else:
-                data, found = _shrinktext(data, subn)
+                data, found = _shrinktext(data, re_kw.subn)
             if found:
                 self.ui.note(msg % f)
                 self.repo.wwrite(f, data, ctx.flags(f))
@@ -242,7 +257,7 @@
     def shrink(self, fname, text):
         '''Returns text with all keyword substitutions removed.'''
         if self.match(fname) and not util.binary(text):
-            return _shrinktext(text, self.re_kwexp.sub)
+            return _shrinktext(text, self.rekwexp.sub)
         return text
 
     def shrinklines(self, fname, lines):
@@ -250,7 +265,7 @@
         if self.match(fname):
             text = ''.join(lines)
             if not util.binary(text):
-                return _shrinktext(text, self.re_kwexp.sub).splitlines(True)
+                return _shrinktext(text, self.rekwexp.sub).splitlines(True)
         return lines
 
     def wread(self, fname, data):
--- a/hgext/mq.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/hgext/mq.py	Tue Nov 09 01:33:48 2010 +0900
@@ -1289,6 +1289,9 @@
             else:
                 match = cmdutil.matchall(repo)
             m, a, r, d = repo.status(match=match)[:4]
+            mm = set(mm)
+            aa = set(aa)
+            dd = set(dd)
 
             # we might end up with files that were added between
             # qtip and the dirstate parent, but then changed in the
@@ -1296,31 +1299,31 @@
             # show up in the added section
             for x in m:
                 if x not in aa:
-                    mm.append(x)
+                    mm.add(x)
             # we might end up with files added by the local dirstate that
             # were deleted by the patch.  In this case, they should only
             # show up in the changed section.
             for x in a:
                 if x in dd:
-                    del dd[dd.index(x)]
-                    mm.append(x)
+                    dd.remove(x)
+                    mm.add(x)
                 else:
-                    aa.append(x)
+                    aa.add(x)
             # make sure any files deleted in the local dirstate
             # are not in the add or change column of the patch
             forget = []
             for x in d + r:
                 if x in aa:
-                    del aa[aa.index(x)]
+                    aa.remove(x)
                     forget.append(x)
                     continue
-                elif x in mm:
-                    del mm[mm.index(x)]
-                dd.append(x)
-
-            m = list(set(mm))
-            r = list(set(dd))
-            a = list(set(aa))
+                else:
+                    mm.discard(x)
+                dd.add(x)
+
+            m = list(mm)
+            r = list(dd)
+            a = list(aa)
             c = [filter(matchfn, l) for l in (m, a, r)]
             match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
             chunks = patch.diff(repo, patchparent, match=match,
--- a/mercurial/cmdutil.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/cmdutil.py	Tue Nov 09 01:33:48 2010 +0900
@@ -147,6 +147,11 @@
         # attempt to parse old-style ranges first to deal with
         # things like old-tag which contain query metacharacters
         try:
+            if isinstance(spec, int):
+                seen.add(spec)
+                l.append(spec)
+                continue
+
             if revrangesep in spec:
                 start, end = spec.split(revrangesep, 1)
                 start = revfix(repo, start, 0)
--- a/mercurial/commands.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/commands.py	Tue Nov 09 01:33:48 2010 +0900
@@ -126,7 +126,7 @@
         lastfunc = funcmap[-1]
         funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
 
-    ctx = repo[opts.get('rev')]
+    ctx = cmdutil.revsingle(repo, opts.get('rev'))
     m = cmdutil.match(repo, pats, opts)
     follow = not opts.get('no_follow')
     for abs in ctx.walk(m):
@@ -178,7 +178,7 @@
     Returns 0 on success.
     '''
 
-    ctx = repo[opts.get('rev')]
+    ctx = cmdutil.revsingle(repo, opts.get('rev'))
     if not ctx:
         raise util.Abort(_('no working directory: please specify a revision'))
     node = ctx.node()
@@ -243,7 +243,7 @@
         opts['date'] = util.parsedate(date)
 
     cmdutil.bail_if_changed(repo)
-    node = repo.lookup(rev)
+    node = cmdutil.revsingle(repo, rev).node()
 
     op1, op2 = repo.dirstate.parents()
     a = repo.changelog.ancestor(op1, node)
@@ -408,7 +408,8 @@
                     raise util.Abort(_("%s killed") % command)
                 else:
                     transition = "bad"
-                ctx = repo[rev or '.']
+                ctx = cmdutil.revsingle(repo, rev)
+                rev = None # clear for future iterations
                 state[transition].append(ctx.node())
                 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
                 check_state(state, interactive=False)
@@ -572,11 +573,14 @@
 
     Returns 0 on success, 1 if no changes found.
     """
-    revs = opts.get('rev') or None
+    revs = None
+    if 'rev' in opts:
+        revs = cmdutil.revrange(repo, opts['rev'])
+
     if opts.get('all'):
         base = ['null']
     else:
-        base = opts.get('base')
+        base = cmdutil.revrange(repo, opts.get('base'))
     if base:
         if dest:
             raise util.Abort(_("--base is incompatible with specifying "
@@ -1026,7 +1030,7 @@
 
 def debugrebuildstate(ui, repo, rev="tip"):
     """rebuild the dirstate as it would look like for the given revision"""
-    ctx = repo[rev]
+    ctx = cmdutil.revsingle(repo, rev)
     wlock = repo.wlock()
     try:
         repo.dirstate.rebuild(ctx.node(), ctx.manifest())
@@ -1140,12 +1144,12 @@
     Returns 0 on success.
     """
 
-    if not rev2:
-        rev2 = hex(nullid)
+    r1 = cmdutil.revsingle(repo, rev1).node()
+    r2 = cmdutil.revsingle(repo, rev2, 'null').node()
 
     wlock = repo.wlock()
     try:
-        repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
+        repo.dirstate.setparents(r1, r2)
     finally:
         wlock.release()
 
@@ -1174,9 +1178,8 @@
         ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
 
 def debugsub(ui, repo, rev=None):
-    if rev == '':
-        rev = None
-    for k, v in sorted(repo[rev].substate.items()):
+    ctx = cmdutil.revsingle(repo, rev, None)
+    for k, v in sorted(ctx.substate.items()):
         ui.write('path %s\n' % k)
         ui.write(' source   %s\n' % v[0])
         ui.write(' revision %s\n' % v[1])
@@ -1435,7 +1438,7 @@
 def debugrename(ui, repo, file1, *pats, **opts):
     """dump rename information"""
 
-    ctx = repo[opts.get('rev')]
+    ctx = cmdutil.revsingle(repo, opts.get('rev'))
     m = cmdutil.match(repo, (file1,) + pats, opts)
     for abs in ctx.walk(m):
         fctx = ctx[abs]
@@ -1808,10 +1811,9 @@
     Returns 0 if matching heads are found, 1 if not.
     """
 
-    if opts.get('rev'):
-        start = repo.lookup(opts['rev'])
-    else:
-        start = None
+    start = None
+    if 'rev' in opts:
+        start = cmdutil.revsingle(repo, opts['rev'], None).node()
 
     if opts.get('topo'):
         heads = [repo[h] for h in repo.heads(start)]
@@ -2200,7 +2202,7 @@
             output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
                                     (changed) and "+" or ""))
     else:
-        ctx = repo[rev]
+        ctx = cmdutil.revsingle(repo, rev)
         if default or id:
             output = [hexfunc(ctx.node())]
         if num:
@@ -2279,6 +2281,7 @@
     d = opts["base"]
     strip = opts["strip"]
     wlock = lock = None
+    msgs = []
 
     def tryone(ui, hunk):
         tmpname, message, user, date, branch, nodeid, p1, p2 = \
@@ -2329,7 +2332,10 @@
             finally:
                 files = cmdutil.updatedir(ui, repo, files,
                                           similarity=sim / 100.0)
-            if not opts.get('no_commit'):
+            if opts.get('no_commit'):
+                if message:
+                    msgs.append(message)
+            else:
                 if opts.get('exact'):
                     m = None
                 else:
@@ -2378,6 +2384,8 @@
             if not haspatch:
                 raise util.Abort(_('no diffs found'))
 
+        if msgs:
+            repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
     finally:
         release(lock, wlock)
 
@@ -2437,7 +2445,7 @@
     Returns 0 if a match is found, 1 otherwise.
     """
     end = opts.get('print0') and '\0' or '\n'
-    rev = opts.get('rev') or None
+    rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
 
     ret = 1
     m = cmdutil.match(repo, pats, opts, default='relglob')
@@ -2572,7 +2580,7 @@
         node = rev
 
     decor = {'l':'644 @ ', 'x':'755 * ', '':'644   '}
-    ctx = repo[node]
+    ctx = cmdutil.revsingle(repo, node)
     for f in ctx:
         if ui.debugflag:
             ui.write("%40s " % hex(ctx.manifest()[f]))
@@ -2641,6 +2649,8 @@
             raise util.Abort(_('working dir not at a head rev - '
                                'use "hg update" or merge with an explicit rev'))
         node = parent == bheads[0] and bheads[-1] or bheads[0]
+    else:
+        node = cmdutil.revsingle(repo, node).node()
 
     if opts.get('preview'):
         # find nodes that are ancestors of p2 but not of p1
@@ -2686,11 +2696,8 @@
 
     Returns 0 on success.
     """
-    rev = opts.get('rev')
-    if rev:
-        ctx = repo[rev]
-    else:
-        ctx = repo[None]
+
+    ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
 
     if file_:
         m = cmdutil.match(repo, (file_,), opts)
@@ -3106,7 +3113,7 @@
     if not opts.get('rev') and p2 != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
-    ctx = repo[opts.get('rev')]
+    ctx = cmdutil.revsingle(repo, opts.get('rev'))
     node = ctx.node()
     mf = ctx.manifest()
     if node == parent:
@@ -3715,7 +3722,7 @@
     if not rev_ and repo.dirstate.parents()[1] != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
-    r = repo[rev_].node()
+    r = cmdutil.revsingle(repo, rev_).node()
 
     if not message:
         # we don't translate commit messages
--- a/mercurial/context.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/context.py	Tue Nov 09 01:33:48 2010 +0900
@@ -603,6 +603,9 @@
     def __str__(self):
         return str(self._parents[0]) + "+"
 
+    def __repr__(self):
+        return "<workingctx %s>" % str(self)
+
     def __nonzero__(self):
         return True
 
@@ -897,6 +900,9 @@
     def __str__(self):
         return "%s@%s" % (self.path(), self._changectx)
 
+    def __repr__(self):
+        return "<workingfilectx %s>" % str(self)
+
     def data(self):
         return self._repo.wread(self._path)
     def renamed(self):
--- a/mercurial/localrepo.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/localrepo.py	Tue Nov 09 01:33:48 2010 +0900
@@ -1202,8 +1202,7 @@
     def heads(self, start=None):
         heads = self.changelog.heads(start)
         # sort the output in rev descending order
-        heads = [(-self.changelog.rev(h), h) for h in heads]
-        return [n for (r, n) in sorted(heads)]
+        return  sorted(heads, key=self.changelog.rev, reverse=True)
 
     def branchheads(self, branch=None, start=None, closed=False):
         '''return a (possibly filtered) list of heads for the given branch
--- a/mercurial/patch.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/patch.py	Tue Nov 09 01:33:48 2010 +0900
@@ -974,7 +974,7 @@
     fp.seek(pos)
     return gitpatches
 
-def iterhunks(ui, fp, sourcefile=None):
+def iterhunks(ui, fp):
     """Read a patch and yield the following events:
     - ("file", afile, bfile, firsthunk): select a new target file.
     - ("hunk", hunk): a new hunk is ready to be applied, follows a
@@ -995,10 +995,6 @@
     BFILE = 1
     context = None
     lr = linereader(fp)
-    # gitworkdone is True if a git operation (copy, rename, ...) was
-    # performed already for the current file. Useful when the file
-    # section may have no hunk.
-    gitworkdone = False
 
     while True:
         newfile = newgitfile = False
@@ -1010,7 +1006,7 @@
                 current_hunk.fix_newline()
             yield 'hunk', current_hunk
             current_hunk = None
-        if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
+        if (state == BFILE and ((not context and x[0] == '@') or
             ((context is not False) and x.startswith('***************')))):
             if context is None and x.startswith('***************'):
                 context = True
@@ -1032,7 +1028,6 @@
         elif x.startswith('diff --git'):
             # check for git diff, scanning the whole patch file if needed
             m = gitre.match(x)
-            gitworkdone = False
             if m:
                 afile, bfile = m.group(1, 2)
                 if not git:
@@ -1047,7 +1042,6 @@
                 if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
                            or gp.mode):
                     afile = bfile
-                    gitworkdone = True
                 newgitfile = True
         elif x.startswith('---'):
             # check for a unified diff
@@ -1075,9 +1069,6 @@
             afile = parsefilename(x)
             bfile = parsefilename(l2)
 
-        if newfile:
-            gitworkdone = False
-
         if newgitfile or newfile:
             emitfile = True
             state = BFILE
@@ -1089,7 +1080,7 @@
             raise PatchError(_("malformed patch %s %s") % (afile,
                              current_hunk.desc))
 
-def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
+def applydiff(ui, fp, changed, strip=1, eolmode='strict'):
     """Reads a patch from fp and tries to apply it.
 
     The dict 'changed' is filled in with all of the filenames changed
@@ -1103,13 +1094,10 @@
     Callers probably want to call 'cmdutil.updatedir' after this to
     apply certain categories of changes not done by this function.
     """
-    return _applydiff(
-        ui, fp, patchfile, copyfile,
-        changed, strip=strip, sourcefile=sourcefile, eolmode=eolmode)
+    return _applydiff(ui, fp, patchfile, copyfile, changed, strip=strip,
+                      eolmode=eolmode)
 
-
-def _applydiff(ui, fp, patcher, copyfn, changed, strip=1,
-               sourcefile=None, eolmode='strict'):
+def _applydiff(ui, fp, patcher, copyfn, changed, strip=1, eolmode='strict'):
     rejects = 0
     err = 0
     current_file = None
@@ -1124,7 +1112,7 @@
         current_file.write_rej()
         return len(current_file.rej)
 
-    for state, values in iterhunks(ui, fp, sourcefile):
+    for state, values in iterhunks(ui, fp):
         if state == 'hunk':
             if not current_file:
                 continue
@@ -1137,14 +1125,10 @@
             rejects += closefile()
             afile, bfile, first_hunk = values
             try:
-                if sourcefile:
-                    current_file = patcher(ui, sourcefile, opener,
-                                           eolmode=eolmode)
-                else:
-                    current_file, missing = selectfile(afile, bfile,
-                                                       first_hunk, strip)
-                    current_file = patcher(ui, current_file, opener,
-                                           missing=missing, eolmode=eolmode)
+                current_file, missing = selectfile(afile, bfile,
+                                                   first_hunk, strip)
+                current_file = patcher(ui, current_file, opener,
+                                       missing=missing, eolmode=eolmode)
             except PatchError, err:
                 ui.warn(str(err) + '\n')
                 current_file = None
--- a/mercurial/revset.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/revset.py	Tue Nov 09 01:33:48 2010 +0900
@@ -202,9 +202,13 @@
     return [r for r in subset if r == l]
 
 def p1(repo, subset, x):
-    """``p1(set)``
-    First parent of changesets in set.
+    """``p1([set])``
+    First parent of changesets in set, or the working directory.
     """
+    if x is None:
+        p = repo[x].parents()[0].rev()
+        return [r for r in subset if r == p]
+
     ps = set()
     cl = repo.changelog
     for r in getset(repo, range(len(repo)), x):
@@ -212,9 +216,17 @@
     return [r for r in subset if r in ps]
 
 def p2(repo, subset, x):
-    """``p2(set)``
-    Second parent of changesets in set.
+    """``p2([set])``
+    Second parent of changesets in set, or the working directory.
     """
+    if x is None:
+        ps = repo[x].parents()
+        try:
+            p = ps[1].rev()
+            return [r for r in subset if r == p]
+        except IndexError:
+            return []
+
     ps = set()
     cl = repo.changelog
     for r in getset(repo, range(len(repo)), x):
@@ -222,9 +234,13 @@
     return [r for r in subset if r in ps]
 
 def parents(repo, subset, x):
-    """``parents(set)``
-    The set of all parents for all changesets in set.
+    """``parents([set])``
+    The set of all parents for all changesets in set, or the working directory.
     """
+    if x is None:
+        ps = tuple(p.rev() for p in repo[x].parents())
+        return [r for r in subset if r in ps]
+
     ps = set()
     cl = repo.changelog
     for r in getset(repo, range(len(repo)), x):
--- a/mercurial/util.py	Mon Nov 08 22:45:56 2010 +0900
+++ b/mercurial/util.py	Tue Nov 09 01:33:48 2010 +0900
@@ -858,23 +858,34 @@
             mode += "b" # for that other OS
 
         nlink = -1
-        if mode not in ("r", "rb"):
+        st_mode = None
+        dirname, basename = os.path.split(f)
+        # If basename is empty, then the path is malformed because it points
+        # to a directory. Let the posixfile() call below raise IOError.
+        if basename and mode not in ('r', 'rb'):
+            if atomictemp:
+                if not os.path.isdir(dirname):
+                    makedirs(dirname, self.createmode)
+                return atomictempfile(f, mode, self.createmode)
             try:
-                nlink = nlinks(f)
+                if 'w' in mode:
+                    st_mode = os.lstat(f).st_mode & 0777
+                    os.unlink(f)
+                    nlink = 0
+                else:
+                    nlink = nlinks(f)
             except OSError:
                 nlink = 0
-                dirname, basename = os.path.split(f)
-                # Avoid calling makedirs when the path points to a
-                # directory -- the open will raise IOError below.
-                if basename and not os.path.isdir(dirname):
+                if not os.path.isdir(dirname):
                     makedirs(dirname, self.createmode)
-            if atomictemp:
-                return atomictempfile(f, mode, self.createmode)
             if nlink > 1:
                 rename(mktempcopy(f), f)
         fp = posixfile(f, mode)
         if nlink == 0:
-            self._fixfilemode(f)
+            if st_mode is None:
+                self._fixfilemode(f)
+            else:
+                os.chmod(f, st_mode)
         return fp
 
     def symlink(self, src, dst):
--- a/tests/test-convert.t	Mon Nov 08 22:45:56 2010 +0900
+++ b/tests/test-convert.t	Tue Nov 09 01:33:48 2010 +0900
@@ -48,8 +48,8 @@
       --sourcesort  try to preserve source revisions order, only supported by
                     Mercurial sources.
   
-      If <REVMAP> isn't given, it will be put in a default location
-      (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file that
+      If "REVMAP" isn't given, it will be put in a default location
+      ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
       maps each source commit ID to the destination ID for that revision, like
       so:
   
@@ -123,16 +123,19 @@
       Mercurial Source
       ''''''''''''''''
   
-      --config convert.hg.ignoreerrors=False    (boolean)
-          ignore integrity errors when reading. Use it to fix Mercurial
-          repositories with missing revlogs, by converting from and to
-          Mercurial.
+      The Mercurial source recognizes the following configuration options, which
+      you can set on the command line with "--config":
   
-      --config convert.hg.saverev=False         (boolean)
-          store original revision ID in changeset (forces target IDs to change)
-  
-      --config convert.hg.startrev=0            (hg revision identifier)
-          convert start revision and its descendants
+      convert.hg.ignoreerrors
+                  ignore integrity errors when reading. Use it to fix Mercurial
+                  repositories with missing revlogs, by converting from and to
+                  Mercurial. Default is False.
+      convert.hg.saverev
+                  store original. revision ID in changeset (forces target IDs to
+                  change). It takes and boolean argument and defaults to False.
+      convert.hg.startrev
+                  convert start revision and its descendants. It takes a hg
+                  revision identifier and defaults to 0.
   
       CVS Source
       ''''''''''
@@ -140,46 +143,45 @@
       CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
       indicate the starting point of what will be converted. Direct access to
       the repository files is not needed, unless of course the repository is
-      :local:. The conversion uses the top level directory in the sandbox to
+      ":local:". The conversion uses the top level directory in the sandbox to
       find the CVS repository, and then uses CVS rlog commands to find files to
       convert. This means that unless a filemap is given, all files under the
       starting directory will be converted, and that any directory
       reorganization in the CVS sandbox is ignored.
   
-      The options shown are the defaults.
-  
-      --config convert.cvsps.cache=True         (boolean)
-          Set to False to disable remote log caching, for testing and debugging
-          purposes.
-  
-      --config convert.cvsps.fuzz=60            (integer)
-          Specify the maximum time (in seconds) that is allowed between commits
-          with identical user and log message in a single changeset. When very
-          large files were checked in as part of a changeset then the default
-          may not be long enough.
+      The following options can be used with "--config":
   
-      --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
-          Specify a regular expression to which commit log messages are matched.
-          If a match occurs, then the conversion process will insert a dummy
-          revision merging the branch on which this log message occurs to the
-          branch indicated in the regex.
-  
-      --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
-          Specify a regular expression to which commit log messages are matched.
-          If a match occurs, then the conversion process will add the most
-          recent revision on the branch indicated in the regex as the second
-          parent of the changeset.
-  
-      --config hook.cvslog
-          Specify a Python function to be called at the end of gathering the CVS
-          log. The function is passed a list with the log entries, and can
-          modify the entries in-place, or add or delete them.
-  
-      --config hook.cvschangesets
-          Specify a Python function to be called after the changesets are
-          calculated from the the CVS log. The function is passed a list with
-          the changeset entries, and can modify the changesets in-place, or add
-          or delete them.
+      convert.cvsps.cache
+                  Set to False to disable remote log caching, for testing and
+                  debugging purposes. Default is True.
+      convert.cvsps.fuzz
+                  Specify the maximum time (in seconds) that is allowed between
+                  commits with identical user and log message in a single
+                  changeset. When very large files were checked in as part of a
+                  changeset then the default may not be long enough. The default
+                  is 60.
+      convert.cvsps.mergeto
+                  Specify a regular expression to which commit log messages are
+                  matched. If a match occurs, then the conversion process will
+                  insert a dummy revision merging the branch on which this log
+                  message occurs to the branch indicated in the regex. Default
+                  is "{{mergetobranch ([-\w]+)}}"
+      convert.cvsps.mergefrom
+                  Specify a regular expression to which commit log messages are
+                  matched. If a match occurs, then the conversion process will
+                  add the most recent revision on the branch indicated in the
+                  regex as the second parent of the changeset. Default is
+                  "{{mergefrombranch ([-\w]+)}}"
+      hook.cvslog
+                  Specify a Python function to be called at the end of gathering
+                  the CVS log. The function is passed a list with the log
+                  entries, and can modify the entries in-place, or add or delete
+                  them.
+      hook.cvschangesets
+                  Specify a Python function to be called after the changesets
+                  are calculated from the the CVS log. The function is passed a
+                  list with the changeset entries, and can modify the changesets
+                  in-place, or add or delete them.
   
       An additional "debugcvsps" Mercurial command allows the builtin changeset
       merging code to be run without doing a conversion. Its parameters and
@@ -199,21 +201,22 @@
       them to paths relative to the source URL, or leave them blank to disable
       auto detection.
   
-      --config convert.svn.branches=branches    (directory name)
-          specify the directory containing branches
+      The following options can be set with "--config":
   
-      --config convert.svn.tags=tags            (directory name)
-          specify the directory containing tags
-  
-      --config convert.svn.trunk=trunk          (directory name)
-          specify the name of the trunk branch
+      convert.svn.branches
+                  specify the directory containing branches. The defaults is
+                  "branches".
+      convert.svn.tags
+                  specify the directory containing tags. The default is "tags".
+      convert.svn.trunk
+                  specify the name of the trunk branch The defauls is "trunk".
   
       Source history can be retrieved starting at a specific revision, instead
       of being integrally converted. Only single branch conversions are
       supported.
   
-      --config convert.svn.startrev=0           (svn revision number)
-          specify start Subversion revision.
+      convert.svn.startrev
+                  specify start Subversion revision number. The default is 0.
   
       Perforce Source
       '''''''''''''''
@@ -222,25 +225,27 @@
       specification as source. It will convert all files in the source to a flat
       Mercurial repository, ignoring labels, branches and integrations. Note
       that when a depot path is given you then usually should specify a target
-      directory, because otherwise the target may be named ...-hg.
+      directory, because otherwise the target may be named "...-hg".
   
       It is possible to limit the amount of source history to be converted by
-      specifying an initial Perforce revision.
+      specifying an initial Perforce revision:
   
-      --config convert.p4.startrev=0            (perforce changelist number)
-          specify initial Perforce revision.
+      convert.p4.startrev
+                  specify initial Perforce revision, a Perforce changelist
+                  number).
   
       Mercurial Destination
       '''''''''''''''''''''
   
-      --config convert.hg.clonebranches=False   (boolean)
-          dispatch source branches in separate clones.
+      The following options are supported:
   
-      --config convert.hg.tagsbranch=default    (branch name)
-          tag revisions branch name
-  
-      --config convert.hg.usebranchnames=True   (boolean)
-          preserve branch names
+      convert.hg.clonebranches
+                  dispatch source branches in separate clones. The default is
+                  False.
+      convert.hg.tagsbranch
+                  branch name for tag revisions, defaults to "default".
+      convert.hg.usebranchnames
+                  preserve branch names. The default is True
   
   options:
   
--- a/tests/test-import.t	Mon Nov 08 22:45:56 2010 +0900
+++ b/tests/test-import.t	Tue Nov 09 01:33:48 2010 +0900
@@ -437,6 +437,13 @@
   $ hg revert -a
   reverting a
 
+
+import with --no-commit should have written .hg/last-message.txt
+
+  $ cat .hg/last-message.txt
+  change (no-eol)
+
+
 test fuzziness with eol=auto
 
   $ hg --config patch.eol=auto import --no-commit -v tip.patch
--- a/tests/test-no-symlinks	Mon Nov 08 22:45:56 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#!/bin/sh
-
-"$TESTDIR/hghave" no-symlink || exit 80
-
-# The following script was used to create the bundle:
-#
-# hg init symlinks
-# cd symlinks
-# echo a > a
-# mkdir d
-# echo b > d/b
-# ln -s a a.lnk
-# ln -s d/b d/b.lnk
-# hg ci -Am t
-# hg bundle --base null ../test-no-symlinks.hg
-
-# Extract a symlink on a platform not supporting them
-echo % unbundle
-hg init t
-cd t
-hg pull -q "$TESTDIR/test-no-symlinks.hg"
-hg update
-
-cat a.lnk && echo
-cat d/b.lnk && echo
-
-# Copy a symlink and move another
-echo % move and copy
-hg copy a.lnk d/a2.lnk
-hg mv d/b.lnk b2.lnk
-hg ci -Am copy
-cat d/a2.lnk && echo
-cat b2.lnk && echo
-
-# Bundle and extract again
-echo % bundle
-hg bundle --base null ../symlinks.hg
-cd ..
-
-hg init t2
-cd t2
-hg pull ../symlinks.hg
-hg update
-
-cat a.lnk && echo
-cat d/a2.lnk && echo
-cat b2.lnk && echo
--- a/tests/test-no-symlinks.out	Mon Nov 08 22:45:56 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-% unbundle
-4 files updated, 0 files merged, 0 files removed, 0 files unresolved
-a
-d/b
-% move and copy
-a
-d/b
-% bundle
-2 changesets found
-pulling from ../symlinks.hg
-requesting all changes
-adding changesets
-adding manifests
-adding file changes
-added 2 changesets with 6 changes to 6 files
-(run 'hg update' to get a working copy)
-5 files updated, 0 files merged, 0 files removed, 0 files unresolved
-a
-a
-d/b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-no-symlinks.t	Tue Nov 09 01:33:48 2010 +0900
@@ -0,0 +1,59 @@
+  $ "$TESTDIR/hghave" no-symlink || exit 80
+
+# The following script was used to create the bundle:
+#
+# hg init symlinks
+# cd symlinks
+# echo a > a
+# mkdir d
+# echo b > d/b
+# ln -s a a.lnk
+# ln -s d/b d/b.lnk
+# hg ci -Am t
+# hg bundle --base null ../test-no-symlinks.hg
+
+Extract a symlink on a platform not supporting them
+
+  $ hg init t
+  $ cd t
+  $ hg pull -q "$TESTDIR/test-no-symlinks.hg"
+  $ hg update
+  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat a.lnk && echo
+  a
+  $ cat d/b.lnk && echo
+  d/b
+
+Copy a symlink and move another
+
+  $ hg copy a.lnk d/a2.lnk
+  $ hg mv d/b.lnk b2.lnk
+  $ hg ci -Am copy
+  $ cat d/a2.lnk && echo
+  a
+  $ cat b2.lnk && echo
+  d/b
+
+Bundle and extract again
+
+  $ hg bundle --base null ../symlinks.hg
+  2 changesets found
+  $ cd ..
+  $ hg init t2
+  $ cd t2
+  $ hg pull ../symlinks.hg
+  pulling from ../symlinks.hg
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 6 changes to 6 files
+  (run 'hg update' to get a working copy)
+  $ hg update
+  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat a.lnk && echo
+  a
+  $ cat d/a2.lnk && echo
+  a
+  $ cat b2.lnk && echo
+  d/b
--- a/tests/test-parentrevspec.t	Mon Nov 08 22:45:56 2010 +0900
+++ b/tests/test-parentrevspec.t	Tue Nov 09 01:33:48 2010 +0900
@@ -69,12 +69,12 @@
   6^^^^^: 0
   6^^^^^^: -1
   6^1: 5
-  6^2: abort: unknown revision '6^2'!
+  6^2: hg: parse error at 1: syntax error
   6^^2: 4
   6^1^2: 4
-  6^^3: abort: unknown revision '6^^3'!
+  6^^3: hg: parse error at 1: syntax error
   $ lookup "6~" "6~1" "6~2" "6~3" "6~4" "6~5" "6~42" "6~1^2" "6~1^2~2"
-  6~: abort: unknown revision '6~'!
+  6~: hg: parse error at 1: syntax error
   6~1: 5
   6~2: 3
   6~3: 2
@@ -102,4 +102,4 @@
   $ hg tag -l -r 2 "foo^bar"
   $ lookup "foo^bar" "foo^bar^"
   foo^bar: 2
-  foo^bar^: abort: unknown revision 'foo^bar^'!
+  foo^bar^: hg: parse error at 3: syntax error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-revset-dirstate-parents.t	Tue Nov 09 01:33:48 2010 +0900
@@ -0,0 +1,52 @@
+  $ HGENCODING=utf-8
+  $ export HGENCODING
+
+  $ try() {
+  >   hg debugrevspec --debug $@
+  > }
+
+  $ log() {
+  >   hg log --template '{rev}\n' -r "$1"
+  > }
+
+  $ hg init repo
+  $ cd repo
+
+  $ try 'p1()'
+  ('func', ('symbol', 'p1'), None)
+  $ try 'p2()'
+  ('func', ('symbol', 'p2'), None)
+  $ try 'parents()'
+  ('func', ('symbol', 'parents'), None)
+
+null revision
+  $ log 'p1()'
+  $ log 'p2()'
+  $ log 'parents()'
+
+working dir with a single parent
+  $ echo a > a
+  $ hg ci -Aqm0
+  $ log 'p1()'
+  0
+  $ log 'tag() and p1()'
+  $ log 'p2()'
+  $ log 'parents()'
+  0
+  $ log 'tag() and parents()'
+
+merge in progress
+  $ echo b > b
+  $ hg ci -Aqm1
+  $ hg up -q 0
+  $ echo c > c
+  $ hg ci -Aqm2
+  $ hg merge -q
+  $ log 'p1()'
+  2
+  $ log 'p2()'
+  1
+  $ log 'tag() and p2()'
+  $ log 'parents()'
+  1
+  2