merge with stable
authorMatt Mackall <mpm@selenic.com>
Sun, 04 Mar 2012 17:14:30 -0600
changeset 16229 5cb2693e1a60
parent 16227 7855c522a9cb (diff)
parent 16228 5b41d5ad52bf (current diff)
child 16232 877aea86fb73
merge with stable
--- a/Makefile	Sun Mar 04 17:12:12 2012 -0600
+++ b/Makefile	Sun Mar 04 17:14:30 2012 -0600
@@ -91,8 +91,10 @@
 
 i18n/hg.pot: $(PYFILES) $(DOCFILES)
 	$(PYTHON) i18n/hggettext mercurial/commands.py \
-	  hgext/*.py hgext/*/__init__.py mercurial/fileset.py mercurial/revset.py \
+	  hgext/*.py hgext/*/__init__.py \
+	  mercurial/fileset.py mercurial/revset.py \
 	  mercurial/templatefilters.py mercurial/templatekw.py \
+	  mercurial/filemerge.py \
 	  $(DOCFILES) > i18n/hg.pot
         # All strings marked for translation in Mercurial contain
         # ASCII characters only. But some files contain string
--- a/README	Sun Mar 04 17:12:12 2012 -0600
+++ b/README	Sun Mar 04 17:14:30 2012 -0600
@@ -11,5 +11,10 @@
  $ hg debuginstall # sanity-check setup
  $ hg              # see help
 
+Running without installing:
+
+ $ make local      # build for inplace usage
+ $ ./hg --version  # should show the latest version
+
 See http://mercurial.selenic.com/ for detailed installation
 instructions, platform-specific notes, and Mercurial user information.
--- a/hgext/bugzilla.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/bugzilla.py	Sun Mar 04 17:14:30 2012 -0600
@@ -1,7 +1,7 @@
 # bugzilla.py - bugzilla integration for mercurial
 #
 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
-# Copyright 2011 Jim Hague <jim.hague@acm.org>
+# Copyright 2011-2 Jim Hague <jim.hague@acm.org>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
@@ -12,7 +12,8 @@
 that refer to bugs by Bugzilla ID are seen. The comment is formatted using
 the Mercurial template mechanism.
 
-The hook does not change bug status.
+The bug references can optionally include an update for Bugzilla of the
+hours spent working on the bug. Bugs can also be marked fixed.
 
 Three basic modes of access to Bugzilla are provided:
 
@@ -32,13 +33,13 @@
 MySQL user and password to have full access rights to the Bugzilla
 database. For these reasons this access mode is now considered
 deprecated, and will not be updated for new Bugzilla versions going
-forward.
+forward. Only adding comments is supported in this access mode.
 
 Access via XMLRPC needs a Bugzilla username and password to be specified
 in the configuration. Comments are added under that username. Since the
 configuration must be readable by all Mercurial users, it is recommended
 that the rights of that user are restricted in Bugzilla to the minimum
-necessary to add comments.
+necessary to add comments. Marking bugs fixed requires Bugzilla 4.0 and later.
 
 Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends
 email to the Bugzilla email interface to submit comments to bugs.
@@ -46,7 +47,8 @@
 user, so the comment appears to come from the Mercurial user. In the event
 that the Mercurial user email is not recognised by Bugzilla as a Bugzilla
 user, the email associated with the Bugzilla username used to log into
-Bugzilla is used instead as the source of the comment.
+Bugzilla is used instead as the source of the comment. Marking bugs fixed
+works on all supported Bugzilla versions.
 
 Configuration items common to all access modes:
 
@@ -62,11 +64,34 @@
                      including 2.18.
 
 bugzilla.regexp
-  Regular expression to match bug IDs in changeset commit message.
-  Must contain one "()" group. The default expression matches ``Bug
-  1234``, ``Bug no. 1234``, ``Bug number 1234``, ``Bugs 1234,5678``,
-  ``Bug 1234 and 5678`` and variations thereof. Matching is case
-  insensitive.
+  Regular expression to match bug IDs for update in changeset commit message.
+  It must contain one "()" named group ``<ids>`` containing the bug
+  IDs separated by non-digit characters. It may also contain
+  a named group ``<hours>`` with a floating-point number giving the
+  hours worked on the bug. If no named groups are present, the first
+  "()" group is assumed to contain the bug IDs, and work time is not
+  updated. The default expression matches ``Bug 1234``, ``Bug no. 1234``,
+  ``Bug number 1234``, ``Bugs 1234,5678``, ``Bug 1234 and 5678`` and
+  variations thereof, followed by an hours number prefixed by ``h`` or
+  ``hours``, e.g. ``hours 1.5``. Matching is case insensitive.
+
+bugzilla.fixregexp
+  Regular expression to match bug IDs for marking fixed in changeset
+  commit message. This must contain a "()" named group ``<ids>` containing
+  the bug IDs separated by non-digit characters. It may also contain
+  a named group ``<hours>`` with a floating-point number giving the
+  hours worked on the bug. If no named groups are present, the first
+  "()" group is assumed to contain the bug IDs, and work time is not
+  updated. The default expression matches ``Fixes 1234``, ``Fixes bug 1234``,
+  ``Fixes bugs 1234,5678``, ``Fixes 1234 and 5678`` and
+  variations thereof, followed by an hours number prefixed by ``h`` or
+  ``hours``, e.g. ``hours 1.5``. Matching is case insensitive.
+
+bugzilla.fixstatus
+  The status to set a bug to when marking fixed. Default ``RESOLVED``.
+
+bugzilla.fixresolution
+  The resolution to set a bug to when marking fixed. Default ``FIXED``.
 
 bugzilla.style
   The style file to use when formatting comments.
@@ -274,24 +299,35 @@
         return user
 
     # Methods to be implemented by access classes.
-    def filter_real_bug_ids(self, ids):
-        '''remove bug IDs that do not exist in Bugzilla from set.'''
+    #
+    # 'bugs' is a dict keyed on bug id, where values are a dict holding
+    # updates to bug state. Recognised dict keys are:
+    #
+    # 'hours': Value, float containing work hours to be updated.
+    # 'fix':   If key present, bug is to be marked fixed. Value ignored.
+
+    def filter_real_bug_ids(self, bugs):
+        '''remove bug IDs that do not exist in Bugzilla from bugs.'''
         pass
 
-    def filter_cset_known_bug_ids(self, node, ids):
-        '''remove bug IDs where node occurs in comment text from set.'''
+    def filter_cset_known_bug_ids(self, node, bugs):
+        '''remove bug IDs where node occurs in comment text from bugs.'''
         pass
 
-    def add_comment(self, bugid, text, committer):
-        '''add comment to bug.
+    def updatebug(self, bugid, newstate, text, committer):
+        '''update the specified bug. Add comment text and set new states.
 
         If possible add the comment as being from the committer of
         the changeset. Otherwise use the default Bugzilla user.
         '''
         pass
 
-    def notify(self, ids, committer):
-        '''Force sending of Bugzilla notification emails.'''
+    def notify(self, bugs, committer):
+        '''Force sending of Bugzilla notification emails.
+
+        Only required if the access method does not trigger notification
+        emails automatically.
+        '''
         pass
 
 # Bugzilla via direct access to MySQL database.
@@ -353,30 +389,31 @@
             raise util.Abort(_('unknown database schema'))
         return ids[0][0]
 
-    def filter_real_bug_ids(self, ids):
-        '''filter not-existing bug ids from set.'''
+    def filter_real_bug_ids(self, bugs):
+        '''filter not-existing bugs from set.'''
         self.run('select bug_id from bugs where bug_id in %s' %
-                 bzmysql.sql_buglist(ids))
-        return set([c[0] for c in self.cursor.fetchall()])
+                 bzmysql.sql_buglist(bugs.keys()))
+        existing = [id for (id,) in self.cursor.fetchall()]
+        for id in bugs.keys():
+            if id not in existing:
+                self.ui.status(_('bug %d does not exist\n') % id)
+                del bugs[id]
 
-    def filter_cset_known_bug_ids(self, node, ids):
+    def filter_cset_known_bug_ids(self, node, bugs):
         '''filter bug ids that already refer to this changeset from set.'''
-
         self.run('''select bug_id from longdescs where
                     bug_id in %s and thetext like "%%%s%%"''' %
-                 (bzmysql.sql_buglist(ids), short(node)))
+                 (bzmysql.sql_buglist(bugs.keys()), short(node)))
         for (id,) in self.cursor.fetchall():
             self.ui.status(_('bug %d already knows about changeset %s\n') %
                            (id, short(node)))
-            ids.discard(id)
-        return ids
+            del bugs[id]
 
-    def notify(self, ids, committer):
+    def notify(self, bugs, committer):
         '''tell bugzilla to send mail.'''
-
         self.ui.status(_('telling bugzilla to send mail:\n'))
         (user, userid) = self.get_bugzilla_user(committer)
-        for id in ids:
+        for id in bugs.keys():
             self.ui.status(_('  bug %s\n') % id)
             cmdfmt = self.ui.config('bugzilla', 'notify', self.default_notify)
             bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
@@ -435,9 +472,14 @@
                                  (user, defaultuser))
         return (user, userid)
 
-    def add_comment(self, bugid, text, committer):
-        '''add comment to bug. try adding comment as committer of
-        changeset, otherwise as default bugzilla user.'''
+    def updatebug(self, bugid, newstate, text, committer):
+        '''update bug state with comment text.
+
+        Try adding comment as committer of changeset, otherwise as
+        default bugzilla user.'''
+        if len(newstate) > 0:
+            self.ui.warn(_("Bugzilla/MySQL cannot update bug state\n"))
+
         (user, userid) = self.get_bugzilla_user(committer)
         now = time.strftime('%Y-%m-%d %H:%M:%S')
         self.run('''insert into longdescs
@@ -565,7 +607,14 @@
         user = self.ui.config('bugzilla', 'user', 'bugs')
         passwd = self.ui.config('bugzilla', 'password')
 
+        self.fixstatus = self.ui.config('bugzilla', 'fixstatus', 'RESOLVED')
+        self.fixresolution = self.ui.config('bugzilla', 'fixresolution',
+                                            'FIXED')
+
         self.bzproxy = xmlrpclib.ServerProxy(bzweb, self.transport(bzweb))
+        ver = self.bzproxy.Bugzilla.version()['version'].split('.')
+        self.bzvermajor = int(ver[0])
+        self.bzverminor = int(ver[1])
         self.bzproxy.User.login(dict(login=user, password=passwd))
 
     def transport(self, uri):
@@ -576,36 +625,64 @@
 
     def get_bug_comments(self, id):
         """Return a string with all comment text for a bug."""
-        c = self.bzproxy.Bug.comments(dict(ids=[id]))
+        c = self.bzproxy.Bug.comments(dict(ids=[id], include_fields=['text']))
         return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']])
 
-    def filter_real_bug_ids(self, ids):
-        res = set()
-        bugs = self.bzproxy.Bug.get(dict(ids=sorted(ids), permissive=True))
-        for bug in bugs['bugs']:
-            res.add(bug['id'])
-        return res
+    def filter_real_bug_ids(self, bugs):
+        probe = self.bzproxy.Bug.get(dict(ids=sorted(bugs.keys()),
+                                          include_fields=[],
+                                          permissive=True))
+        for badbug in probe['faults']:
+            id = badbug['id']
+            self.ui.status(_('bug %d does not exist\n') % id)
+            del bugs[id]
 
-    def filter_cset_known_bug_ids(self, node, ids):
-        for id in sorted(ids):
+    def filter_cset_known_bug_ids(self, node, bugs):
+        for id in sorted(bugs.keys()):
             if self.get_bug_comments(id).find(short(node)) != -1:
                 self.ui.status(_('bug %d already knows about changeset %s\n') %
                                (id, short(node)))
-                ids.discard(id)
-        return ids
+                del bugs[id]
+
+    def updatebug(self, bugid, newstate, text, committer):
+        args = {}
+        if 'hours' in newstate:
+            args['work_time'] = newstate['hours']
 
-    def add_comment(self, bugid, text, committer):
-        self.bzproxy.Bug.add_comment(dict(id=bugid, comment=text))
+        if self.bzvermajor >= 4:
+            args['ids'] = [bugid]
+            args['comment'] = {'body' : text}
+            args['status'] = self.fixstatus
+            args['resolution'] = self.fixresolution
+            self.bzproxy.Bug.update(args)
+        else:
+            if 'fix' in newstate:
+                self.ui.warn(_("Bugzilla/XMLRPC needs Bugzilla 4.0 or later "
+                               "to mark bugs fixed\n"))
+            args['id'] = bugid
+            args['comment'] = text
+            self.bzproxy.Bug.add_comment(args)
 
 class bzxmlrpcemail(bzxmlrpc):
     """Read data from Bugzilla via XMLRPC, send updates via email.
 
     Advantages of sending updates via email:
       1. Comments can be added as any user, not just logged in user.
-      2. Bug statuses and other fields not accessible via XMLRPC can
-        be updated. This is not currently used.
+      2. Bug statuses or other fields not accessible via XMLRPC can
+         potentially be updated.
+
+    There is no XMLRPC function to change bug status before Bugzilla
+    4.0, so bugs cannot be marked fixed via XMLRPC before Bugzilla 4.0.
+    But bugs can be marked fixed via email from 3.4 onwards.
     """
 
+    # The email interface changes subtly between 3.4 and 3.6. In 3.4,
+    # in-email fields are specified as '@<fieldname> = <value>'. In
+    # 3.6 this becomes '@<fieldname> <value>'. And fieldname @bug_id
+    # in 3.4 becomes @id in 3.6. 3.6 and 4.0 both maintain backwards
+    # compatibility, but rather than rely on this use the new format for
+    # 4.0 onwards.
+
     def __init__(self, ui):
         bzxmlrpc.__init__(self, ui)
 
@@ -614,6 +691,14 @@
             raise util.Abort(_("configuration 'bzemail' missing"))
         mail.validateconfig(self.ui)
 
+    def makecommandline(self, fieldname, value):
+        if self.bzvermajor >= 4:
+            return "@%s %s" % (fieldname, str(value))
+        else:
+            if fieldname == "id":
+                fieldname = "bug_id"
+            return "@%s = %s" % (fieldname, str(value))
+
     def send_bug_modify_email(self, bugid, commands, comment, committer):
         '''send modification message to Bugzilla bug via email.
 
@@ -634,8 +719,9 @@
                 raise util.Abort(_("default bugzilla user %s email not found") %
                                  user)
         user = matches['users'][0]['email']
+        commands.append(self.makecommandline("id", bugid))
 
-        text = "\n".join(commands) + "\n@bug_id = %d\n\n" % bugid + comment
+        text = "\n".join(commands) + "\n\n" + comment
 
         _charsets = mail._charsets(self.ui)
         user = mail.addressencode(self.ui, user, _charsets)
@@ -647,8 +733,14 @@
         sendmail = mail.connect(self.ui)
         sendmail(user, bzemail, msg.as_string())
 
-    def add_comment(self, bugid, text, committer):
-        self.send_bug_modify_email(bugid, [], text, committer)
+    def updatebug(self, bugid, newstate, text, committer):
+        cmds = []
+        if 'hours' in newstate:
+            cmds.append(self.makecommandline("work_time", newstate['hours']))
+        if 'fix' in newstate:
+            cmds.append(self.makecommandline("bug_status", self.fixstatus))
+            cmds.append(self.makecommandline("resolution", self.fixresolution))
+        self.send_bug_modify_email(bugid, cmds, text, committer)
 
 class bugzilla(object):
     # supported versions of bugzilla. different versions have
@@ -662,7 +754,13 @@
         }
 
     _default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
-                       r'((?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)')
+                       r'(?P<ids>(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
+                       r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
+
+    _default_fix_re = (r'fix(?:es)?\s*(?:bugs?\s*)?,?\s*'
+                       r'(?:nos?\.?|num(?:ber)?s?)?\s*'
+                       r'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
+                       r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
 
     _bz = None
 
@@ -688,38 +786,76 @@
         return getattr(self.bz(), key)
 
     _bug_re = None
+    _fix_re = None
     _split_re = None
 
-    def find_bug_ids(self, ctx):
-        '''return set of integer bug IDs from commit comment.
+    def find_bugs(self, ctx):
+        '''return bugs dictionary created from commit comment.
 
-        Extract bug IDs from changeset comments. Filter out any that are
+        Extract bug info from changeset comments. Filter out any that are
         not known to Bugzilla, and any that already have a reference to
         the given changeset in their comments.
         '''
         if bugzilla._bug_re is None:
             bugzilla._bug_re = re.compile(
-                self.ui.config('bugzilla', 'regexp', bugzilla._default_bug_re),
-                re.IGNORECASE)
+                self.ui.config('bugzilla', 'regexp',
+                               bugzilla._default_bug_re), re.IGNORECASE)
+            bugzilla._fix_re = re.compile(
+                self.ui.config('bugzilla', 'fixregexp',
+                               bugzilla._default_fix_re), re.IGNORECASE)
             bugzilla._split_re = re.compile(r'\D+')
         start = 0
-        ids = set()
+        hours = 0.0
+        bugs = {}
+        bugmatch = bugzilla._bug_re.search(ctx.description(), start)
+        fixmatch = bugzilla._fix_re.search(ctx.description(), start)
         while True:
-            m = bugzilla._bug_re.search(ctx.description(), start)
-            if not m:
+            bugattribs = {}
+            if not bugmatch and not fixmatch:
                 break
+            if not bugmatch:
+                m = fixmatch
+            elif not fixmatch:
+                m = bugmatch
+            else:
+                if bugmatch.start() < fixmatch.start():
+                    m = bugmatch
+                else:
+                    m = fixmatch
             start = m.end()
-            for id in bugzilla._split_re.split(m.group(1)):
+            if m is bugmatch:
+                bugmatch = bugzilla._bug_re.search(ctx.description(), start)
+                if 'fix' in bugattribs:
+                    del bugattribs['fix']
+            else:
+                fixmatch = bugzilla._fix_re.search(ctx.description(), start)
+                bugattribs['fix'] = None
+
+            try:
+                ids = m.group('ids')
+            except IndexError:
+                ids = m.group(1)
+            try:
+                hours = float(m.group('hours'))
+                bugattribs['hours'] = hours
+            except IndexError:
+                pass
+            except TypeError:
+                pass
+            except ValueError:
+                self.ui.status(_("%s: invalid hours\n") % m.group('hours'))
+
+            for id in bugzilla._split_re.split(ids):
                 if not id:
                     continue
-                ids.add(int(id))
-        if ids:
-            ids = self.filter_real_bug_ids(ids)
-        if ids:
-            ids = self.filter_cset_known_bug_ids(ctx.node(), ids)
-        return ids
+                bugs[int(id)] = bugattribs
+        if bugs:
+            self.filter_real_bug_ids(bugs)
+        if bugs:
+            self.filter_cset_known_bug_ids(ctx.node(), bugs)
+        return bugs
 
-    def update(self, bugid, ctx):
+    def update(self, bugid, newstate, ctx):
         '''update bugzilla bug with reference to changeset.'''
 
         def webroot(root):
@@ -752,7 +888,7 @@
                root=self.repo.root,
                webroot=webroot(self.repo.root))
         data = self.ui.popbuffer()
-        self.add_comment(bugid, data, util.email(ctx.user()))
+        self.updatebug(bugid, newstate, data, util.email(ctx.user()))
 
 def hook(ui, repo, hooktype, node=None, **kwargs):
     '''add comment to bugzilla for each changeset that refers to a
@@ -764,11 +900,11 @@
     try:
         bz = bugzilla(ui, repo)
         ctx = repo[node]
-        ids = bz.find_bug_ids(ctx)
-        if ids:
-            for id in ids:
-                bz.update(id, ctx)
-            bz.notify(ids, util.email(ctx.user()))
+        bugs = bz.find_bugs(ctx)
+        if bugs:
+            for bug in bugs:
+                bz.update(bug, bugs[bug], ctx)
+            bz.notify(bugs, util.email(ctx.user()))
     except Exception, e:
         raise util.Abort(_('Bugzilla error: %s') % e)
 
--- a/hgext/convert/bzr.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/convert/bzr.py	Sun Mar 04 17:14:30 2012 -0600
@@ -23,7 +23,7 @@
 
 try:
     # bazaar imports
-    from bzrlib import branch, revision, errors
+    from bzrlib import bzrdir, revision, errors
     from bzrlib.revisionspec import RevisionSpec
 except ImportError:
     pass
@@ -42,14 +42,17 @@
 
         try:
             # access bzrlib stuff
-            branch
+            bzrdir
         except NameError:
             raise NoRepo(_('Bazaar modules could not be loaded'))
 
         path = os.path.abspath(path)
         self._checkrepotype(path)
-        self.branch = branch.Branch.open(path)
-        self.sourcerepo = self.branch.repository
+        try:
+            self.sourcerepo = bzrdir.BzrDir.open(path).open_repository()
+        except errors.NoRepositoryPresent:
+            raise NoRepo(_('%s does not look like a Bazaar repository')
+                         % path)
         self._parentids = {}
 
     def _checkrepotype(self, path):
@@ -88,16 +91,28 @@
     def after(self):
         self.sourcerepo.unlock()
 
+    def _bzrbranches(self):
+        return self.sourcerepo.find_branches(using=True)
+
     def getheads(self):
         if not self.rev:
-            return [self.branch.last_revision()]
-        try:
-            r = RevisionSpec.from_string(self.rev)
-            info = r.in_history(self.branch)
-        except errors.BzrError:
-            raise util.Abort(_('%s is not a valid revision in current branch')
-                             % self.rev)
-        return [info.rev_id]
+            # Set using=True to avoid nested repositories (see issue3254)
+            heads = sorted([b.last_revision() for b in self._bzrbranches()])
+        else:
+            revid = None
+            for branch in self._bzrbranches():
+                try:
+                    r = RevisionSpec.from_string(self.rev)
+                    info = r.in_history(branch)
+                except errors.BzrError:
+                    pass
+                revid = info.rev_id
+            if revid is None:
+                raise util.Abort(_('%s is not a valid revision') % self.rev)
+            heads = [revid]
+        # Empty repositories return 'null:', which cannot be retrieved
+        heads = [h for h in heads if h != 'null:']
+        return heads
 
     def getfile(self, name, rev):
         revtree = self.sourcerepo.revision_tree(rev)
@@ -140,20 +155,24 @@
             parents = self._filterghosts(rev.parent_ids)
             self._parentids[version] = parents
 
+        branch = self.recode(rev.properties.get('branch-nick', u'default'))
+        if branch == 'trunk':
+            branch = 'default'
         return commit(parents=parents,
                 date='%d %d' % (rev.timestamp, -rev.timezone),
                 author=self.recode(rev.committer),
-                # bzr returns bytestrings or unicode, depending on the content
                 desc=self.recode(rev.message),
+                branch=branch,
                 rev=version)
 
     def gettags(self):
-        if not self.branch.supports_tags():
-            return {}
-        tagdict = self.branch.tags.get_tag_dict()
         bytetags = {}
-        for name, rev in tagdict.iteritems():
-            bytetags[self.recode(name)] = rev
+        for branch in self._bzrbranches():
+            if not branch.supports_tags():
+                return {}
+            tagdict = branch.tags.get_tag_dict()
+            for name, rev in tagdict.iteritems():
+                bytetags[self.recode(name)] = rev
         return bytetags
 
     def getchangedfiles(self, rev, i):
@@ -231,7 +250,11 @@
                 continue
 
             # we got unicode paths, need to convert them
-            path, topath = [self.recode(part) for part in paths]
+            path, topath = paths
+            if path is not None:
+                path = self.recode(path)
+            if topath is not None:
+                topath = self.recode(topath)
             seen.add(path or topath)
 
             if topath is None:
@@ -260,19 +283,3 @@
         parentmap = self.sourcerepo.get_parent_map(ids)
         parents = tuple([parent for parent in ids if parent in parentmap])
         return parents
-
-    def recode(self, s, encoding=None):
-        """This version of recode tries to encode unicode to bytecode,
-        and preferably using the UTF-8 codec.
-        Other types than Unicode are silently returned, this is by
-        intention, e.g. the None-type is not going to be encoded but instead
-        just passed through
-        """
-        if not encoding:
-            encoding = self.encoding or 'utf-8'
-
-        if isinstance(s, unicode):
-            return s.encode(encoding)
-        else:
-            # leave it alone
-            return s
--- a/hgext/graphlog.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/graphlog.py	Sun Mar 04 17:14:30 2012 -0600
@@ -17,7 +17,7 @@
 from mercurial.i18n import _
 from mercurial.node import nullrev
 from mercurial import cmdutil, commands, extensions, scmutil
-from mercurial import hg, util, graphmod
+from mercurial import hg, util, graphmod, templatekw
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -237,69 +237,175 @@
         return (len(repo) - 1, 0)
 
 def check_unsupported_flags(pats, opts):
-    for op in ["follow_first", "copies", "newest_first"]:
+    for op in ["newest_first"]:
         if op in opts and opts[op]:
             raise util.Abort(_("-G/--graph option is incompatible with --%s")
                              % op.replace("_", "-"))
-    if pats and opts.get('follow'):
-        raise util.Abort(_("-G/--graph option is incompatible with --follow "
-                           "with file argument"))
+
+def makefilematcher(repo, pats, followfirst):
+    # When displaying a revision with --patch --follow FILE, we have
+    # to know which file of the revision must be diffed. With
+    # --follow, we want the names of the ancestors of FILE in the
+    # revision, stored in "fcache". "fcache" is populated by
+    # reproducing the graph traversal already done by --follow revset
+    # and relating linkrevs to file names (which is not "correct" but
+    # good enough).
+    fcache = {}
+    fcacheready = [False]
+    pctx = repo['.']
+    wctx = repo[None]
 
-def revset(pats, opts):
-    """Return revset str built of revisions, log options and file patterns.
+    def populate():
+        for fn in pats:
+            for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
+                for c in i:
+                    fcache.setdefault(c.linkrev(), set()).add(c.path())
+
+    def filematcher(rev):
+        if not fcacheready[0]:
+            # Lazy initialization
+            fcacheready[0] = True
+            populate()
+        return scmutil.match(wctx, fcache.get(rev, []), default='path')
+
+    return filematcher
+
+def revset(repo, pats, opts):
+    """Return (expr, filematcher) where expr is a revset string built
+    of revisions, log options and file patterns. If --stat or --patch
+    are not passed filematcher is None. Otherwise it a a callable
+    taking a revision number and returning a match objects filtering
+    the files to be detailed when displaying the revision.
     """
     opt2revset = {
-        'follow': (0, 'follow()'),
-        'no_merges': (0, 'not merge()'),
-        'only_merges': (0, 'merge()'),
-        'removed': (0, 'removes("*")'),
-        'date': (1, 'date($)'),
-        'branch': (2, 'branch($)'),
-        'exclude': (2, 'not file($)'),
-        'include': (2, 'file($)'),
-        'keyword': (2, 'keyword($)'),
-        'only_branch': (2, 'branch($)'),
-        'prune': (2, 'not ($ or ancestors($))'),
-        'user': (2, 'user($)'),
+        'follow':           ('follow()', None),
+        'follow_first':     ('_followfirst()', None),
+        'no_merges':        ('not merge()', None),
+        'only_merges':      ('merge()', None),
+        'date':             ('date(%(val)r)', None),
+        'branch':           ('branch(%(val)r)', ' or '),
+        '_patslog':         ('filelog(%(val)r)', ' or '),
+        '_patsfollow':      ('follow(%(val)r)', ' or '),
+        '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
+        'keyword':          ('keyword(%(val)r)', ' or '),
+        'prune':            ('not (%(val)r or ancestors(%(val)r))', ' and '),
+        'user':             ('user(%(val)r)', ' or '),
+        'rev':              ('%(val)s', ' or '),
         }
-    optrevset = []
+
+    opts = dict(opts)
+    # branch and only_branch are really aliases and must be handled at
+    # the same time
+    if 'branch' in opts and 'only_branch' in opts:
+        opts['branch'] = opts['branch'] + opts.pop('only_branch')
+
+    follow = opts.get('follow') or opts.get('follow_first')
+    followfirst = opts.get('follow_first')
+    if 'follow' in opts:
+        del opts['follow']
+    if 'follow_first' in opts:
+        del opts['follow_first']
+    # pats/include/exclude are passed to match.match() directly in
+    # _matchfile() revset but walkchangerevs() builds its matcher with
+    # scmutil.match(). The difference is input pats are globbed on
+    # platforms without shell expansion (windows).
+    pctx = repo[None]
+    match, pats = scmutil.matchandpats(pctx, pats, opts)
+    slowpath = match.anypats() or (match.files() and opts.get('removed'))
+    if not slowpath:
+        for f in match.files():
+            if follow and f not in pctx:
+                raise util.Abort(_('cannot follow file not in parent '
+                                   'revision: "%s"') % f)
+            filelog = repo.file(f)
+            if not len(filelog):
+                # A zero count may be a directory or deleted file, so
+                # try to find matching entries on the slow path.
+                if follow:
+                    raise util.Abort(
+                        _('cannot follow nonexistent file: "%s"') % f)
+                slowpath = True
+    if slowpath:
+        # See cmdutil.walkchangerevs() slow path.
+        #
+        if follow:
+            raise util.Abort(_('can only follow copies/renames for explicit '
+                               'filenames'))
+        # pats/include/exclude cannot be represented as separate
+        # revset expressions as their filtering logic applies at file
+        # level. For instance "-I a -X a" matches a revision touching
+        # "a" and "b" while "file(a) and not file(b)" does
+        # not. Besides, filesets are evaluated against the working
+        # directory.
+        matchargs = ['r:']
+        for p in pats:
+            matchargs.append('p:' + p)
+        for p in opts.get('include', []):
+            matchargs.append('i:' + p)
+        for p in opts.get('exclude', []):
+            matchargs.append('x:' + p)
+        matchargs = ','.join(('%r' % p) for p in matchargs)
+        opts['rev'] = opts.get('rev', []) + ['_matchfiles(%s)' % matchargs]
+    else:
+        if follow:
+            if followfirst:
+                if pats:
+                    opts['_patsfollowfirst'] = list(pats)
+                else:
+                    opts['follow_first'] = True
+            else:
+                if pats:
+                    opts['_patsfollow'] = list(pats)
+                else:
+                    opts['follow'] = True
+        else:
+            opts['_patslog'] = list(pats)
+
+    filematcher = None
+    if opts.get('patch') or opts.get('stat'):
+        if follow:
+            filematcher = makefilematcher(repo, pats, followfirst)
+        else:
+            filematcher = lambda rev: match
+
     revset = []
     for op, val in opts.iteritems():
         if not val:
             continue
-        if op == 'rev':
-            # Already a revset
-            revset.extend(val)
         if op not in opt2revset:
             continue
-        arity, revop = opt2revset[op]
-        revop = revop.replace('$', '%(val)r')
-        if arity == 0:
-            optrevset.append(revop)
-        elif arity == 1:
-            optrevset.append(revop % {'val': val})
+        revop, andor = opt2revset[op]
+        if '%(val)' not in revop:
+            revset.append(revop)
         else:
-            for f in val:
-                optrevset.append(revop % {'val': f})
+            if not isinstance(val, list):
+                expr = revop % {'val': val}
+            else:
+                expr = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
+            revset.append(expr)
 
-    for path in pats:
-        optrevset.append('file(%r)' % path)
-
-    if revset or optrevset:
-        if revset:
-            revset = ['(' + ' or '.join(revset) + ')']
-        if optrevset:
-            revset.append('(' + ' and '.join(optrevset) + ')')
-        revset = ' and '.join(revset)
+    if revset:
+        revset = '(' + ' and '.join(revset) + ')'
     else:
         revset = 'all()'
-    return revset
+    return revset, filematcher
 
-def generate(ui, dag, displayer, showparents, edgefn):
+def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
+             filematcher=None):
     seen, state = [], asciistate()
     for rev, type, ctx, parents in dag:
         char = ctx.node() in showparents and '@' or 'o'
-        displayer.show(ctx)
+        copies = None
+        if getrenamed and ctx.rev():
+            copies = []
+            for fn in ctx.files():
+                rename = getrenamed(fn, ctx.rev())
+                if rename:
+                    copies.append((fn, rename[0]))
+        revmatchfn = None
+        if filematcher is not None:
+            revmatchfn = filematcher(ctx.rev())
+        displayer.show(ctx, copies=copies, matchfn=revmatchfn)
         lines = displayer.hunk.pop(rev).split('\n')[:-1]
         displayer.flush(rev)
         edges = edgefn(type, char, lines, seen, rev, parents)
@@ -326,15 +432,23 @@
 
     check_unsupported_flags(pats, opts)
 
-    revs = sorted(scmutil.revrange(repo, [revset(pats, opts)]), reverse=1)
+    expr, filematcher = revset(repo, pats, opts)
+    revs = sorted(scmutil.revrange(repo, [expr]), reverse=1)
     limit = cmdutil.loglimit(opts)
     if limit is not None:
         revs = revs[:limit]
     revdag = graphmod.dagwalker(repo, revs)
 
+    getrenamed = None
+    if opts.get('copies'):
+        endrev = None
+        if opts.get('rev'):
+            endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
+        getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
     displayer = show_changeset(ui, repo, opts, buffered=True)
     showparents = [ctx.node() for ctx in repo[None].parents()]
-    generate(ui, revdag, displayer, showparents, asciiedges)
+    generate(ui, revdag, displayer, showparents, asciiedges, getrenamed,
+             filematcher)
 
 def graphrevs(repo, nodes, opts):
     limit = cmdutil.loglimit(opts)
--- a/hgext/largefiles/lfutil.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/largefiles/lfutil.py	Sun Mar 04 17:14:30 2012 -0600
@@ -449,3 +449,11 @@
         newheads = repo.branchheads(branch)
         heads = heads + newheads
     return heads
+
+def getstandinsstate(repo):
+    standins = []
+    matcher = getstandinmatcher(repo)
+    for standin in dirstate_walk(repo.dirstate, matcher):
+        lfile = splitstandin(standin)
+        standins.append((lfile, readstandin(repo, lfile)))
+    return standins
--- a/hgext/largefiles/overrides.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/largefiles/overrides.py	Sun Mar 04 17:14:30 2012 -0600
@@ -265,13 +265,10 @@
 # The overridden function filters the unknown files by removing any
 # largefiles. This makes the merge proceed and we can then handle this
 # case further in the overridden manifestmerge function below.
-def override_checkunknown(origfn, wctx, mctx, folding):
-    origunknown = wctx.unknown()
-    wctx._unknown = filter(lambda f: lfutil.standin(f) not in wctx, origunknown)
-    try:
-        return origfn(wctx, mctx, folding)
-    finally:
-        wctx._unknown = origunknown
+def override_checkunknownfile(origfn, repo, wctx, mctx, f):
+    if lfutil.standin(f) in wctx:
+        return False
+    return origfn(repo, wctx, mctx, f)
 
 # The manifest merge handles conflicts on the manifest level. We want
 # to handle changes in largefile-ness of files at this level too.
@@ -322,7 +319,7 @@
                 processed.append((standin, "g", p2.flags(standin)))
             else:
                 processed.append((standin, "r"))
-        elif m == "m" and lfutil.standin(f) in p1 and f in p2:
+        elif m == "g" and lfutil.standin(f) in p1 and f in p2:
             # Case 2: largefile in the working copy, normal file in
             # the second parent
             standin = lfutil.standin(f)
@@ -454,7 +451,7 @@
                 m._fmap = set(m._files)
                 orig_matchfn = m.matchfn
                 m.matchfn = lambda f: (lfutil.isstandin(f) and
-                                    lfile(lfutil.splitstandin(f)) and
+                                    (f in manifest) and
                                     orig_matchfn(lfutil.splitstandin(f)) or
                                     None)
                 return m
@@ -546,7 +543,7 @@
                 match = oldmatch(ctx, pats, opts, globbed, default)
                 m = copy.copy(match)
                 def tostandin(f):
-                    if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
+                    if lfutil.standin(f) in ctx:
                         return lfutil.standin(f)
                     elif lfutil.standin(f) in repo[None]:
                         return None
@@ -605,8 +602,20 @@
         wlock.release()
 
 def hg_update(orig, repo, node):
+    # In order to not waste a lot of extra time during the update largefiles
+    # step, we keep track of the state of the standins before and after we
+    # call the original update function, and only update the standins that
+    # have changed in the hg.update() call
+    oldstandins = lfutil.getstandinsstate(repo)
     result = orig(repo, node)
-    lfcommands.updatelfiles(repo.ui, repo)
+    newstandins = lfutil.getstandinsstate(repo)
+    tobeupdated = set(oldstandins).symmetric_difference(set(newstandins))
+    filelist = []
+    for f in tobeupdated:
+        if f[0] not in filelist:
+            filelist.append(f[0])
+
+    lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
     return result
 
 def hg_clean(orig, repo, node, show_stats=True):
--- a/hgext/largefiles/uisetup.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/largefiles/uisetup.py	Sun Mar 04 17:14:30 2012 -0600
@@ -64,8 +64,8 @@
                                    overrides.override_update)
     entry = extensions.wrapcommand(commands.table, 'pull',
                                    overrides.override_pull)
-    entry = extensions.wrapfunction(merge, '_checkunknown',
-                                    overrides.override_checkunknown)
+    entry = extensions.wrapfunction(merge, '_checkunknownfile',
+                                    overrides.override_checkunknownfile)
     entry = extensions.wrapfunction(merge, 'manifestmerge',
                                     overrides.override_manifestmerge)
     entry = extensions.wrapfunction(filemerge, 'filemerge',
--- a/hgext/mq.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/hgext/mq.py	Sun Mar 04 17:14:30 2012 -0600
@@ -257,10 +257,11 @@
                 ci += 1
             del self.comments[ci]
 
-def secretcommit(repo, phase, *args, **kwargs):
-    """helper dedicated to ensure a commit are secret
-
-    It should be used instead of repo.commit inside the mq source
+def newcommit(repo, phase, *args, **kwargs):
+    """helper dedicated to ensure a commit respect mq.secret setting
+
+    It should be used instead of repo.commit inside the mq source for operation
+    creating new changeset.
     """
     if phase is None:
         if repo.ui.configbool('mq', 'secret', False):
@@ -581,7 +582,7 @@
         ret = hg.merge(repo, rev)
         if ret:
             raise util.Abort(_("update returned %d") % ret)
-        n = secretcommit(repo, None, ctx.description(), ctx.user(), force=True)
+        n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
         if n is None:
             raise util.Abort(_("repo commit failed"))
         try:
@@ -621,7 +622,7 @@
             # the first patch in the queue is never a merge patch
             #
             pname = ".hg.patches.merge.marker"
-            n = secretcommit(repo, None, '[mq]: merge marker', force=True)
+            n = newcommit(repo, None, '[mq]: merge marker', force=True)
             self.removeundo(repo)
             self.applied.append(statusentry(n, pname))
             self.applieddirty = True
@@ -752,8 +753,8 @@
 
             match = scmutil.matchfiles(repo, files or [])
             oldtip = repo['tip']
-            n = secretcommit(repo, None, message, ph.user, ph.date, match=match,
-                             force=True)
+            n = newcommit(repo, None, message, ph.user, ph.date, match=match,
+                          force=True)
             if repo['tip'] == oldtip:
                 raise util.Abort(_("qpush exactly duplicates child changeset"))
             if n is None:
@@ -993,8 +994,8 @@
                 if util.safehasattr(msg, '__call__'):
                     msg = msg()
                 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
-                n = secretcommit(repo, None, commitmsg, user, date, match=match,
-                                 force=True)
+                n = newcommit(repo, None, commitmsg, user, date, match=match,
+                              force=True)
                 if n is None:
                     raise util.Abort(_("repo commit failed"))
                 try:
@@ -1548,8 +1549,8 @@
 
                 # Ensure we create a new changeset in the same phase than
                 # the old one.
-                n = secretcommit(repo, oldphase, message, user, ph.date,
-                                 match=match, force=True)
+                n = newcommit(repo, oldphase, message, user, ph.date,
+                              match=match, force=True)
                 # only write patch after a successful commit
                 patchf.close()
                 self.applied.append(statusentry(n, patchfn))
@@ -1997,7 +1998,7 @@
           ('P', 'push', None, _('qpush after importing'))],
          _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
 def qimport(ui, repo, *filename, **opts):
-    """import a patch
+    """import a patch or existing changeset
 
     The patch is inserted into the series after the last applied
     patch. If no patches have been applied, qimport prepends the patch
--- a/i18n/de.po	Sun Mar 04 17:12:12 2012 -0600
+++ b/i18n/de.po	Sun Mar 04 17:14:30 2012 -0600
@@ -43,7 +43,7 @@
 #
 # Schlecht:
 #   Mergen Sie zunächst die Branche.
-#   Falls der Tag im Filelog zu finden ist...
+#   Falls der Tag im Filelog nicht gecheckt werden kann...
 #
 # Übersetzungen:
 #  backup        Sicherheitskopie (gewinnt in google-fight gegen Sicherungsk.)
@@ -54,9 +54,12 @@
 #  changeset     Änderungssatz
 #  commit        Commit/Übernahme
 #  commit (v)    übernehmen
+#  comm. message Versionsmeldung
 #  committer     Autor
+#  corrupt       beschädigt (nicht korrumpiert)
 #  deprecated    veraltet
 #  force (v)     erzwingen
+#  history       Historie
 #  merge         zusammenführen
 #  notation      Schreibweise
 #  queue         Reihe
@@ -64,24 +67,25 @@
 #  manage/track  versionieren
 #  pull          abrufen
 #  push          übertragen
-#  rebase        Pfropfung/Verschiebung
+#  rebase        Verschiebung (Pfropfung)
 #  section       Abschnitt (nicht Sektion)
 #  tag           Etikett    (svn verwendet "Marke")
 #
 # Nicht übersetzt bleiben
-#  hook     mangels guter Übersetzung (Einhängeaktion?)
-#  token    Fachbegriff auch im Deutschen
-#  parser   Fachbegriff auch im Deutschen
-#  patch    Bezieht sich auf ein sehr altes Programm mit demselben Namen und
+#  Hook  (m) mangels guter Übersetzung (Einhängeaktion?)
+#  Token (n) Fachbegriff auch im Deutschen
+#  Parser(m) Fachbegriff auch im Deutschen
+#  Patch (m) Bezieht sich auf ein sehr altes Programm mit demselben Namen und
 #           Dateien in dessen Syntax/Format
 #           Die ursprüngliche Bedeutung des englischen Wortes (Flicken) trifft
 #           heute eh nicht mehr zu.
-#  diff     Siehe patch, ausserdem Abkürzung eines auch deutschen Worts
-#  glob
+#  Diff  (n) Siehe patch, ausserdem Abkürzung eines auch deutschen Worts
+#  glob  (n)
 #  dirstate Nur für Entwickler interessant. Im sonstigen Handbuch umschreiben!
 #  .. note:: Dies ist spezielle reStructured-Syntax und darf nicht geändert
 #           werden
-# 
+#  Pager (m) mangels guter Übersetzung (Theoretisch "Verseitener" :)
+#
 # Weiteres:
 #  graft/transplant: Da graft eine Reimplementierung von transplant als
 #         Kernfunktion ist, kann in beiden Fällen "Transplantation" als
@@ -92,12 +96,14 @@
 #         kann die Übersetzung "Revision" lauten.
 #  largefile: Binärriese (neues Wort, vielleicht fällt jemandem ein besseres
 #             ein. Dies ist zur Zeit konsequent verwendet)
+#  default: Sollte dies nicht lieber eine Voreinstellung statt des Standards
+#           sein?
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Mercurial\n"
 "Report-Msgid-Bugs-To: <mercurial-devel@selenic.com>\n"
-"POT-Creation-Date: 2011-12-04 13:42+0200\n"
+"POT-Creation-Date: 2012-01-21 14:20+0200\n"
 "PO-Revision-Date: 2010-12-24 15:09+0100\n"
 "Last-Translator: Fabian Kreutz <fabian.kreutz@qvantel.com>\n"
 "Language-Team: \n"
@@ -470,8 +476,7 @@
 
 #, python-format
 msgid "acl: user \"%s\" denied on \"%s\" (changeset \"%s\")"
-msgstr ""
-"acl: Zugriff für \"%s\" auf \"%s\" abgelehnt (Änderungssatz \"%s\")"
+msgstr "acl: Zugriff für \"%s\" auf \"%s\" abgelehnt (Änderungssatz \"%s\")"
 
 #, python-format
 msgid "acl: user \"%s\" not allowed on \"%s\" (changeset \"%s\")"
@@ -529,7 +534,8 @@
 msgstr ""
 "Direktes Schreiben in die Datenbank führt leicht zu Problemen bei Schema-\n"
 "veränderungen. Ausserdem wird ein externes Bugzilla Script benötigt, um\n"
-"Benachrichtigungsemails zu versenden. Dieses Script wird mit den Rechten des\n"
+"Benachrichtigungsemails zu versenden. Dieses Script wird mit den Rechten "
+"des\n"
 "Mercurial-Benutzers ausgeführt, benötigt eine lokale Bugzilla-Installation\n"
 "sowie Leserechte an der Bugzilla Konfigurationsdatei und MySQL Benutzername\n"
 "und Passwort, um vollen Zugriff auf die Bugzilla Datenbank zu haben. Aus\n"
@@ -650,7 +656,8 @@
 msgstr ""
 "bugzilla.strip\n"
 "  Die Anzahl von Verzeichnisebenen, die vom Anfang des Archivpfads\n"
-"  (``{root}`` in Vorlagen) abgeschnitten wird, um ``{webroot}`` zu erhalten.\n"
+"  (``{root}`` in Vorlagen) abgeschnitten wird, um ``{webroot}`` zu "
+"erhalten.\n"
 "  Zum Beispiel mit ``{root}`` als ``/var/local/mein-projekt`` und einem\n"
 "  Wert 2, wird ``{webroot}`` auf ``mein-projekt`` gesetzt. Standard 0."
 
@@ -721,8 +728,7 @@
 msgid ""
 "XMLRPC+email access mode uses the XMLRPC access mode configuration items,\n"
 "and also:"
-msgstr ""
-"XMLRPC+email nutzt die gleichen Zugriffskonfiguration wie XMLRPC plus:"
+msgstr "XMLRPC+email nutzt die gleichen Zugriffskonfiguration wie XMLRPC plus:"
 
 msgid ""
 "bugzilla.bzemail\n"
@@ -928,7 +934,8 @@
 "with a collection of Mercurial repositories in ``/var/local/hg/repos/``,\n"
 "with a web interface at ``http://my-project.org/hg``. ::"
 msgstr ""
-"MySQL Beispielkonfiguration. Bugzilla 3.2 ist lokal in ``/opt/bugzilla-3.2``\n"
+"MySQL Beispielkonfiguration. Bugzilla 3.2 ist lokal in ``/opt/"
+"bugzilla-3.2``\n"
 "installiert. Die MySQL Datenbank wird per ``localhost`` angesprochen,\n"
 "der Name der Bugzilla Datenbank ist ``bugs`` und der MySQL Zugriff ist für\n"
 "enutzer ``bugs`` mit Password ``XYZZY`` erlaubt. Die Archive, deren\n"
@@ -1341,7 +1348,8 @@
 "(fettgeschrieben), 'dim' (gedämmt), 'inverse' (Vorder- und Hintergrund\n"
 "getauscht), 'italic' (Schrägschrift), 'standout' (hervorstehend) und\n"
 "'underline' (unterstrichen); im ECMA-48 Modus nur 'bold', 'inverse',\n"
-"'italic' und 'underline'. Wie dies tatsächlich aussieht, hängt vom Terminal-\n"
+"'italic' und 'underline'. Wie dies tatsächlich aussieht, hängt vom "
+"Terminal-\n"
 "emulator ab. Effekte, die nicht realisiert werden können, werden ohne\n"
 "Warnung ignoriert."
 
@@ -1415,13 +1423,11 @@
 "deaktivieren.\n"
 
 msgid "no terminfo entry for setab/setaf: reverting to ECMA-48 color\n"
-msgstr ""
-"Kein terminfo-Eintrag für setab/setaf: Falle auf ECMA-48 zurück\n"
+msgstr "Kein terminfo-Eintrag für setab/setaf: Falle auf ECMA-48 zurück\n"
 
 #, python-format
 msgid "warning: failed to set color mode to %s\n"
-msgstr ""
-"Warnung: Konnte Farbmodus nicht auf %s setzen\n"
+msgstr "Warnung: Konnte Farbmodus nicht auf %s setzen\n"
 
 #, python-format
 msgid "ignoring unknown color/effect %r (configured in color.%s)\n"
@@ -1569,8 +1575,7 @@
 msgstr "      Quellautor = Zielautor"
 
 msgid "    Empty lines and lines starting with a ``#`` are ignored."
-msgstr ""
-"    Leere Zeilen und Zeilen, die mit ``#`` beginnen, werden ignoriert."
+msgstr "    Leere Zeilen und Zeilen, die mit ``#`` beginnen, werden ignoriert."
 
 msgid ""
 "    The filemap is a file that allows filtering and remapping of files\n"
@@ -2476,7 +2481,7 @@
 msgstr ""
 
 msgid "failed to reach end of mtn automate stdio headers"
-msgstr ""
+msgstr "Konnte den Endpunkt der mtn automate-Schnittstelle nicht erreichen"
 
 #, python-format
 msgid "%s does not look like a P4 repository"
@@ -2493,6 +2498,10 @@
 "Mercurial konnte sich selbst nicht ausführen, prüfe, ob die Programmdatei\n"
 "in PATH enthalten ist."
 
+#, python-format
+msgid "log stream exception '%s'"
+msgstr ""
+
 msgid ""
 "svn: cannot probe remote repository, assume it could be a subversion "
 "repository. Use --source-type if you know better.\n"
@@ -3331,10 +3340,10 @@
 "  project = foo\n"
 "  # das Modul (Unterprojekt) (optional)\n"
 "  #module = foo\n"
-"  # Hänge eine Statistik über die Änderungen an die Commit-Nachricht an\n"
+"  # Hänge eine Statistik über Änderungen an die Versionsmeldung an\n"
 "  # (optional)\n"
 "  #diffstat = False\n"
-"  # Vorlage für die Commit-Nachrichten (optional)\n"
+"  # Vorlage für die Versionsmeldungen (optional)\n"
 "  #template = {desc}\\n{baseurl}{webroot}/rev/{node}-- {diffstat}\n"
 "  # zu verwendender Stil (optional)\n"
 "  #style = foo\n"
@@ -4160,9 +4169,10 @@
 "enabled for this to work."
 msgstr ""
 "Wird ein Änderungssatz, welcher einen Binärriesen ändert oder hinzufügt,\n"
-"in ein entferntes Archiv übertragen, so werden auch die Revisionen der\n"
-"Binärdatei übertragen. Das entfernte Mercurial muss hierfür die\n"
-"largefiles-Erweiterung unterstützen."
+"in ein entferntes Archiv übertragen, so werden gleichzeitig (asymmetrisch\n"
+"zum Abrufen per pull) auch die (neuen) Revisionen der Binärdatei "
+"übertragen.\n"
+"Das entfernte Mercurial muss hierfür die largefiles-Erweiterung unterstützen."
 
 msgid ""
 "When you pull a changeset that affects largefiles from a remote\n"
@@ -4242,10 +4252,18 @@
 
 msgid ""
 "Files that match one of these patterns will be added as largefiles\n"
-"regardless of their size.\n"
+"regardless of their size."
 msgstr ""
 "Dateien, die auf ein solches Muster passen, werden unabhängig von ihrer\n"
-"Größe als Binärriesen hinzugefügt.\n"
+"Größe als Binärriesen hinzugefügt."
+
+msgid ""
+"The ``largefiles.minsize`` and ``largefiles.patterns`` config options\n"
+"will be ignored for any repositories not already containing a\n"
+"largefile. To add the first largefile to a repository, you must\n"
+"explicitly do so with the --large flag passed to the :hg:`add`\n"
+"command.\n"
+msgstr ""
 
 msgid "convert a normal repository to a largefiles repository"
 msgstr "Konvertiert ein normales Archiv in ein Archiv mit Binärriesen"
@@ -4335,6 +4353,18 @@
 msgid "largefile %s becomes symlink"
 msgstr "Binärriese %s wird ein symbolischer Verweis"
 
+#, python-format
+msgid "skipping incorrectly formatted tag %s\n"
+msgstr "Überspringe fehlerhaft formatiertes Etikett: %s\n"
+
+#, python-format
+msgid "skipping incorrectly formatted id %s\n"
+msgstr "Überspringe fehlerhaft formatierte ID: %s\n"
+
+#, python-format
+msgid "no mapping for id %s\n"
+msgstr "Keine Abbildung für ID %s\n"
+
 msgid "uploading largefiles"
 msgstr "Lade Binärriesen hoch"
 
@@ -4378,10 +4408,6 @@
 msgid "Found %s in system cache\n"
 msgstr "%s im Zwischenspeicher gefunden\n"
 
-#, python-format
-msgid "bad hash in '%s' (only %d bytes long)"
-msgstr "Prüfsummenfehler in '%s' (nur %d Bytes lang)"
-
 msgid "Can't get file locally"
 msgstr "Kann Datei nicht lokal abrufen"
 
@@ -4417,8 +4443,8 @@
 msgstr "Keine Dateien angegeben"
 
 #, python-format
-msgid "not removing %s: %s (use -f to force removal)\n"
-msgstr "Entferne nicht %s: %s (Nutze -f um Entfernung zu erzwingen)\n"
+msgid "not removing %s: %s (use forget to undo)\n"
+msgstr "Entferne nicht %s: %s (Nutze ``forget`` um rückgängig zu machen)\n"
 
 msgid "file still exists"
 msgstr "Datei existiert noch"
@@ -4436,6 +4462,28 @@
 msgid "uncommitted local changes"
 msgstr "Ausstehende nicht versionierte Änderungen"
 
+msgid "&Largefile"
+msgstr "&Binärriese"
+
+msgid "&Normal file"
+msgstr "&Normale Datei"
+
+#, python-format
+msgid ""
+"%s has been turned into a largefile\n"
+"use (l)argefile or keep as (n)ormal file?"
+msgstr ""
+"%s wurde in einen Binärriesen umgewandelt.\n"
+"Nutze als (B)inärriese oder als (n)ormale Datei?"
+
+#, python-format
+msgid ""
+"%s has been turned into a normal file\n"
+"keep as (l)argefile or use (n)ormal file?"
+msgstr ""
+"%s wurde in eine normale Datei umgewandelt.\n"
+"Nutze als (B)inärriese oder als (n)ormale Datei?"
+
 #, python-format
 msgid "merging %s and %s to %s\n"
 msgstr "Führe %s und %s zusammen zu %s\n"
@@ -4464,6 +4512,13 @@
 msgid "no files to copy"
 msgstr "Keine Dateien zu kopieren"
 
+msgid "caching new largefiles\n"
+msgstr "Lade neue Binärriesen in den Zwischenspeicher\n"
+
+#, python-format
+msgid "%d largefiles cached\n"
+msgstr "%d Binärriesen in den Zwischenspeicher geladen\n"
+
 #, python-format
 msgid "unknown archive type '%s'"
 msgstr "Unbekannter Archivtyp '%s'"
@@ -4472,6 +4527,10 @@
 msgstr "Bei Archivierung in Dateien kann kein Präfix angegeben werden"
 
 #, python-format
+msgid "largefile %s not found in repo store or system cache"
+msgstr "Binärriese %s weder im Archivlager noch Zwischenspeicher gefunden"
+
+#, python-format
 msgid "not removing %s: file is already untracked\n"
 msgstr "Entferne %s nicht: Datei ist nicht versioniert\n"
 
@@ -4485,18 +4544,24 @@
 msgid "largefiles: %d to upload\n"
 msgstr "largefiles: %d hochzuladen\n"
 
-msgid "addremove cannot be run on a repo with largefiles"
-msgstr ""
-"'addremove' kann nicht auf ein Archiv mit Binärriesen angewendet werden"
-
-#, python-format
-msgid "largefiles: failed to put %s (%s) into store: %s"
-msgstr "largefiles: Konnte %s (%s) nicht in einlagern: %s"
+msgid "largefile contents do not match hash"
+msgstr "Inhalt des Binärriesen passt nicht auf Prüfsumme"
+
+#, python-format
+msgid "largefiles: failed to put %s into store: %s"
+msgstr "largefiles: Konnte %s nicht in einlagern: %s"
 
 #, python-format
 msgid "requested largefile %s not present in cache"
 msgstr "Angeforderer Binärriese %s ist nicht im Zwischenspeicher"
 
+msgid "remote: "
+msgstr "Entfernt: "
+
+#, python-format
+msgid "unexpected putlfile response: %s"
+msgstr "Unerwartete Antwort von putlfile: %s"
+
 msgid "putlfile failed:"
 msgstr "putlfile fehlgeschlagen:"
 
@@ -4539,7 +4604,8 @@
 "largefiles: repo method %r appears to have already been wrapped by another "
 "extension: largefiles may behave incorrectly\n"
 msgstr ""
-"largefiles: Aktion %r scheint bereits von einer anderen Erweiterung verändert zu sein. Dadurch kann es zu Fehlern in largefiles kommen\n"
+"largefiles: Aktion %r scheint bereits von einer anderen Erweiterung "
+"verändert zu sein. Dadurch kann es zu Fehlern in largefiles kommen\n"
 
 #, python-format
 msgid "file \"%s\" is a largefile standin"
@@ -5011,6 +5077,13 @@
 msgstr "Nachfahren der Revision %d werden nicht verwaltet"
 
 #, python-format
+msgid "revision %d is not mutable"
+msgstr "Revision %d ist unveränderbar"
+
+msgid "see \"hg help phases\" for details"
+msgstr "siehe \"hg help phases\" für Details"
+
+#, python-format
 msgid "cannot import merge revision %d"
 msgstr "Kann Zusammenführung %d nicht importieren"
 
@@ -5030,7 +5103,8 @@
 msgstr "Benenne %s in %s um\n"
 
 msgid "need --name to import a patch from -"
-msgstr "Beim Import von der Standardeingabe muss die Option --name angegeben werden"
+msgstr ""
+"Beim Import von der Standardeingabe muss die Option --name angegeben werden"
 
 #, python-format
 msgid "unable to read file %s"
@@ -5054,11 +5128,13 @@
 
 msgid ""
 "    The patches must not be applied, and at least one patch is required. "
-"With\n"
-"    -k/--keep, the patch files are preserved in the patch directory."
+"Exact\n"
+"    patch identifiers must be given. With -k/--keep, the patch files are\n"
+"    preserved in the patch directory."
 msgstr ""
 "    Die Patches dürfen nicht angewendet sein und mindestens einer muss\n"
-"    angegeben sein. Mit -k/--keep werden die Patchdateien erhalten."
+"    angegeben sein. Exakte Patch-IDs müssen verwendet werden. Mit -k/--keep\n"
+"    werden die Patchdateien erhalten."
 
 msgid ""
 "    To stop managing a patch and move it into permanent history,\n"
@@ -5896,12 +5972,12 @@
 "    Beispiel::"
 
 msgid ""
-"        qguard foo.patch -stable    (negative guard)\n"
-"        qguard bar.patch +stable    (positive guard)\n"
+"        qguard foo.patch -- -stable    (negative guard)\n"
+"        qguard bar.patch    +stable    (positive guard)\n"
 "        qselect stable"
 msgstr ""
-"        qguard foo.patch -stable    (negativer Wächter)\n"
-"        qguard bar.patch +stable    (positiver Wächter)\n"
+"        qguard foo.patch -- -stable    (negativer Wächter)\n"
+"        qguard bar.patch    +stable    (positiver Wächter)\n"
 "        qselect stable"
 
 msgid ""
@@ -6005,7 +6081,7 @@
 msgstr ""
 "    Schließt die angegebenen Revisionen ab (entspricht den angewandten\n"
 "    Patches), indem sie aus der Kontrolle von mq entfernt und in die\n"
-"    reguläre Projektgeschichte übernommen werden."
+"    reguläre Projekthistorie übernommen werden."
 
 msgid ""
 "    Accepts a revision range or the -a/--applied option. If --applied\n"
@@ -6025,8 +6101,7 @@
 msgstr ""
 "    Dies kann insbes. nützlich sein, wenn Ihre Änderungen in einem\n"
 "    vorgelagerten Projektarchiv angewandt wurden, oder wenn Sie Ihre\n"
-"    Änderungen in ein vorgelagertes Archiv übertragen wollen.\n"
-"    "
+"    Änderungen in ein vorgelagertes Archiv übertragen wollen."
 
 msgid "no revisions specified"
 msgstr "Keine Revisionen angegeben"
@@ -6062,9 +6137,8 @@
 "    Supports switching between different patch queues, as well as creating\n"
 "    new patch queues and deleting existing ones."
 msgstr ""
-"    Unterstützt das Wechseln zwischen verschiedener Patch-Reihen, ebenso "
-"wie\n"
-"    das erstellen neuer Reihen und das Löschen bereits bestehender."
+"    Unterstützt das Wechseln zwischen verschiedener Patch-Reihen, sowie\n"
+"    das Erstellen neuer Reihen und das Löschen bereits bestehender."
 
 msgid ""
 "    Omitting a queue name or specifying -l/--list will show you the "
@@ -6130,7 +6204,7 @@
 
 #, python-format
 msgid "non-queue directory \"%s\" already exists"
-msgstr "Nicht-Reihen-Verzeichnis \"%s\" existiert bereits"
+msgstr "Verzeichnis \"%s\" existiert bereits, ist aber keine Reihe"
 
 msgid "use --create to create a new queue"
 msgstr "Verwenden Sie --create, um eine neue Reihe zu erzeugen"
@@ -6192,6 +6266,9 @@
 "This extension let you run hooks sending email notifications when\n"
 "changesets are being pushed, from the sending or receiving side."
 msgstr ""
+"Diese Erweiterung ermöglicht das Senden von Benachrichtigungsemails,\n"
+"wannimmer Änderungssätze übertragen werden. Dies kann von der über-\n"
+"tragenden oder der empfangenden Seite aus geschehen."
 
 msgid ""
 "First, enable the extension as explained in :hg:`help extensions`, and\n"
@@ -6199,6 +6276,10 @@
 "are run by the changesets receiver while the ``outgoing`` one is for\n"
 "the sender::"
 msgstr ""
+"Zunächst muss die Erweiterung (wie in :hg:`help extensions` beschrieben)\n"
+"aktiviert werden und dann als Hook registriert werden. Für den Empfänger\n"
+"sind dies die ``incoming`` und ``outgoing`` Hooks, für den Sender der\n"
+"``outgoing`` Hook::"
 
 msgid ""
 "  [hooks]\n"
@@ -6217,6 +6298,8 @@
 "  # one email for all outgoing changesets\n"
 "  outgoing.notify = python:hgext.notify.hook"
 msgstr ""
+"  # eine E-Mail für jeden übertragenen Änderungssatz\n"
+"  outgoing.notify = python:hgext.notify.hook"
 
 msgid ""
 "Now the hooks are running, subscribers must be assigned to\n"
@@ -6224,6 +6307,9 @@
 "given email or the ``[reposubs]`` section to map emails to a single\n"
 "repository::"
 msgstr ""
+"Schliesslich müssen noch die Abonnements für die Projektarchive definiert\n"
+"werden. Im Abschnitt ``[usersubs]`` kann man mehrere Archive einer Email\n"
+"zuweisen, in ``[reposubs]`` umgekehrt mehrere Emails für ein Archiv angeben."
 
 msgid ""
 "  [usersubs]\n"
@@ -6252,6 +6338,9 @@
 "root. The subscriptions can be defined in their own file and\n"
 "referenced with::"
 msgstr ""
+"Die glob-Muster müssen auf den absoluten Pfad zum Archiv passen. Alle\n"
+"Abonnements können in einer eigenen Datei gesammelt werden und folgender-\n"
+"maßen in der Konfiguration eingebunden werden::"
 
 msgid ""
 "  [notify]\n"
@@ -6264,23 +6353,33 @@
 "Alternatively, they can be added to Mercurial configuration files by\n"
 "setting the previous entry to an empty value."
 msgstr ""
+"Alternativ (mit leerem Wert für ``notify.config``) können die Abonnements\n"
+"in der Mercurial Konfigurationsdatei angegeben werden."
 
 msgid ""
 "At this point, notifications should be generated but will not be sent until "
 "you\n"
 "set the ``notify.test`` entry to ``False``."
 msgstr ""
+"Nach dieser Konfiguration werden die Benachrichtigungen nun generiert, aber\n"
+"noch nicht gesendet, bis der Wert von ``notify.test`` auf ``False`` gesetzt\n"
+"wird."
 
 msgid ""
 "Notifications content can be tweaked with the following configuration "
 "entries:"
 msgstr ""
+"Der Inhalt der Benachrichtigungen kann mit der folgenden Konfiguration\n"
+"angepasst werden:"
 
 msgid ""
 "notify.test\n"
 "  If ``True``, print messages to stdout instead of sending them. Default: "
 "True."
 msgstr ""
+"notify.test\n"
+"  Falls ``True`` werden die Nachrichten auf die Standardausgabe und nicht\n"
+"  als Email versendet. Standard: True."
 
 msgid ""
 "notify.sources\n"
@@ -6292,6 +6391,14 @@
 "  locally. Outgoing sources are the same except for ``unbundle`` which\n"
 "  is replaced by ``bundle``. Default: serve."
 msgstr ""
+"notify.sources\n"
+"  Kommaseparierte Liste von Quellaktionen. Benachrichtigungen werden nur\n"
+"  gesendet, wenn die Änderungen von einer solchen Aktion ausgelöst wurden.\n"
+"  Quellen für ankommende Änderungen sind ``serve`` (Änderungen via http\n"
+"  oder ssh), ``pull`` (aktiv abgerufen), ``unbundle`` (per :hg:`unbundle`\n"
+"  eingefügt) oder ``push`` (lokal übertragen). Für ausgehende Änderungen\n"
+"  gibt es die gleichen, nur mit ``unbundle`` gegen ``bundle`` getauscht.\n"
+"  Standard: serve."
 
 msgid ""
 "notify.strip\n"
@@ -6303,12 +6410,21 @@
 "change\n"
 "  ``/long/path/repository`` into ``repository``. Default: 0."
 msgstr ""
+"notify.strip\n"
+"  Anzahl der Schrägstriche, die vom URL-Pfad abgeschnitten werden sollen.\n"
+"  Standardmäßig werden Archive mit ihrem absoluten Pfad benannt.\n"
+"  Mit ``notify.strip`` lässt sich das in relative Pfade abwandeln. Zum\n"
+"  Beispiel wird ``/langer/pfad/zum/archiv`` mit ``strip=4`` zu ``archiv``.\n"
+"  Standard: 0."
 
 msgid ""
 "notify.domain\n"
 "  If subscribers emails or the from email have no domain set, complete them\n"
 "  with this value."
 msgstr ""
+"notify.domain\n"
+"  Falls eine Abonnementen- oder die Sender-Adresse keine Domäne haben,\n"
+"  wird dieser Wert eingefügt."
 
 msgid ""
 "notify.style\n"
@@ -6328,62 +6444,103 @@
 "notify.incoming\n"
 "  Template to use when run as incoming hook, override ``notify.template``."
 msgstr ""
+"notify.incoming\n"
+"  Vorlage (mit höherer Priorität als ``notify.template``), falls die Aktion\n"
+"  durch einen 'incoming'-Hook ausgelöst wurde."
 
 msgid ""
 "notify.outgoing\n"
 "  Template to use when run as outgoing hook, override ``notify.template``."
 msgstr ""
+"notify.outcoming\n"
+"  Vorlage (mit höherer Priorität als ``notify.template``), falls die Aktion\n"
+"  durch einen 'outcoming'-Hook ausgelöst wurde."
 
 msgid ""
 "notify.changegroup\n"
 "  Template to use when running as changegroup hook, override\n"
 "  ``notify.template``."
 msgstr ""
+"notify.changegroup\n"
+"  Vorlage (mit höherer Priorität als ``notify.template``), falls die Aktion\n"
+"  durch einen 'changegroup'-Hook ausgelöst wurde."
 
 msgid ""
 "notify.maxdiff\n"
 "  Maximum number of diff lines to include in notification email. Set to 0\n"
 "  to disable the diff, -1 to include all of it. Default: 300."
 msgstr ""
+"notify.maxdiff\n"
+"  Maximale Zeilenanzahl des Diffs in der Benachrichtigungsemail. Der Wert\n"
+"  0 unterbindet die Anzeige des Diffs, -1 wird das gesamte Diff ausgeben.\n"
+"  Standard: 300."
 
 msgid ""
 "notify.maxsubject\n"
 "  Maximum number of characters in emails subject line. Default: 67."
 msgstr ""
+"notify.maxsubject\n"
+"  Maximale Länge der Betreffszeile. Standard: 67."
 
 msgid ""
 "notify.diffstat\n"
 "  Set to True to include a diffstat before diff content. Default: True."
 msgstr ""
+"notify.diffstat\n"
+"  Zeige eine Diff-Statistik vor dem eigentlich Diff an. Standard: True."
 
 msgid ""
 "notify.merge\n"
 "  If True, send notifications for merge changesets. Default: True."
 msgstr ""
+"notify.merge\n"
+"  Sende Nachrichten auch für Zusammenführungen. Standard: True."
 
 msgid ""
 "notify.mbox\n"
 "  If set, append mails to this mbox file instead of sending. Default: None."
 msgstr ""
 "notify.mbox\n"
-"  Schreibe Nachrichten in mbox Datei, anstatt sie zu versenden. Standard: None"
+"  Schreibe Nachrichten in mbox Datei, anstatt sie zu versenden. Standard: "
+"None"
+
+msgid ""
+"notify.fromauthor\n"
+"  If set, use the first committer of the changegroup for the \"From\" field "
+"of\n"
+"  the notification mail. If not set, take the user from the pushing repo.\n"
+"  Default: False."
+msgstr ""
+"notify.fromauthor\n"
+"  Verwende den Autoren der ersten Änderungsgruppe als Absender der\n"
+"  Benachrichtigungsemail. Falls nicht gesetzt, verwende den Nutzer im\n"
+"  übertragenden Archiv. Standard: False."
+
 
 msgid ""
 "If set, the following entries will also be used to customize the "
 "notifications:"
 msgstr ""
+"Durch Setzen der folgenden Einträge können die Benachrichtigungen weiter\n"
+"angepasst werden:"
 
 msgid ""
 "email.from\n"
 "  Email ``From`` address to use if none can be found in generated email "
 "content."
 msgstr ""
+"email.from\n"
+"  Die zu verwendende Senderadresse (``From``), falls die Vorlage keinen\n"
+"  Wert setzt."
 
 msgid ""
 "web.baseurl\n"
 "  Root repository browsing URL to combine with repository paths when making\n"
 "  references. See also ``notify.strip``."
 msgstr ""
+"web.baseurl\n"
+"  Die Basis-URL für alle Archive, die mit dem abgeschnittenen (siehe\n"
+"  ``notify.strip``) Archivpfad für Referenzen verwendet wird."
 
 #, python-format
 msgid "%s: %d new changesets"
@@ -6487,7 +6644,7 @@
 "Befehle den Pager benutzen."
 
 msgid "If pager.attend is present, pager.ignore will be ignored."
-msgstr "Wenn pager.attend vorhanden ist, wird pager.ignore ignoriert."
+msgstr "Wenn pager.attend vorhanden ist, ist pager.ignore wirkungslos."
 
 msgid ""
 "To ignore global commands like :hg:`version` or :hg:`help`, you have\n"
@@ -6550,7 +6707,7 @@
 "configuration file::"
 msgstr ""
 "Andere Standardwerte können beispielsweise durch den folgenden Abschnitt\n"
-"in der hgrc konfiguriert werden::"
+"in der Konfigurationsdatei gesetzt werden::"
 
 msgid ""
 "  [email]\n"
@@ -6571,8 +6728,8 @@
 "Use ``[patchbomb]`` as configuration section name if you need to\n"
 "override global ``[email]`` address settings."
 msgstr ""
-"Benutzen Sie [patchbomb] als Abschnittsnamen, falls Sie globale\n"
-"[email]-Einstellungen überschreiben müssen."
+"Benutzen Sie ``[patchbomb]`` als Abschnittsnamen, falls Sie globale\n"
+"``[email]``-Einstellungen überschreiben müssen."
 
 msgid ""
 "Then you can use the :hg:`email` command to mail a series of\n"
@@ -6709,9 +6866,9 @@
 "    with a final summary of all messages and asked for confirmation before\n"
 "    the messages are sent."
 msgstr ""
-"Bei der Angabe der Optionen -d/--diffstat oder -c/--confirm wird eine\n"
-"abschließende Zusammenfassung aller Nachrichten angezeigt und um\n"
-"Bestätigung gebeten, bevor die Nachrichten versendet werden."
+"    Bei der Angabe der Optionen -d/--diffstat oder -c/--confirm wird eine\n"
+"    abschließende Zusammenfassung aller Nachrichten angezeigt und um\n"
+"    Bestätigung gebeten, bevor die Nachrichten versendet werden."
 
 msgid ""
 "    By default the patch is included as text in the email body for\n"
@@ -6747,9 +6904,9 @@
 "    previewed with any mail user agent which supports UNIX mbox\n"
 "    files."
 msgstr ""
-"Alternativ werden die Nachrichten mit der Option -m/--mbox in eine Datei\n"
-"geschrieben, die von jedem Emailprogramm, welches das UNIX-mbox-Format\n"
-"unterstützt, geprüft werden, zum Beispiel mit mutt::"
+"    Alternativ werden die Nachrichten mit der Option -m/--mbox in eine\n"
+"    Datei geschrieben, die von jedem Emailprogramm, welches das UNIX-mbox-\n"
+"    Format unterstützt, geprüft werden, zum Beispiel mit mutt::"
 
 msgid ""
 "    With -n/--test, all steps will run, but mail will not be sent.\n"
@@ -6759,13 +6916,13 @@
 "    PAGER environment variable is set, your pager will be fired up once\n"
 "    for each patchbomb message, so you can verify everything is alright."
 msgstr ""
-"Um das Versenden verfrühter Patches zu verhindern, sollte man :hg:`email`\n"
-"mit der Option \"-n\" (Testmodus) aufrufen. Sie werden nach einer\n"
-"Empfängeradresse, einem Betreff und einer einleitenden Nachricht gefragt,\n"
-"die die Patches Ihrer Patchbombe beschreibt. Danach werden die\n"
-"Patchbombennachrichten angezeigt. Wenn die PAGER-Umgebungsvariable gesetzt\n"
-"ist, wird Ihr Pager für jede Patchbombe einzeln aufgerufen, so dass alles\n"
-"überprüft werden kann."
+"    Mit der Option -n/--test werden alle Schritte ausgeführt und das\n"
+"    Ergebnis angezeigt, aber keine Email versendet. Sie werden also nach\n"
+"    einer Empfängeradresse, einem Betreff und einer einleitenden Nachricht,\n"
+"    die die Patches Ihrer Patchbombe beschreibt, gefragt. Dann werden alle\n"
+"    Patchbombennachrichten angezeigt. Wenn die PAGER-Umgebungsvariable\n"
+"    gesetzt ist, wird Ihr Pager für jede Patchbombe einzeln aufgerufen, so\n"
+"    dass alles überprüft werden kann."
 
 msgid ""
 "    In case email sending fails, you will find a backup of your series\n"
@@ -6828,7 +6985,7 @@
 "    hgrc. See the [email] section in hgrc(5) for details.\n"
 "    "
 msgstr ""
-"    Um dieses Kommando zu benutzen muss das Email-Versenden im Abschnitt\n"
+"    Um dieses Kommando zu benutzen muss der Emailversand im Abschnitt\n"
 "    [email] der Konfiguration aktiviert sein. Siehe hgrc(5) für Details.\n"
 "    "
 
@@ -6863,23 +7020,23 @@
 msgstr "Diese Patch-Serie besteht aus %d Patches."
 
 msgid "no recipient addresses provided"
-msgstr ""
+msgstr "Keine Empfängeradresse angegeben"
 
 msgid ""
 "\n"
 "Final summary:"
 msgstr ""
 "\n"
-"Zusammenfassung:"
+"Engültige Zusammenfassung:"
 
 msgid "are you sure you want to send (yn)?"
-msgstr "Sicher, dass Sie jetzt senden möchten (y/n)?"
+msgstr "Sicher, dass Sie jetzt senden möchten (j/n)?"
 
 msgid "&No"
 msgstr "&Nein"
 
 msgid "&Yes"
-msgstr "Ja (&y)"
+msgstr "&Ja"
 
 msgid "patchbomb canceled"
 msgstr "patchbomb abgebrochen"
@@ -6957,36 +7114,36 @@
 #. i18n: format XX seconds as "XXs"
 #, python-format
 msgid "%02ds"
-msgstr ""
+msgstr "%02dsek"
 
 #. i18n: format X minutes and YY seconds as "XmYYs"
 #, python-format
 msgid "%dm%02ds"
-msgstr ""
+msgstr "%d:%02d"
 
 #. i18n: format X hours and YY minutes as "XhYYm"
 #, python-format
 msgid "%dh%02dm"
-msgstr ""
+msgstr "%d:%02d"
 
 #. i18n: format X days and YY hours as "XdYYh"
 #, python-format
 msgid "%dd%02dh"
-msgstr ""
+msgstr "%dd%02dh"
 
 #. i18n: format X weeks and YY days as "XwYYd"
 #, python-format
 msgid "%dw%02dd"
-msgstr ""
+msgstr "%dw%02dd"
 
 #. i18n: format X years and YY weeks as "XyYYw"
 #, python-format
 msgid "%dy%02dw"
-msgstr ""
+msgstr "%dy%02dw"
 
 #, python-format
 msgid "%d %s/sec"
-msgstr ""
+msgstr "%d %s/Sek"
 
 msgid "command to delete untracked files from the working directory"
 msgstr "Löscht nicht versionierte Dateien aus dem Arbeitsverzeichnis"
@@ -7056,7 +7213,7 @@
 "    option.\n"
 "    "
 msgstr ""
-"    Seien Sie mit purge vorsichtig, da Sie Dateien unwiderbringlich\n"
+"    Seien Sie mit purge vorsichtig, da Sie Dateien unwiederbringlich\n"
 "    löschen könnten, die Sie nicht zum Projektarchiv hinzugefügt\n"
 "    haben. Wenn Sie nur die Liste der Dateien sehen wollen, die dieses\n"
 "    Programm entfernen würde, nutzen Sie die Option --print.\n"
@@ -7112,16 +7269,16 @@
 msgstr "Verschiebe auf den gegebenen Änderungssatz"
 
 msgid "collapse the rebased changesets"
-msgstr "Faltet die erzeugten Änderungssätze nach dem Rebase zusammen"
+msgstr "Fügt die verschobenen Änderungssätze zu einem einzelnen zusammen"
 
 msgid "use text as collapse commit message"
-msgstr "Nimm Text als gefaltete Commit-Nachricht"
+msgstr "Setzt die Versionsmeldung des zusammengefügten Änderungssatzes"
 
 msgid "invoke editor on commit messages"
-msgstr "Ruft Editor zum setzen der Versionsmeldung auf"
+msgstr "Ruft Editor zum Setzen der Versionsmeldung auf"
 
 msgid "read collapse commit message from file"
-msgstr "Liest gefaltete Commit-Nachricht aus Datei"
+msgstr "Liest Versionsmeldung für den zusammengefügten Änderungssatz aus Datei"
 
 msgid "keep original changesets"
 msgstr "Behält die ursprünglichen Änderungssätze bei"
@@ -7136,15 +7293,17 @@
 msgstr "Methode für das Zusammenführen"
 
 msgid "continue an interrupted rebase"
-msgstr "Führt einen unterbrochenen Rebase fort"
+msgstr "Führt eine unterbrochene Pfropfung fort"
 
 msgid "abort an interrupted rebase"
-msgstr "Bricht einen unterbrochenen Rebase ab"
+msgstr "Bricht eine unterbrochene Pfropfung ab"
 
 msgid ""
 "hg rebase [-s REV | -b REV] [-d REV] [options]\n"
 "hg rebase {-a|-c}"
 msgstr ""
+"hg rebase [-s REV | -b REV] [-d REV] [Optionen]\n"
+"hg rebase {-a|-c}"
 
 msgid "move changeset (and descendants) to a different branch"
 msgstr ""
@@ -7170,7 +7329,7 @@
 "    Sie sollten keine Änderungssätze umpfropfen, die auch andere bereits\n"
 "    haben, ansonsten zwingen Sie jeden anderen die gleiche rebase-\n"
 "    Operation durchzuführen, um die verschobenen Versionen nicht\n"
-"    doppelt zu haben, wenn sie Ihre Änderungen ziehen."
+"    doppelt zu haben, sobald sie Ihre Änderungen abrufen."
 
 msgid ""
 "    If you don't specify a destination changeset (``-d/--dest``),\n"
@@ -7178,10 +7337,10 @@
 "    destination. (The destination changeset is not modified by\n"
 "    rebasing, but new changesets are added as its descendants.)"
 msgstr ""
-"    Wenn Sie keine Zielversion spezifizieren (``-d/--dest``),\n"
-"    verwendet rebase den head des aktuellen named branch, der am \n"
-"    nächsten an tip ist als Ziel (die Zielversion wird durch rebase\n"
-"    nicht verändert. Sie erhält nur neue changesets als Kinder)."
+"    Wenn Sie keine Zielversion spezifizieren (``-d/--dest``), verwendet\n"
+"    rebase als Ziel den Kopf des aktuellen benannten Zweigs, der\n"
+"    der Projektspitze (tip) am nächsten ist. (Die Zielversion wird durch\n"
+"    die Verschiebung nicht verändert, aber erhält neue Kinder.)"
 
 msgid ""
 "    You can specify which changesets to rebase in two ways: as a\n"
@@ -7196,19 +7355,17 @@
 "    the whole branch. If you specify neither ``-s`` nor ``-b``, rebase\n"
 "    uses the parent of the working directory as the base."
 msgstr ""
-"    Sie können auf zwei Arten angeben, welche Changesets rebased werden\n"
-"    sollen: als \"source\" oder als \"base\" Changesets. Beide sind\n"
-"    Abkürzungen für ein Menge von Changesets, die topologisch\n"
-"    zusammenhängen (die \"source\" Branch). Wenn Sie source angeben\n"
-"    (``-s/--source``), rebase wird dieses Changeset und all seine\n"
-"    Descendants nach dest pfropfen. Wenn Sie base angeben (``-b/--base``),\n"
-"    rebase wird Vorgänger von base auswählen, bis zu aber nicht\n"
-"    einschließlich dem gemeinsamen Vorgänger mit dest. Es ist also\n"
-"    ``-b`` weniger präzise, aber bequemer, als ``-s``: Sie können\n"
-"    jegliches Changeset im Quell-Branch angeben, und rebase wird den\n"
-"    gesamten Branch auswählen. Wenn Sie weder ``-s``noch ``-b`` angeben,\n"
-"    wird rebase den Parent des aktuellen Verzeichnisses als Base\n"
-"    auswählen."
+"    Sie können die zu verschiebenden Versionen auf zwei Arten angeben: Als\n"
+"    \"Quell-\" oder als \"Basisversion\". Mit diesen ist jeweils eine Menge\n"
+"    von topologisch verwandten Revisionen gemeint (der \"Quell\"-zweig).\n"
+"    Wenn Sie eine Quellversion (``-s/--source``) angeben, wird Mercurial\n"
+"    diese Version und alle ihrer Nachfahren verschieben. Geben Sie eine\n"
+"    Basisversion (``-b/--base``) an, so sucht Mercurial den jüngsten\n"
+"    gemeinsamen Vorfahren der Basis und des Ziels und verschiebt den Zweig,\n"
+"    der die Basis enthält, nicht jedoch den gemeinsamen Vorfahren.\n"
+"    Somit ist ``-s`` genauer, aber ``-b`` praktischer: Man gibt irgendeine\n"
+"    Version im zu verschiebenden Zweig an. Wenn Sie weder ``-s`` noch\n"
+"    ``-b`` angeben, wird den Vorfahr der Arbeitskopie als Basis verwendet."
 
 msgid ""
 "    By default, rebase recreates the changesets in the source branch\n"
@@ -7217,12 +7374,11 @@
 "    changesets in the source branch (e.g. merges from the destination\n"
 "    branch) may be dropped if they no longer contribute any change."
 msgstr ""
-"    Per Default erzeugt rebase die Changesets im Quell-Branch als\n"
-"    Descendants von dest neu, und zerstört dann die Originale. Benutzen\n"
-"    Sie ``--keep``, um die originalen Quell-Changesets zu bewahren.\n"
-"    Einige Changeset im Quell-Branch (z.B. Merges vom Ziel-Branch),\n"
-"    können gelöscht werden, wenn sie keine weiteren Änderungen\n"
-"    mehr beisteuern."
+"    Standardmäßig werden die Änderungssätze des Quellzweigs als Nachfahren\n"
+"    des Ziels erzeugt und ihr Original zerstört. Mit ``--keep`` werden\n"
+"    die Originale erhalten. Einige Änderungssätze des Quellzweigs könnten\n"
+"    entfernt werden (z.B. Zusammenführungen aus dem Zielzweig, die keine\n"
+"    eigenen Änderungen über die Zusammenführung hinaus enthalten)."
 
 msgid ""
 "    One result of the rules for selecting the destination changeset\n"
@@ -7232,11 +7388,14 @@
 "    destination (or ``update`` to the other head, if it's the head of\n"
 "    the intended source branch)."
 msgstr ""
-"    Ein Ergebnis der Regeln für das Auswählen der Ziel-Changesets und des\n"
-"    Quell-Branches ist, daß, im Gegensatz zu ``merge``, rebase nichts\n"
-"    tun wird, wenn Sie auf dem neuesten (tipmost) Head eines benannten\n"
-"    Branches mit zwei Heads sind. Sie müssen Quelle und/oder Ziel angeben\n"
-"    (oder auf den anderen Head ``update``en)."
+"    Als Folge der automatischen Auswahlregeln für Quelle und Ziel wird\n"
+"    (im Gegensatz zu einer Zusammenführung) keine Verschiebung "
+"durchgeführt,\n"
+"    wenn der jüngere von zwei Köpfen eines Zweiges aktuell ist. In diesem\n"
+"    Falle (z.B. bei lokaler Versionsübernahme nach einem Abrufen), muss\n"
+"    eine Quelle und/oder ein Ziel explizit angegeben werden. Dieser Fall\n"
+"    tritt aber vor allem ein, wenn das Arbeitsverzeichnis im Zielzweig\n"
+"    liegt, also zunächst auf den Quellzweig aktualisiert werden muss."
 
 msgid ""
 "    If a rebase is interrupted to manually resolve a merge, it can be\n"
@@ -7255,9 +7414,11 @@
 
 msgid "message can only be specified with collapse"
 msgstr ""
+"Eine Versionsmeldung kann nur für beim Zusammenfalten (--collapse) gegeben\n"
+"werden"
 
 msgid "cannot use both abort and continue"
-msgstr "abort und continue können nicht gleichzeitig genutzt werden"
+msgstr "abort und continue dürfen nicht gleichzeitig angegeben werden"
 
 msgid "cannot use collapse with continue or abort"
 msgstr "collapse kann nicht mit continue oder abort genutzt werden"
@@ -7266,19 +7427,19 @@
 msgstr "detach kann nicht mit continue oder abort genutzt werden"
 
 msgid "abort and continue do not allow specifying revisions"
-msgstr "abort und continue erlauben die Angabe einer Revision nicht"
+msgstr "abort und continue erlauben keine Angabe einer Revision"
 
 msgid "tool option will be ignored\n"
-msgstr ""
+msgstr "Die Option tool wird ignoriert\n"
 
 msgid "cannot specify both a source and a base"
-msgstr "Es können nicht Quelle und Basis gleichzeitig angegeben werden"
+msgstr "Quelle und Basis dürfen nicht gleichzeitig angegeben werden"
 
 msgid "cannot specify both a revision and a base"
-msgstr "Es können nicht Revision und Basis gleichzeitig angegeben werden"
+msgstr "Revision und Basis dürfen nicht gleichzeitig angegeben werden"
 
 msgid "cannot specify both a revision and a source"
-msgstr "Es können nicht Revision und Quelle gleichzeitig angegeben werden"
+msgstr "Revision und Quelle dürfen nicht gleichzeitig angegeben werden"
 
 msgid "detach requires a revision to be specified"
 msgstr "detach benötigt eine Revision"
@@ -7292,11 +7453,18 @@
 msgid "use --keep to keep original changesets"
 msgstr "Verwende --keep, um die ursprünglichen Änderungssätze zu behalten"
 
+#, python-format
+msgid "Can't rebase immutable changeset %s"
+msgstr "Nicht veränderbarer Änderungssatz %s kann nicht verschoben werden"
+
+msgid "see hg help phases for details"
+msgstr "Siehe hg help phases für Details"
+
 msgid "nothing to rebase\n"
-msgstr "Kein Rebase nötig\n"
+msgstr "Kein Verschiebung nötig\n"
 
 msgid "cannot collapse multiple named branches"
-msgstr "Kann nicht mehrere benannte Zweige kollabieren"
+msgstr "Mehrere benannte Zweige können nicht zusammengefaltet werden"
 
 msgid "rebasing"
 msgstr "Verschiebe"
@@ -7313,13 +7481,13 @@
 msgstr "keine Änderungen, Revision %d übersprungen\n"
 
 msgid "rebase merging completed\n"
-msgstr "Zusammenführungen des Rebase abgeschlossen\n"
+msgstr "Zusammenführungen der Verschiebung abgeschlossen\n"
 
 msgid "warning: new changesets detected on source branch, not stripping\n"
 msgstr "Warnung: Neue Änderungssätze auf Quellzweig gefunden, lösche nicht\n"
 
 msgid "rebase completed\n"
-msgstr "Rebase abgeschlossen\n"
+msgstr "Verschiebung abgeschlossen\n"
 
 #, python-format
 msgid "%d revisions have been skipped\n"
@@ -7332,39 +7500,43 @@
 #, python-format
 msgid "cannot use revision %d as base, result would have 3 parents"
 msgstr ""
-"Revision %d kann nicht als Basis genutzt werden, das Ergebnis hätte 3 "
+"Revision %d kann nicht als Basis genutzt werden: das Ergebnis hätte 3 "
 "Vorgänger"
 
 msgid "no rebase in progress"
-msgstr "Kein vorheriger Rebase zur Wiederaufnahme"
+msgstr "Keine vorherige Verschiebung zur Wiederaufnahme"
+
+msgid "warning: immutable rebased changeset detected, can't abort\n"
+msgstr ""
+"Warnung: Unveränderbare Änderungssätze gefunden. Kann nicht abbrechen\n"
 
 msgid "warning: new changesets detected on target branch, can't abort\n"
 msgstr ""
-"Warnung: Neue Änderungssätze auf Zielzweig gefunden, kann nicht abbrechen\n"
+"Warnung: Neue Änderungssätze auf Zielzweig gefunden. Kann nicht abbrechen\n"
 
 msgid "rebase aborted\n"
-msgstr "Rebase abgebrochen\n"
+msgstr "Verschiebung abgebrochen\n"
 
 msgid "cannot rebase onto an applied mq patch"
-msgstr "Rebase kann auf einem angewandten MQ-Patch nicht aufsetzen"
+msgstr "Verschiebung kann nicht auf einem angewandten MQ-Patch aufsetzen"
 
 msgid "no matching revisions"
 msgstr "keine passenden Revisionen"
 
 msgid "can't rebase multiple roots"
-msgstr ""
+msgstr "Mehrere Wurzeln können nicht verschoben werden"
 
 msgid "source is ancestor of destination"
 msgstr "Quelle ist ein Vorfahr des Ziels"
 
 msgid "--tool can only be used with --rebase"
-msgstr ""
+msgstr "--tool kann nicht gleichzeitig mit --rebase verwendet werden"
 
 msgid "rebase working directory to branch head"
-msgstr "Führt Rebase zu einem Zweigkopf auf dem Arbeitsverzeichnis aus"
+msgstr "Führt Verschiebung des Arbeitsverzeichnisses auf den Zweigkopf"
 
 msgid "specify merge tool for rebase"
-msgstr "Method für Zusammenführungen innerhalb der Verschiebung"
+msgstr "Methode für Zusammenführungen innerhalb der Verschiebung"
 
 msgid "commands to interactively select changes for commit/qrefresh"
 msgstr "Befehle um interaktiv Änderungen für commit/qrefresh zu wählen"
@@ -7389,16 +7561,16 @@
 msgstr "%d Hunks, %d Zeilen geändert\n"
 
 msgid "[Ynsfdaq?]"
-msgstr ""
+msgstr "[Jnsdfab?]"
 
 msgid "&Yes, record this change"
-msgstr "&Yes - übernimmt diese Änderung"
+msgstr "&Ja - übernimmt diese Änderung"
 
 msgid "&No, skip this change"
-msgstr "&No, überspringt diese Änderung"
+msgstr "&Nein, überspringt diese Änderung"
 
 msgid "&Skip remaining changes to this file"
-msgstr "&Überspringe die restlichen Änderungen an dieser Datei"
+msgstr "Über&springe die restlichen Änderungen an dieser Datei"
 
 msgid "Record remaining changes to this &file"
 msgstr "Zeichne die restlichen Änderungen an dieser &Datei auf"
@@ -7407,13 +7579,13 @@
 msgstr "&Fertig, überspringe die restlichen Änderungen und Dateien"
 
 msgid "Record &all changes to all remaining files"
-msgstr "Übernimmt &alle Änderungen aller restlichen Dateien"
+msgstr "Zeichne &alle Änderungen der verbleibenden Dateien auf"
 
 msgid "&Quit, recording no changes"
-msgstr "&Quit, übernimmt keine Änderungen"
+msgstr "&Beende, zeichnet keine Änderungen auf"
 
 msgid "&?"
-msgstr ""
+msgstr "&?"
 
 msgid "user quit"
 msgstr "Abbruch durch Benutzer"
@@ -7460,7 +7632,7 @@
 "      y - record this change\n"
 "      n - skip this change"
 msgstr ""
-"      y - übernimmt diese Änderung\n"
+"      j - übernimmt diese Änderung\n"
 "      n - überspringt diese Änderung"
 
 msgid ""
@@ -7475,9 +7647,9 @@
 "      a - record all changes to all remaining files\n"
 "      q - quit, recording no changes"
 msgstr ""
-"      d - fertig, überspringt verbleibende Änderungen und Dateien\n"
+"      f - fertig, überspringt verbleibende Änderungen und Dateien\n"
 "      a - übernimmt alle Änderungen aller verbleibenden Dateien\n"
-"      q - beendet ohne Änderungen zu übernehmen"
+"      b - beendet ohne Änderungen zu übernehmen"
 
 msgid "      ? - display help"
 msgstr "      ? - zeigt Hilfe an"
@@ -7507,8 +7679,8 @@
 
 msgid "cannot partially commit a merge (use \"hg commit\" instead)"
 msgstr ""
-"Eine Zusammenführung kann nicht teilweise übernommen werden (verwende "
-"stattdessen :h:`commit`)"
+"Eine Zusammenführung kann nicht teilweise übernommen werden (verwende :hg:"
+"`commit`)"
 
 msgid "no changes to record\n"
 msgstr "Keine Änderungen zu übernehmen\n"
@@ -7520,49 +7692,51 @@
 msgstr "Interaktive Auswahl der Änderungen für refresh"
 
 msgid "recreates hardlinks between repository clones"
-msgstr "stellt Hardlinks zwischen Repository Clones wieder her"
+msgstr "stellt Hardlinks zwischen Archivklonen wieder her"
 
 msgid "recreate hardlinks between two repositories"
-msgstr "stellt Hardlinks zwischen zwei Repositories wieder her"
+msgstr "stellt harte Verknüpfungen zwischen zwei Archiven wieder her"
 
 msgid ""
 "    When repositories are cloned locally, their data files will be\n"
 "    hardlinked so that they only use the space of a single repository."
 msgstr ""
-"    Wenn Repositories lokal geklont werden, werden ihre Datendateien\n"
-"    hart gelinkt, sodaß sie nur den Platz eines einzelnen Repositories\n"
-"    belegen."
+"    Wenn Archive lokal geklont werden, werden ihre Datendateien hart\n"
+"    verknüpft, sodass sie nur den Platz eines einzelnen Archivs belegen."
 
 msgid ""
 "    Unfortunately, subsequent pulls into either repository will break\n"
 "    hardlinks for any files touched by the new changesets, even if\n"
 "    both repositories end up pulling the same changes."
 msgstr ""
-"    Unglücklicherweise werden nachfolgende ``pull``s in jedes von diesen\n"
-"    Repositories Hardlinks für alle Dateien, die von dem neuen Changeset\n"
-"    betroffen sind, brechen, selbst wenn beide Repositories schlussendlich\n"
-"    die gleichen Änderungen einbeziehen."
+"    Unglücklicherweise brechen spätere Archivänderungen (z.B. Abrufen) die\n"
+"    Verknüpfungen aller Dateien, die von neuen Änderungssätzen betroffen\n"
+"    sind, selbst wenn beide Archive letzlich dieselben Änderungen enthalten."
 
 msgid ""
 "    Similarly, passing --rev to \"hg clone\" will fail to use any\n"
 "    hardlinks, falling back to a complete copy of the source\n"
 "    repository."
 msgstr ""
-"    In gleicher Weise scheitert \"hg clone\" mit --rev an Hardlinks,\n"
-"    sondern nutzt eine komplette Kopie des Quell-Repositories."
+"    In gleicher Weise scheitert \"hg clone\" mit --rev an harten\n"
+"    Verknüpfungen und nutzt eine komplette Kopie des Quellarchivs."
 
 msgid ""
 "    This command lets you recreate those hardlinks and reclaim that\n"
 "    wasted space."
 msgstr ""
-"    Dieses Kommando erlaubt Ihnen, diese Hardlinks wieder herzustellen und\n"
-"    den verlorenen Platz wieder zurück zu gewinnen."
+"    Dieses Kommando erlaubt es, diese Verknüpfungen wieder herzustellen\n"
+"    und den verlorenen Platz zurückzugewinnen."
 
 msgid ""
 "    This repository will be relinked to share space with ORIGIN, which\n"
 "    must be on the same local disk. If ORIGIN is omitted, looks for\n"
 "    \"default-relink\", then \"default\", in [paths]."
 msgstr ""
+"    Dieses Archiv wird neu verknüpft um den Speicher mit HERKUNFT, welches\n"
+"    auf derselben lokalen Platte sein muss, zu teilen. Wenn HERKUNFT nicht\n"
+"    angegeben ist, wird der Pfade (siehe [paths]) mit dem Namen\n"
+"    \"default-relink\" und schliesslich \"default\" verwendet."
 
 msgid ""
 "    Do not attempt any read operations on this repository while the\n"
@@ -7570,9 +7744,12 @@
 "    writes.)\n"
 "    "
 msgstr ""
+"    Versuchen Sie nicht, während der Aktion Leseoperationen auf diesem\n"
+"    Archiv durchzuführen. Schreiben wird von beiden Archive verhindert.\n"
+"    "
 
 msgid "hardlinks are not supported on this system"
-msgstr "Hardlinks werden von diesem System nicht unterstützt"
+msgstr "Harte Verknüpfungen werden von diesem System nicht unterstützt"
 
 msgid "must specify local origin repository"
 msgstr "Lokales Quellarchiv muss angegeben werden"
@@ -7582,11 +7759,11 @@
 msgstr "Wiederverknüpft: %s nach %s\n"
 
 msgid "there is nothing to relink\n"
-msgstr ""
+msgstr "Es gibt nichts zum wiederverknüpfen\n"
 
 #, python-format
 msgid "tip has %d files, estimated total number of files: %s\n"
-msgstr ""
+msgstr "Die Archivspitze (tip) hat %d Dateien. Geschätzte Gesamtzahl: %s\n"
 
 msgid "collecting"
 msgstr "Sammle"
@@ -7602,21 +7779,21 @@
 msgstr "Quelle und Ziel sind auf unterschiedlichen Geräten"
 
 msgid "pruning"
-msgstr ""
+msgstr "Schränke ein"
 
 #, python-format
 msgid "pruned down to %d probably relinkable files\n"
-msgstr ""
+msgstr "Schränke auf %d wahrscheinlich wiederverknüpfbare Dateien ein\n"
 
 msgid "relinking"
-msgstr ""
+msgstr "Verknüpfe erneut"
 
 #, python-format
 msgid "relinked %d files (%s reclaimed)\n"
-msgstr ""
+msgstr "%d Dateien wiederverknüpft (%s zurückgewonnen)\n"
 
 msgid "[ORIGIN]"
-msgstr ""
+msgstr "[HERKUNFT]"
 
 msgid "extend schemes with shortcuts to repository swarms"
 msgstr ""
@@ -7731,7 +7908,7 @@
 
 #, python-format
 msgid "skipping already applied revision %s\n"
-msgstr ""
+msgstr "Überspringe bereits angewendete Revision %s\n"
 
 #, python-format
 msgid "skipping merge changeset %s:%s\n"
@@ -7750,14 +7927,14 @@
 msgstr ""
 
 msgid "filter failed"
-msgstr ""
+msgstr "Filter fehlgeschlagen"
 
 msgid "can only omit patchfile if merging"
 msgstr ""
 
 #, python-format
 msgid "%s: empty changeset"
-msgstr ""
+msgstr "%s: leerer Änderungssatz"
 
 msgid "fix up the merge and run hg transplant --continue"
 msgstr ""
@@ -7774,7 +7951,7 @@
 msgstr ""
 
 msgid "commit failed"
-msgstr ""
+msgstr "Übernahme der Änderungen schlug fehl"
 
 msgid "filter corrupted changeset (no user or date)"
 msgstr "filtriere beschädigte Änderungssätze (ohne Nutzer oder Datum)"
@@ -8129,8 +8306,8 @@
 msgstr "Aktualisiere Lesezeichen %s\n"
 
 #, python-format
-msgid "not updating divergent bookmark %s\n"
-msgstr "Aktualisiere nicht divergierendes Lesezeichen %s\n"
+msgid "divergent bookmark %s stored as %s\n"
+msgstr "Divergierendes Lesezeichen %s als %s gespeichert\n"
 
 msgid "searching for changed bookmarks\n"
 msgstr "Suche nach geänderten Lesezeichen\n"
@@ -8288,6 +8465,10 @@
 msgstr "Marke:           %s\n"
 
 #, python-format
+msgid "phase:       %s\n"
+msgstr "Phase:       %s\n"
+
+#, python-format
 msgid "parent:      %d:%s\n"
 msgstr "Vorgänger:       %d:%s\n"
 
@@ -8585,6 +8766,8 @@
 "    Returns 0 if all files are successfully added.\n"
 "    "
 msgstr ""
+"    Gibt 0 zurück, wenn alle Dateien erfolgreich hinzugefügt wurden.\n"
+"    "
 
 msgid "add all new files, delete all missing files"
 msgstr "Fügt alle neuen Dateien hinzu, löscht alle fehlenden Dateien"
@@ -8601,8 +8784,8 @@
 "    ``.hgignore``. As with add, these changes take effect at the next\n"
 "    commit."
 msgstr ""
-"    Neue Dateien werden ignoriert, wenn sie einem der Muster aus "
-"``.hgignore``\n"
+"    Neue Dateien werden ignoriert, wenn sie einem der Muster aus ``."
+"hgignore``\n"
 "    entsprechen. Genau wie add, wirken diese Änderungen erst beim nächsten\n"
 "    Übernehmen (commit)."
 
@@ -8632,8 +8815,7 @@
 msgstr "Annotiert die angegebene Revision"
 
 msgid "follow copies/renames and list the filename (DEPRECATED)"
-msgstr ""
-"Folge Kopien/Umbenennungen und liste Dateinamen auf (VERALTET)"
+msgstr "Folge Kopien/Umbenennungen und liste Dateinamen auf (VERALTET)"
 
 msgid "don't follow copies and renames"
 msgstr "Unterläßt das Folgen von Dateikopien und Umbenennungen"
@@ -9086,17 +9268,18 @@
 msgid "track a line of development with movable markers"
 msgstr "Folgt einem Entwicklungsstrang mit einer beweglichen Markierung"
 
-msgid ""
-"    Bookmarks are pointers to certain commits that move when\n"
-"    committing. Bookmarks are local. They can be renamed, copied and\n"
-"    deleted. It is possible to use bookmark names in :hg:`merge` and\n"
-"    :hg:`update` to merge and update respectively to a given bookmark."
+#, fuzzy
+msgid ""
+"    Bookmarks are pointers to certain commits that move when committing.\n"
+"    Bookmarks are local. They can be renamed, copied and deleted. It is\n"
+"    possible to use :hg:`merge NAME` to merge from a given bookmark, and\n"
+"    :hg:`update NAME` to update to a given bookmark."
 msgstr ""
 "    Lesezeichen sind Zeiger auf bestimmte Versionen, die mitwandern,\n"
 "    wenn eine neuen Version erzeugt wird. Lesezeichen sind nur lokal.\n"
 "    Sie können umbenannt, kopiert und gelöscht werden. Es ist möglich,\n"
-"    Lesezeichen bei :hg: `merge` und :hg:`update` zu nutzen, um auf das\n"
-"    angegebene Lesezeichen zu aktualisieren."
+"    Lesezeichen bei :hg: `merge` und :hg:`update` anzugeben, um das an-\n"
+"    gegebene Lesezeichen zusammenzuführen, bzw. darauf zu aktualisieren."
 
 msgid ""
 "    You can use :hg:`bookmark NAME` to set a bookmark on the working\n"
@@ -9165,6 +9348,14 @@
 msgstr "Setzt oder zeigt den Namen des aktuellen Zweigs"
 
 msgid ""
+"    .. note::\n"
+"       Branch names are permanent and global. Use :hg:`bookmark` to create "
+"a\n"
+"       light-weight bookmark instead. See :hg:`help glossary` for more\n"
+"       information about named branches and bookmarks."
+msgstr ""
+
+msgid ""
 "    With no argument, show the current branch name. With one argument,\n"
 "    set the working directory branch name (the branch will not exist\n"
 "    in the repository until the next commit). Standard practice\n"
@@ -9201,13 +9392,6 @@
 "    :hg:`update`. Mit :hg:`commit --close-branch` wird der aktuelle Zweig\n"
 "    geschlossen."
 
-msgid ""
-"    .. note::\n"
-"       Branch names are permanent. Use :hg:`bookmark` to create a\n"
-"       light-weight bookmark instead. See :hg:`help glossary` for more\n"
-"       information about named branches and bookmarks."
-msgstr ""
-
 #, python-format
 msgid "reset working directory to branch %s\n"
 msgstr "Setze Arbeitsverzeichnis auf Zweig %s zurück\n"
@@ -9223,6 +9407,9 @@
 msgid "marked working directory as branch %s\n"
 msgstr "Arbeitsverzeichnis wurde als Zweig %s markiert\n"
 
+msgid "(branches are permanent and global, did you want a bookmark?)\n"
+msgstr ""
+
 msgid "show only branches that have unmerged heads"
 msgstr "Zeigt nur Branches deren Köpfe nicht zusammengeführt wurden"
 
@@ -9259,12 +9446,14 @@
 "    Returns 0.\n"
 "    "
 msgstr ""
+"    Gibt 0 zurück.\n"
+"    "
 
 msgid " (closed)"
-msgstr ""
+msgstr " (geschlossen)"
 
 msgid " (inactive)"
-msgstr ""
+msgstr " (inaktiv)"
 
 msgid "run even when the destination is unrelated"
 msgstr "Auch ausführen, wenn das Ziel keinen Bezug hat"
@@ -9461,7 +9650,8 @@
 "      --pull option to avoid hardlinking."
 msgstr ""
 "      Aus Effizienzgründen werden 'hardlinks' für das Klonen genutzt, wann\n"
-"      immer Quelle und Ziel auf dem selben Dateisystem liegen (dies gilt nur\n"
+"      immer Quelle und Ziel auf dem selben Dateisystem liegen (dies gilt "
+"nur\n"
 "      für die Daten des Archivs, nicht für die Arbeitskopie). Einige\n"
 "      Dateisyteme, wie etwa AFS, implementieren 'hardlinks' fehlerhaft,\n"
 "      erzeugen dabei aber keine Fehlermeldung. Dann muss die --pull Option\n"
@@ -9554,8 +9744,7 @@
 msgstr ""
 
 msgid "    See :hg:`help urls` for details on specifying URLs."
-msgstr ""
-"    Siehe auch :hg:`help urls` für das Format von Adressangaben."
+msgstr "    Siehe auch :hg:`help urls` für das Format von Adressangaben."
 
 msgid "cannot specify both --noupdate and --updaterev"
 msgstr ""
@@ -10171,8 +10360,7 @@
 msgstr "[OPTION]... [-o DATEINAMENMUSTER] REV..."
 
 msgid "dump the header and diffs for one or more changesets"
-msgstr ""
-"Gibt Kopfzeilen und Änderungsverlauf einer oder mehrerer Versionen aus"
+msgstr "Gibt Kopfzeilen und Änderungsverlauf einer oder mehrerer Versionen aus"
 
 msgid "    Print the changeset header and diffs for one or more revisions."
 msgstr ""
@@ -10221,8 +10409,10 @@
 "    :``%N``: Anzahl der generierten Patches\n"
 "    :``%R``: Revisionnummer des Änderungssatzes\n"
 "    :``%b``: Basisname des exportierten Archivs\n"
-"    :``%h``: Kurzform der Prüfsumme des Änderungssatzes (12 Byte hexadezimal)\n"
-"    :``%m``: Erste Zeile der Übernahmenachricht (nur alphanumerische Zeichen)\n"
+"    :``%h``: Kurzform der Prüfsumme des Änderungssatzes (12 Byte "
+"hexadezimal)\n"
+"    :``%m``: Erste Zeile der Übernahmenachricht (nur alphanumerische "
+"Zeichen)\n"
 "    :``%n``: Laufende Nummer mit führenden Nullen, beginnend bei 1\n"
 "    :``%r``: Revisionsnummer mit führenden Nullen"
 
@@ -10349,9 +10539,9 @@
 
 msgid ""
 "    If a graft merge results in conflicts, the graft process is\n"
-"    aborted so that the current merge can be manually resolved. Once\n"
-"    all conflicts are addressed, the graft process can be continued\n"
-"    with the -c/--continue option."
+"    interrupted so that the current merge can be manually resolved.\n"
+"    Once all conflicts are addressed, the graft process can be\n"
+"    continued with the -c/--continue option."
 msgstr ""
 
 msgid ""
@@ -10913,10 +11103,10 @@
 
 msgid ""
 "    With -s/--similarity, hg will attempt to discover renames and\n"
-"    copies in the patch in the same way as 'addremove'."
+"    copies in the patch in the same way as :hg:`addremove`."
 msgstr ""
 "    Mit der Option -s/--similarity werden Umbenennungen und Kopien auf\n"
-"    gleiche Weise wie mit dem Befehl \"hg addremove\" erkannt."
+"    gleiche Weise wie mit dem Befehl :hg:`addremove` erkannt."
 
 msgid ""
 "    To read a patch from standard input, use \"-\" as the patch name. If\n"
@@ -10966,7 +11156,7 @@
 msgstr ""
 
 msgid "patch is damaged or loses information"
-msgstr "Prüfsumme stimmt nicht überein: Patch korrumpiert"
+msgstr "Prüfsumme stimmt nicht überein: Patch beschädigt"
 
 msgid "applied to working directory"
 msgstr "Angewendet aufs Arbeitsverzeichnis"
@@ -11404,12 +11594,11 @@
 msgid "run 'hg heads' to see all heads"
 msgstr "'hg heads' zeigt alle Köpfe"
 
-msgid "there is nothing to merge"
+msgid "nothing to merge"
 msgstr "Es gibt nichts zum Zusammenführen"
 
-#, python-format
-msgid "%s - use \"hg update\" instead"
-msgstr "%s - Nutze \"hg update\" stattdessen"
+msgid "use 'hg update' instead"
+msgstr "Nutze stattdessen 'hg update'"
 
 msgid "working directory not at a head revision"
 msgstr "Arbeitsverzeichnis ist nicht auf Stand der Kopfversion"
@@ -11478,7 +11667,7 @@
 msgstr "'%s' nicht im Manifest gefunden!"
 
 msgid "[NAME]"
-msgstr ""
+msgstr "[NAME]"
 
 msgid "show aliases for remote repositories"
 msgstr "Zeigt Adresse für Aliasnamen von entfernten Projektarchiven an"
@@ -11525,6 +11714,61 @@
 msgid "not found!\n"
 msgstr "nicht gefunden!\n"
 
+msgid "set changeset phase to public"
+msgstr ""
+
+msgid "set changeset phase to draft"
+msgstr ""
+
+msgid "set changeset phase to secret"
+msgstr ""
+
+msgid "allow to move boundary backward"
+msgstr ""
+
+msgid "target revision"
+msgstr ""
+
+msgid "[-p|-d|-s] [-f] [-r] REV..."
+msgstr ""
+
+msgid "set or show the current phase name"
+msgstr ""
+
+msgid "    With no argument, show the phase name of specified revisions."
+msgstr ""
+
+msgid ""
+"    With one of -p/--public, -d/--draft or -s/--secret, change the\n"
+"    phase value of the specified revisions."
+msgstr ""
+
+msgid ""
+"    Unless -f/--force is specified, :hg:`phase` won't move changeset from a\n"
+"    lower phase to an higher phase. Phases are ordered as follows::"
+msgstr ""
+
+msgid "        public < draft < secret"
+msgstr ""
+
+msgid ""
+"    Return 0 on success, 1 if no phases were changed.\n"
+"    "
+msgstr ""
+
+msgid "only one phase can be specified"
+msgstr ""
+
+msgid "no revisions specified!"
+msgstr ""
+
+#, python-format
+msgid "phase change for %i changesets\n"
+msgstr ""
+
+msgid "no phases changed\n"
+msgstr ""
+
 #, python-format
 msgid "not updating: %s\n"
 msgstr "aktualisiere nicht: %s\n"
@@ -11672,8 +11916,7 @@
 msgid ""
 "    Push changesets from the local repository to the specified\n"
 "    destination."
-msgstr ""
-"    Überträgt lokale Änderungen in das angegebene Ziel."
+msgstr "    Überträgt lokale Änderungen in das angegebene Ziel."
 
 msgid ""
 "    This operation is symmetrical to pull: it is identical to a pull\n"
@@ -11728,7 +11971,6 @@
 "gezogen\n"
 "    werden. Beim Weglassen des ZIELs wird standardmäßig der 'default'-Pfad\n"
 "    genutzt. Weitere Hilfe gibt unter :hg:`help urls`."
-"    "
 
 msgid ""
 "    Returns 0 if push was successful, 1 if nothing to push.\n"
@@ -11809,7 +12051,8 @@
 msgstr ""
 "      Option -A/--after kann genutzt werden, um Dateien zu entfernen, die\n"
 "      bereits gelöscht wurden, -f/--force kann genutzt werden, um die\n"
-"      Löschung zu erzwingen. -Af entfernt Dateien aus der nächsten Revision,\n"
+"      Löschung zu erzwingen. -Af entfernt Dateien aus der nächsten "
+"Revision,\n"
 "      ohne sie im Arbeitsverzeichnis zu löschen"
 
 msgid ""
@@ -11819,7 +12062,8 @@
 "      (as reported by :hg:`status`). The actions are Warn, Remove\n"
 "      (from branch) and Delete (from disk):"
 msgstr ""
-"      Die folgende Tabelle beschreibt detailliert das Verhalten von 'remove'\n"
+"      Die folgende Tabelle beschreibt detailliert das Verhalten von "
+"'remove'\n"
 "      für unterschiedliche Dateizustände (Spalten) und Optionskombinationen\n"
 "      (Reihen). Die Dateizustände sind Hinzugefügt (A), Unverändert (C),\n"
 "      Verändert (M) und Fehlend (!) (wie von :hg:`status` angezeigt). Die\n"
@@ -11871,7 +12115,9 @@
 
 #, python-format
 msgid "not removing %s: file has been marked for add (use forget to undo)\n"
-msgstr "Entferne nicht %s: Datei soll hinzugefügt werden ('hg forget' um dies rückgängig zu machen)\n"
+msgstr ""
+"Entferne nicht %s: Datei soll hinzugefügt werden ('hg forget' um dies "
+"rückgängig zu machen)\n"
 
 msgid "record a rename that has already occurred"
 msgstr ""
@@ -11994,8 +12240,7 @@
 msgstr ""
 
 msgid "restore files to their checkout state"
-msgstr ""
-"Setzt gegebene Dateien oder Verzeichnisse auf frühere Version zurück"
+msgstr "Setzt gegebene Dateien oder Verzeichnisse auf frühere Version zurück"
 
 msgid ""
 "    .. note::\n"
@@ -12033,7 +12278,8 @@
 msgstr ""
 "    Mit der -r/--rev oder der -d/--date Option werden die Dateien oder\n"
 "    Verzeichnisse auf die gegebene Revision zurückgesetzt. Da 'revert' aber\n"
-"    nicht die mit dem Arbeitsverzeichnis assoziierte Revisionsnummer ändert,\n"
+"    nicht die mit dem Arbeitsverzeichnis assoziierte Revisionsnummer "
+"ändert,\n"
 "    gelten die betroffenen Dateien dann als modifiziert. Damit kann man\n"
 "    ungewollte aber bereits übernommene Änderungen rückgängig machen. Siehe\n"
 "    auch :hg:`backout` für eine ähnliche Methode."
@@ -12155,17 +12401,14 @@
 "    - unbundle"
 
 msgid ""
-"    It's possible to lose data with rollback: commit, update back to\n"
-"    an older changeset, and then rollback. The update removes the\n"
-"    changes you committed from the working directory, and rollback\n"
-"    removes them from history. To avoid data loss, you must pass\n"
-"    --force in this case."
-msgstr ""
-"    Es gibt die Möglichkeit durch das Zurückrollen Daten zu verlieren, z.B.\n"
-"    die Übernahme neuer Änderungen (commit) gefolgt von einer Aktualisierung\n"
-"    auf eine andere Revision und einem Zurückrollen der Übernahme.\n"
-"    Damit dies nicht aus Versehen passiert, wird in diesem Falle --force als\n"
-"    Parameter gefordert."
+"    To avoid permanent data loss, rollback will refuse to rollback a\n"
+"    commit transaction if it isn't checked out. Use --force to\n"
+"    override this protection."
+msgstr ""
+"    Um einen versehentlichen Datenverlust zu verhindern, wird rollback\n"
+"    keine Änderungssätze aus der Historie entfernen, die nicht im\n"
+"    Arbeitsverzeichnis aktuell sind. Um diesen Schutz abzuschalten,\n"
+"    muss --force (erzwingen) angegeben werden."
 
 msgid ""
 "    This command is not intended for use on public repositories. Once\n"
@@ -12396,7 +12639,8 @@
 "       relative to one merge parent."
 msgstr ""
 "    .. note:\n"
-"       Der Status kann sich vom Diff unterscheiden, wenn sich Berechtigungen\n"
+"       Der Status kann sich vom Diff unterscheiden, wenn sich "
+"Berechtigungen\n"
 "       geändert haben oder eine Zusammenführung durchgeführt wurde. Das\n"
 "       Standard-Diff-Format zeigt keine Berechtigungsänderungen an und\n"
 "       'diff' zeigt nur Änderungen relativ zu einer Vorgängerversion einer\n"
@@ -12436,9 +12680,12 @@
 "      I = ignoriert\n"
 "        = die zuvor hinzugefügt Datei (A) wurde von hier kopiert"
 
-msgid "      - show changes in the working directory relative to a changeset:"
-msgstr ""
-"      - zeigt Änderungen zwischen dem Arbeitsverzeichnis und einer Revision:"
+msgid ""
+"      - show changes in the working directory relative to a\n"
+"        changeset::"
+msgstr ""
+"      - zeigt Änderungen zwischen dem Arbeitsverzeichnis und einer\n"
+"        Revision::"
 
 msgid "          hg status --rev 9353"
 msgstr ""
@@ -12721,7 +12968,8 @@
 "    umbenannt oder manuell einem anderen Änderungssatz angehängt werden."
 
 msgid "update to new branch head if changesets were unbundled"
-msgstr "aktualisiere auf den neuen Zweigkopf when Änderungssätze entpackt wurden"
+msgstr ""
+"aktualisiere auf den neuen Zweigkopf when Änderungssätze entpackt wurden"
 
 msgid "[-u] FILE..."
 msgstr "[-u] DATEI..."
@@ -12745,7 +12993,9 @@
 msgstr "entferne nicht versionierte Änderungen (kein Backup)"
 
 msgid "update across branches if no uncommitted changes"
-msgstr "Aktualisiere auf anderen Zweig (falls keine unversionierte Änderungen ausstehen)"
+msgstr ""
+"Aktualisiere auf anderen Zweig (falls keine unversionierten Änderungen "
+"ausstehen)"
 
 msgid "[-c] [-C] [-d DATE] [[-r] REV]"
 msgstr "[-c] [-C] [-d DATUM] [[-r] REV]"
@@ -12756,10 +13006,11 @@
 msgid ""
 "    Update the repository's working directory to the specified\n"
 "    changeset. If no changeset is specified, update to the tip of the\n"
-"    current named branch."
+"    current named branch and move the current bookmark."
 msgstr ""
 "    Hebt das Arbeitsverzeichnis auf die angegebene Revision an. Ohne\n"
-"    Angabe einer Revision wird der Spitze des aktuellen Zweigs gewählt."
+"    Angabe einer Revision wird der Spitze des aktuellen Zweigs gewählt\n"
+"    und ggf. das aktuelle Lesezeichen verschoben."
 
 msgid ""
 "    If the changeset is not a descendant of the working directory's\n"
@@ -12867,11 +13118,11 @@
 msgstr "(siehe http://mercurial.selenic.com für mehr Information)"
 
 msgid ""
-"Copyright (C) 2005-2011 Matt Mackall and others\n"
+"Copyright (C) 2005-2012 Matt Mackall and others\n"
 "This is free software; see the source for copying conditions. There is NO\n"
 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
 msgstr ""
-"Copyright (C) 2005-2011 Matt Mackall und andere\n"
+"Copyright (C) 2005-2012 Matt Mackall und andere\n"
 "Dies ist freie Software; siehe Quellen für Kopierbestimmungen. Es besteht\n"
 "KEINE Gewährleistung für das Programm, nicht einmal der Marktreife oder der\n"
 "Verwendbarkeit für einen bestimmten Zweck.\n"
@@ -13175,7 +13426,7 @@
 msgstr "Option --cwd kann nicht abgekürzt werden!"
 
 msgid ""
-"Option -R has to be separated from other options (e.g. not -qR) and --"
+"option -R has to be separated from other options (e.g. not -qR) and --"
 "repository may only be abbreviated as --repo!"
 msgstr ""
 "Option -R muss von anderen Optionen getrennt werden (also z.B. nicht -qR) "
@@ -13899,7 +14150,7 @@
 msgid ""
 "\n"
 "``annotate``\n"
-"\"\"\"\"\"\"\"\""
+"\"\"\"\"\"\"\"\"\"\"\"\""
 msgstr ""
 
 msgid ""
@@ -14502,11 +14753,14 @@
 "various actions such as starting or finishing a commit. Multiple\n"
 "hooks can be run for the same action by appending a suffix to the\n"
 "action. Overriding a site-wide hook can be done by changing its\n"
-"value or setting it to an empty string."
+"value or setting it to an empty string.  Hooks can be prioritized\n"
+"by adding a prefix of ``priority`` to the hook name on a new line\n"
+"and setting the priority.  The default priority is 0 if\n"
+"not specified."
 msgstr ""
 
 msgid "Example ``.hg/hgrc``::"
-msgstr ""
+msgstr "Beispiel ``.hg/hgrc``-Datei::"
 
 msgid ""
 "  [hooks]\n"
@@ -14515,7 +14769,9 @@
 "  # do not use the site-wide hook\n"
 "  incoming =\n"
 "  incoming.email = /my/email/hook\n"
-"  incoming.autobuild = /my/build/hook"
+"  incoming.autobuild = /my/build/hook\n"
+"  # force autobuild hook to run before other incoming hooks\n"
+"  priority.incoming.autobuild = 1"
 msgstr ""
 
 msgid ""
@@ -15798,11 +16054,12 @@
 msgstr ""
 "EDITOR\n"
 "    Manchmal muss Mercurial eine Textdatei in einem Editor öffnen, damit\n"
-"    der Nutzer sie bearbeiten kann, zum Beispiel when eine Commit-\n"
-"    Nachricht geschrieben wird. Der verwendete Editor wird aus den drei\n"
-"    Umgebungsvariablen HGEDITOR, VISUAL und EDITOR (in dieser Reihenfolge)\n"
-"    ermittelt. Der erste nicht-leere wird verwendet. Wenn alle Angaben\n"
-"    leer sind, wird der Standard 'vi' verwendet."
+"    der Nutzer sie bearbeiten kann, zum Beispiel wenn eine Versionsmeldung\n"
+"    geschrieben wird. Der verwendete Editor wird aus den drei Umgebungs-\n"
+"    variablen HGEDITOR, VISUAL und EDITOR (in dieser Reihenfolge) "
+"ermittelt.\n"
+"    Der erste nicht-leere wird verwendet. Wenn alle Angaben leer sind, wird\n"
+"    der Standard 'vi' verwendet."
 
 msgid ""
 "PYTHONPATH\n"
@@ -15993,7 +16250,7 @@
 msgid "- Find C files in a non-standard encoding::"
 msgstr ""
 
-msgid "    hg locate \"set:**.c and not encoding(ascii)\""
+msgid "    hg locate \"set:**.c and not encoding('UTF-8')\""
 msgstr ""
 
 msgid "- Revert copies of large binary files::"
@@ -16656,9 +16913,8 @@
 "character is treated as a comment character, and the ``\\`` character\n"
 "is treated as an escape character."
 msgstr ""
-"Eine ignore-Datei ist eine Textdatei, die aus einer Liste von Patterns "
-"besteht,\n"
-"mit einem Ausdruck pro Zeile. Leere Zeilen werden übersprungen.\n"
+"Eine ignore-Datei ist eine Textdatei, die aus einer Liste von Dateimustern\n"
+"besteht, mit einem Ausdruck pro Zeile. Leere Zeilen werden übersprungen.\n"
 "Das ``#``-Zeichen wird als Kommentarzeichen behandelt und das \n"
 "``\\``-Zeichen als Escape-Zeichen."
 
@@ -16666,11 +16922,11 @@
 "Mercurial supports several pattern syntaxes. The default syntax used\n"
 "is Python/Perl-style regular expressions."
 msgstr ""
-"Mercurial unterstützt verschiedene Pattern-Syntaxen. Im Normalfall\n"
+"Mercurial unterstützt verschiedene Dateimuster-Syntaxen. Im Normalfall\n"
 "werden Python/Perl-artige Reguläre Ausdrücke verwendet."
 
 msgid "To change the syntax used, use a line of the following form::"
-msgstr ""
+msgstr "Die folgende Zeile ändert die von diesem Punkt an verwendete Syntax::"
 
 msgid "  syntax: NAME"
 msgstr "  Syntax: NAME"
@@ -16689,7 +16945,7 @@
 "The chosen syntax stays in effect when parsing all patterns that\n"
 "follow, until another syntax is selected."
 msgstr ""
-"Die gewählte Syntax wird auf auf alle folgenden Patterns angewendet\n"
+"Die gewählte Syntax wird auf auf alle folgenden Muster angewendet\n"
 "bis eine andere Syntax ausgewählt wird."
 
 msgid ""
@@ -17103,8 +17359,7 @@
 msgstr "Beispiel mit regulärem Ausdruck::"
 
 msgid "  re:.*\\.c$      any name ending in \".c\", anywhere in the repository"
-msgstr ""
-"  re:.*\\.c$     jeder Name endend mit \".c\" überall im Projektarchiv"
+msgstr "  re:.*\\.c$     jeder Name endend mit \".c\" überall im Projektarchiv"
 
 msgid "File examples::"
 msgstr "Datei-Beispiele::"
@@ -17398,16 +17653,16 @@
 "subrepositories."
 msgstr ""
 "Mercurial unterstützt im Augenblick Mercurial-, Git- und Subversion-\n"
-"Subrepositories."
+"Unterarchive."
 
 msgid "Subrepositories are made of three components:"
-msgstr "Subrespositories bestehen aus drei Komponenten:"
+msgstr "Unterarchive bestehen aus drei Komponenten:"
 
 msgid ""
 "1. Nested repository checkouts. They can appear anywhere in the\n"
 "   parent working directory."
 msgstr ""
-"1. Verschachtelte Repository Checkouts. Sie können überall im\n"
+"1. Verschachtelte Archivaktualisierungen. Sie können überall im\n"
 "   übergeordneten Arbeitsverzeichnis auftauchen."
 
 msgid ""
@@ -17415,9 +17670,9 @@
 "   tell where the subrepository checkouts come from. Mercurial\n"
 "   subrepositories are referenced like:"
 msgstr ""
-"2. Verschachtelte Repository References. Sie werden in  ``.hgsub``\n"
-"   definiert und geben an, wo Subrepository Checkouts herkommen.\n"
-"   Mercurial Subrepositories werden wie folgt angegeben:"
+"2. Verschachtelte Archivreferenzen. Sie werden in ``.hgsub`` definiert\n"
+"   und geben an, wo Archivaktualisierungen herkommen.\n"
+"   Mercurial-Unterarchive werden wie folgt angegeben:"
 
 msgid "     path/to/nested = https://example.com/nested/repo/path"
 msgstr ""
@@ -17506,8 +17761,8 @@
 "To remove a subrepository from the parent repository, delete its\n"
 "reference from ``.hgsub``, then remove its files."
 msgstr ""
-"Um ein Subrepository aus seinem Parent Repository zu entfernen,\n"
-"entfernen Sie seine Verweise aus ``.hgsub`` und löschen die Dateien."
+"Um ein Unterarchiv aus seinem Elternarchiv zu entfernen, löschen Sie\n"
+"seine Verweise aus ``.hgsub`` und seine Dateien."
 
 msgid ""
 "Interaction with Mercurial Commands\n"
@@ -18125,7 +18380,7 @@
 msgstr ""
 
 msgid "use -f to force"
-msgstr "Benutzen Sie -f, um dennoch fortzufahren"
+msgstr "Benutzen Sie -f zum erzwingen"
 
 #, python-format
 msgid "named branch could not be reset: current branch is still '%s'\n"
@@ -18203,6 +18458,10 @@
 "changegroupsubset nicht unterstützt."
 
 #, python-format
+msgid "updating %s to public failed!\n"
+msgstr ""
+
+#, python-format
 msgid "%d changesets found\n"
 msgstr "%d Änderungssätze gefunden\n"
 
@@ -18339,11 +18598,13 @@
 msgstr "Zeilenangaben im Diff-Kontext müssen Zahlen sein, nicht %r"
 
 #, python-format
-msgid ""
-"untracked file in working directory differs from file in requested revision: "
-"'%s'"
-msgstr ""
-"Unversionierte Datei in der Arbeitskopie unterscheidet sich von der "
+msgid "%s: untracked file differs\n"
+msgstr "%s: Unversionierte Datei verändert\n"
+
+msgid ""
+"untracked files in working directory differ from files in requested revision"
+msgstr ""
+"Unversionierte Dateien in der Arbeitskopie unterscheidet sich von der "
 "angeforderten Revision: '%s'"
 
 #, python-format
@@ -18415,15 +18676,11 @@
 msgid "merging with a working directory ancestor has no effect"
 msgstr "Zusammenführen mit einem Vorfahren der Arbeitskopie hat keinen Effekt"
 
-msgid "nothing to merge (use 'hg update' or check 'hg heads')"
-msgstr ""
-"Nichts zum Zusammenführen gefunden (nutze 'hg update' oder überprüfe 'hg "
-"heads')"
-
-msgid "outstanding uncommitted changes (use 'hg status' to list changes)"
-msgstr ""
-"Ausstehende nicht versionierte Änderungen (nutze 'hg status' zur Auflistung "
-"der Änderungen)"
+msgid "use 'hg update' or check 'hg heads'"
+msgstr "nutze 'hg update' oder überprüfe 'hg heads'"
+
+msgid "use 'hg status' to list changes"
+msgstr "nutze 'hg status' zur Auflistung der Änderungen"
 
 #, python-format
 msgid "outstanding uncommitted changes in subrepository '%s'"
@@ -18571,6 +18828,14 @@
 msgstr ""
 
 #, python-format
+msgid "ignoring inconsistense public root from remote: %s"
+msgstr ""
+
+#, python-format
+msgid "ignoring unexpected root from remote: %i %s"
+msgstr ""
+
+#, python-format
 msgid "exited with status %d"
 msgstr "Beendet mit Status %d"
 
@@ -18796,6 +19061,14 @@
 "    Nachkommen der Änderungssätze in der Liste sind."
 
 msgid ""
+"``draft()``\n"
+"    Changeset in draft phase."
+msgstr ""
+
+msgid "draft takes no arguments"
+msgstr "draft erwartet keine Argumente"
+
+msgid ""
 "``filelog(pattern)``\n"
 "    Changesets connected to the specified filelog."
 msgstr ""
@@ -19047,6 +19320,33 @@
 "    ein Eintrag nicht gefunden wird, die leere Menge."
 
 msgid ""
+"``public()``\n"
+"    Changeset in public phase."
+msgstr ""
+
+msgid "public takes no arguments"
+msgstr "public erwartet keine Argumente"
+
+msgid ""
+"``remote([id], [path])``\n"
+"    Local revision that corresponds to the given identifier in a\n"
+"    remote repository, if present. Here, the '.' identifier is a\n"
+"    synonym for the current local branch."
+msgstr ""
+
+#. i18n: "remote" is a keyword
+msgid "outgoing takes one or two arguments"
+msgstr "'outgoing' erwartet ein oder zwei Argumente"
+
+#. i18n: "remote" is a keyword
+msgid "remote requires a string id"
+msgstr "remote erwartet eine Zeichenkette (ID)"
+
+#. i18n: "remote" is a keyword
+msgid "remote requires a repository path"
+msgstr "'remote' erwartet einen Projektarchivpfad"
+
+msgid ""
 "``removes(pattern)``\n"
 "    Changesets which remove files matching pattern."
 msgstr ""
@@ -19089,6 +19389,14 @@
 "    Änderungssätze, die keine Eltern in der Menge haben."
 
 msgid ""
+"``secret()``\n"
+"    Changeset in secret phase."
+msgstr ""
+
+msgid "secret takes no arguments"
+msgstr "secret erwartet keine Argumente"
+
+msgid ""
 "``sort(set[, [-]key...])``\n"
 "    Sort set by keys. The default sort order is ascending, specify a key\n"
 "    as ``-key`` to sort in descending order."
@@ -19109,7 +19417,7 @@
 msgstr ""
 "    - ``rev`` für die Revisionsnummer,\n"
 "    - ``branch`` für den Zweignamen,\n"
-"    - ``desc`` für die Commit-Nachricht (description),\n"
+"    - ``desc`` für die Versionsmeldung (description),\n"
 "    - ``user`` für den Benutzernamen (Alias ``author``),\n"
 "    - ``date`` für das Datum des Commits"
 
@@ -19163,7 +19471,7 @@
 
 #, python-format
 msgid "possible case-folding collision for %s"
-msgstr ""
+msgstr "Groß-/Kleinschreibungskonflikt bei %s"
 
 #, python-format
 msgid "path ends in directory separator: %s"
@@ -19197,7 +19505,7 @@
 "kopiert).\n"
 
 msgid ".hg/requires file is corrupt"
-msgstr ".hg/requires file ist korrumpiert"
+msgstr ".hg/requires file ist beschädigt"
 
 #, python-format
 msgid "unknown repository format: requires features '%s' (upgrade Mercurial)"
@@ -19216,7 +19524,7 @@
 msgstr ""
 
 msgid "searching"
-msgstr "suchen"
+msgstr ""
 
 msgid "repository is unrelated"
 msgstr "Projektarchiv steht in keinem Zusammenhang"
@@ -19253,13 +19561,13 @@
 msgid "no suitable response from remote hg"
 msgstr "Keine passende Antwort des entfernten hg"
 
-msgid "remote: "
-msgstr "Entfernt: "
-
 #, python-format
 msgid "push refused: %s"
 msgstr "Hochladen abgewiesen: %s"
 
+msgid "ssl connection failed"
+msgstr "ssk-Verbindung fehlgeschlagen"
+
 msgid "Python SSL support not found"
 msgstr "SSL-Unterstützung für Python nicht gefunden"
 
@@ -19276,33 +19584,17 @@
 msgid "IDN in certificate not supported"
 msgstr ""
 
-#, fuzzy
 msgid "no commonName or subjectAltName found in certificate"
-msgstr "Kein commonName oder subjectAltNamt gefunden im Zertifikat"
+msgstr "Kein commonName oder subjectAltName im Zertifikat gefunden"
 
 #, python-format
 msgid "could not find web.cacerts: %s"
 msgstr "Konnte web.cacerts nicht finden: %s"
 
 #, python-format
-msgid "%s certificate error: %s (use --insecure to connect insecurely)"
-msgstr ""
-"%s Zertifikatsfehler: %s (Benutzen Sie --insecure, um unsicher zu verbinden)"
-
-#, python-format
-msgid "invalid certificate for %s with fingerprint %s"
-msgstr ""
-
-#, python-format
-msgid ""
-"warning: %s certificate with fingerprint %s not verified (check "
-"hostfingerprints or web.cacerts config setting)\n"
-msgstr ""
-
-#, python-format
 msgid "host fingerprint for %s can't be verified (Python too old)"
 msgstr ""
-"Host fingerprint für %s kann nicht verifiziert werden (Python ist zu alt)"
+"Server Authentizität für %s kann nicht verifiziert werden (Python ist zu alt)"
 
 #, python-format
 msgid "warning: certificate for %s can't be verified (Python too old)\n"
@@ -19311,6 +19603,34 @@
 "alt)\n"
 
 #, python-format
+msgid "%s ssl connection error"
+msgstr ""
+
+#, python-format
+msgid "%s certificate error: no certificate received"
+msgstr "%s Zertifikatfehler: Kein Zertifikat empfangen"
+
+#, python-format
+msgid "invalid certificate for %s with fingerprint %s"
+msgstr ""
+
+#, python-format
+msgid "%s certificate error: %s"
+msgstr "%s Zertifikatfehler: %s"
+
+#, , python-format
+msgid "configure hostfingerprint %s or use --insecure to connect insecurely"
+msgstr ""
+"Erlauben Sie Serverkennung %s in der Konfiguration oder benutzen Sie "
+"--insecure, um unsicher zu verbinden"
+
+#, python-format
+msgid ""
+"warning: %s certificate with fingerprint %s not verified (check "
+"hostfingerprints or web.cacerts config setting)\n"
+msgstr ""
+
+#, python-format
 msgid "'%s' does not appear to be an hg repository"
 msgstr "'%s' scheint kein hg-Projektarchiv zu sein"
 
@@ -19451,13 +19771,13 @@
 msgid "pushing branch %s of subrepo %s\n"
 msgstr "Übertrage Zweig %s von Unterarchiv %s\n"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "no branch checked out in subrepo %s\n"
 "cannot push revision %s"
 msgstr ""
-"kein Branch in Subrepo %s ausgecheckt\n"
-"Revision %s kann nicht gepusht werden"
+"kein Zweig in Unterarchiv %s aktuell\n"
+"Revision %s kann nicht übertragen werden"
 
 #, python-format
 msgid "%s, line %s: %s\n"
@@ -19540,13 +19860,12 @@
 msgid ":firstline: Any text. Returns the first line of text."
 msgstr ":firstline: Beliebiger Text. Gibt die erste Zeile des Texts zurück."
 
-#, fuzzy
 msgid ""
 ":hex: Any text. Convert a binary Mercurial node identifier into\n"
 "    its long hexadecimal representation."
 msgstr ""
-":hex: Beliebiger Text. Konvertiert einen binären Mercurial node identifier \n"
-"    in seine lange hexadezimale Repräsentation."
+":hex: Beliebiger Text. Konvertiert eine binären Mercurial Knoten-ID \n"
+"    in eine lange hexadezimale Repräsentation."
 
 msgid ""
 ":hgdate: Date. Returns the date as a pair of numbers: \"1157407993\n"
@@ -19613,7 +19932,6 @@
 ":short: Prüfsumme. Gibt die Kurzform der Prüfsumme zurück, d.h.\n"
 "    als 12 Zeichen lange hexadezimale Zeichenkette."
 
-#, fuzzy
 msgid ""
 ":shortbisect: Any text. Treats `text` as a bisection status, and\n"
 "    returns a single-character representing the status (G: good, B: bad,\n"
@@ -19621,11 +19939,9 @@
 "    is not a valid bisection status."
 msgstr ""
 ":shortbisect: Beliebiger text. Behandelt `text` als Teilungsstatus, und\n"
-"    gibt ein einzelnes Zeichen zurück, dass den Status repräsentiert (G: "
-"gut, B: schlecht,\n"
-"    S: übersprungen, U: ungetestet, I: ignoriert). Gibt ein einzelnes "
-"Leerzeichen zurück,\n"
-"    wenn `text` kein gültiger Teilungsstatus ist."
+"    gibt einen repräsentatives Buchstaben zurück (G: gut, B: schlecht,\n"
+"    S: übersprungen, U: ungetestet, I: ignoriert). Wenn `text` kein\n"
+"    gültiger Teilungsstatus ist, wird ein Leerzeichen zurückgegeben."
 
 msgid ":shortdate: Date. Returns a date like \"2006-09-18\"."
 msgstr ":shortdate: Datumsangabe. Gibt ein Datum wie \"2006-09-18\" zurück."
@@ -19762,9 +20078,11 @@
 ":node: Zeichenkette. Die Prüfsumme, die einen Änderungssatz identifiziert,\n"
 "    als 40 Zeichen lange hexadezimale Zeichenkette."
 
-#, fuzzy
-msgid ":rev: Integer. The changeset phase."
-msgstr ":rev: Ganze Zahl. Die Phase des Changesets."
+msgid ":rev: String. The changeset phase name."
+msgstr ""
+
+msgid ":rev: Integer. The changeset phase index."
+msgstr ""
 
 msgid ":rev: Integer. The repository-local changeset revision number."
 msgstr ""
--- a/mercurial/commands.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/commands.py	Sun Mar 04 17:14:30 2012 -0600
@@ -2155,13 +2155,17 @@
 
 @command('debugrevspec', [], ('REVSPEC'))
 def debugrevspec(ui, repo, expr):
-    '''parse and apply a revision specification'''
+    """parse and apply a revision specification
+
+    Use --verbose to print the parsed tree before and after aliases
+    expansion.
+    """
     if ui.verbose:
         tree = revset.parse(expr)[0]
-        ui.note(tree, "\n")
+        ui.note(revset.prettyformat(tree), "\n")
         newtree = revset.findaliases(ui, tree)
         if newtree != tree:
-            ui.note(newtree, "\n")
+            ui.note(revset.prettyformat(newtree), "\n")
     func = revset.match(ui, expr)
     for c in func(repo, range(len(repo))):
         ui.write("%s\n" % c)
@@ -4212,7 +4216,8 @@
 
         public < draft < secret
 
-    Return 0 on success, 1 if no phases were changed.
+    Return 0 on success, 1 if no phases were changed or some could not
+    be changed.
     """
     # search for a unique phase argument
     targetphase = None
@@ -4254,8 +4259,18 @@
             changes = 0
             newdata = repo._phaserev
             changes = sum(o != newdata[i] for i, o in enumerate(olddata))
+            rejected = [n for n in nodes
+                        if newdata[repo[n].rev()] < targetphase]
+            if rejected:
+                ui.warn(_('cannot move %i changesets to a more permissive '
+                          'phase, use --force\n') % len(rejected))
+                ret = 1
             if changes:
-                ui.note(_('phase change for %i changesets\n') % changes)
+                msg = _('phase changed for %i changesets\n') % changes
+                if ret:
+                    ui.status(msg)
+                else:
+                    ui.note(msg)
             else:
                 ui.warn(_('no phases changed\n'))
                 ret = 1
@@ -5264,18 +5279,22 @@
     if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
         copy = copies.pathcopies(repo[node1], repo[node2])
 
+    fm = ui.formatter('status', opts)
+    format = '%s %s' + end
+    if opts.get('no_status'):
+        format = '%.0s%s' + end
+
     for state, char, files in changestates:
         if state in show:
-            format = "%s %%s%s" % (char, end)
-            if opts.get('no_status'):
-                format = "%%s%s" % end
-
+            label = 'status.' + state
             for f in files:
-                ui.write(format % repo.pathto(f, cwd),
-                         label='status.' + state)
+                fm.startitem()
+                fm.write("status path", format, char,
+                         repo.pathto(f, cwd), label=label)
                 if f in copy:
-                    ui.write('  %s%s' % (repo.pathto(copy[f], cwd), end),
+                    fm.write("copy", '  %s' + end, repo.pathto(copy[f], cwd),
                              label='status.copied')
+    fm.end()
 
 @command('^summary|sum',
     [('', 'remote', None, _('check for push and pull'))], '[--remote]')
@@ -5712,18 +5731,21 @@
     if check and clean:
         raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
 
+    if date:
+        if rev is not None:
+            raise util.Abort(_("you can't specify a revision and a date"))
+        rev = cmdutil.finddate(ui, repo, date)
+
     if check:
         # we could use dirty() but we can ignore merge and branch trivia
         c = repo[None]
         if c.modified() or c.added() or c.removed():
             raise util.Abort(_("uncommitted local changes"))
-
-    if date:
-        if rev is not None:
-            raise util.Abort(_("you can't specify a revision and a date"))
-        rev = cmdutil.finddate(ui, repo, date)
-
-    if clean or check:
+        if not rev:
+            rev = repo[repo[None].branch()].rev()
+        mergemod._checkunknown(repo, repo[None], repo[rev])
+
+    if clean:
         ret = hg.clean(repo, rev)
     else:
         ret = hg.update(repo, rev)
--- a/mercurial/context.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/context.py	Sun Mar 04 17:14:30 2012 -0600
@@ -611,11 +611,12 @@
 
         return None
 
-    def ancestors(self):
+    def ancestors(self, followfirst=False):
         visit = {}
         c = self
+        cut = followfirst and 1 or None
         while True:
-            for parent in c.parents():
+            for parent in c.parents()[:cut]:
                 visit[(parent.rev(), parent.node())] = parent
             if not visit:
                 break
@@ -715,9 +716,6 @@
     def _manifest(self):
         """generate a manifest corresponding to the working directory"""
 
-        if self._unknown is None:
-            self.status(unknown=True)
-
         man = self._parents[0].manifest().copy()
         if len(self._parents) > 1:
             man2 = self.p2().manifest()
@@ -731,8 +729,7 @@
         copied = self._repo.dirstate.copies()
         ff = self._flagfunc
         modified, added, removed, deleted = self._status
-        unknown = self._unknown
-        for i, l in (("a", added), ("m", modified), ("u", unknown)):
+        for i, l in (("a", added), ("m", modified)):
             for f in l:
                 orig = copied.get(f, f)
                 man[f] = getman(orig).get(orig, nullid) + i
@@ -934,9 +931,10 @@
         finally:
             wlock.release()
 
-    def ancestors(self):
+    def ancestors(self, followfirst=False):
+        cut = followfirst and 1 or None
         for a in self._repo.changelog.ancestors(
-            *[p.rev() for p in self._parents]):
+            *[p.rev() for p in self._parents[:cut]]):
             yield changectx(self._repo, a)
 
     def undelete(self, list):
--- a/mercurial/copies.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/copies.py	Sun Mar 04 17:14:30 2012 -0600
@@ -18,15 +18,6 @@
         return ""
     return f[:s]
 
-def _dirs(files):
-    d = set()
-    for f in files:
-        f = _dirname(f)
-        while f not in d:
-            d.add(f)
-            f = _dirname(f)
-    return d
-
 def _findlimit(repo, a, b):
     """Find the earliest revision that's an ancestor of a or b but not both,
     None if no such revision exists.
@@ -174,9 +165,18 @@
         return _backwardcopies(x, y)
     return _chain(x, y, _backwardcopies(x, a), _forwardcopies(a, y))
 
-def mergecopies(repo, c1, c2, ca, checkdirs=True):
+def mergecopies(repo, c1, c2, ca):
     """
-    Find moves and copies between context c1 and c2
+    Find moves and copies between context c1 and c2 that are relevant
+    for merging.
+
+    Returns two dicts, "copy" and "diverge".
+
+    "copy" is a mapping from destination name -> source name,
+    where source is in c1 and destination is in c2 or vice-versa.
+
+    "diverge" is a mapping of source name -> list of destination names
+    for divergent renames.
     """
     # avoid silly behavior for update from empty dir
     if not c1 or not c2 or c1 == c2:
@@ -301,14 +301,14 @@
             repo.ui.debug("   %s -> %s %s\n" % (f, fullcopy[f], note))
     del diverge2
 
-    if not fullcopy or not checkdirs:
+    if not fullcopy:
         return copy, diverge
 
     repo.ui.debug("  checking for directory renames\n")
 
     # generate a directory move map
-    d1, d2 = _dirs(m1), _dirs(m2)
-    invalid = set()
+    d1, d2 = c1.dirs(), c2.dirs()
+    invalid = set([""])
     dirmove = {}
 
     # examine each file copy for a potential directory move, which is
--- a/mercurial/encoding.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/encoding.py	Sun Mar 04 17:14:30 2012 -0600
@@ -190,3 +190,80 @@
         return s.upper() # we don't know how to fold this except in ASCII
     except LookupError, k:
         raise error.Abort(k, hint="please check your locale settings")
+
+def toutf8b(s):
+    '''convert a local, possibly-binary string into UTF-8b
+
+    This is intended as a generic method to preserve data when working
+    with schemes like JSON and XML that have no provision for
+    arbitrary byte strings. As Mercurial often doesn't know
+    what encoding data is in, we use so-called UTF-8b.
+
+    If a string is already valid UTF-8 (or ASCII), it passes unmodified.
+    Otherwise, unsupported bytes are mapped to UTF-16 surrogate range,
+    uDC00-uDCFF.
+
+    Principles of operation:
+
+    - ASCII and UTF-8 data sucessfully round-trips and is understood
+      by Unicode-oriented clients
+    - filenames and file contents in arbitrary other encodings can have
+      be round-tripped or recovered by clueful clients
+    - local strings that have a cached known UTF-8 encoding (aka
+      localstr) get sent as UTF-8 so Unicode-oriented clients get the
+      Unicode data they want
+    - because we must preserve UTF-8 bytestring in places such as
+      filenames, metadata can't be roundtripped without help
+
+    (Note: "UTF-8b" often refers to decoding a mix of valid UTF-8 and
+    arbitrary bytes into an internal Unicode format that can be
+    re-encoded back into the original. Here we are exposing the
+    internal surrogate encoding as a UTF-8 string.)
+    '''
+
+    if isinstance(s, localstr):
+        return s._utf8
+
+    try:
+        if s.decode('utf-8'):
+            return s
+    except UnicodeDecodeError:
+        # surrogate-encode any characters that don't round-trip
+        s2 = s.decode('utf-8', 'ignore').encode('utf-8')
+        r = ""
+        pos = 0
+        for c in s:
+            if s2[pos:pos + 1] == c:
+                r += c
+                pos += 1
+            else:
+                r += unichr(0xdc00 + ord(c)).encode('utf-8')
+        return r
+
+def fromutf8b(s):
+    '''Given a UTF-8b string, return a local, possibly-binary string.
+
+    return the original binary string. This
+    is a round-trip process for strings like filenames, but metadata
+    that's was passed through tolocal will remain in UTF-8.
+
+    >>> m = "\\xc3\\xa9\\x99abcd"
+    >>> n = toutf8b(m)
+    >>> n
+    '\\xc3\\xa9\\xed\\xb2\\x99abcd'
+    >>> fromutf8b(n) == m
+    True
+    '''
+
+    # fast path - look for uDxxx prefixes in s
+    if "\xed" not in s:
+        return s
+
+    u = s.decode("utf-8")
+    r = ""
+    for c in u:
+        if ord(c) & 0xff00 == 0xdc00:
+            r += chr(ord(c) & 0xff)
+        else:
+            r += c.encode("utf-8")
+    return r
--- a/mercurial/filemerge.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/filemerge.py	Sun Mar 04 17:14:30 2012 -0600
@@ -19,11 +19,21 @@
 def _toollist(ui, tool, part, default=[]):
     return ui.configlist("merge-tools", tool + "." + part, default)
 
-_internal = ['internal:' + s
-             for s in 'fail local other merge prompt dump'.split()]
+internals = {}
+
+def internaltool(name, trymerge, onfailure=None):
+    '''return a decorator for populating internal merge tool table'''
+    def decorator(func):
+        fullname = 'internal:' + name
+        func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
+        internals[fullname] = func
+        func.trymerge = trymerge
+        func.onfailure = onfailure
+        return func
+    return decorator
 
 def _findtool(ui, tool):
-    if tool in _internal:
+    if tool in internals:
         return tool
     for kn in ("regkey", "regkeyalt"):
         k = _toolstr(ui, tool, kn)
@@ -123,6 +133,131 @@
             if newdata != data:
                 util.writefile(file, newdata)
 
+@internaltool('prompt', False)
+def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
+    """Asks the user which of the local or the other version to keep as
+    the merged version."""
+    ui = repo.ui
+    fd = fcd.path()
+
+    if ui.promptchoice(_(" no tool found to merge %s\n"
+                         "keep (l)ocal or take (o)ther?") % fd,
+                       (_("&Local"), _("&Other")), 0):
+        return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
+    else:
+        return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
+
+@internaltool('local', False)
+def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
+    """Uses the local version of files as the merged version."""
+    return 0
+
+@internaltool('other', False)
+def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
+    """Uses the other version of files as the merged version."""
+    repo.wwrite(fcd.path(), fco.data(), fco.flags())
+    return 0
+
+@internaltool('fail', False)
+def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
+    """
+    Rather than attempting to merge files that were modified on both
+    branches, it marks them as unresolved. The resolve command must be
+    used to resolve these conflicts."""
+    return 1
+
+def _premerge(repo, toolconf, files):
+    tool, toolpath, binary, symlink = toolconf
+    a, b, c, back = files
+
+    ui = repo.ui
+
+    # do we attempt to simplemerge first?
+    try:
+        premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
+    except error.ConfigError:
+        premerge = _toolstr(ui, tool, "premerge").lower()
+        valid = 'keep'.split()
+        if premerge not in valid:
+            _valid = ', '.join(["'" + v + "'" for v in valid])
+            raise error.ConfigError(_("%s.premerge not valid "
+                                      "('%s' is neither boolean nor %s)") %
+                                    (tool, premerge, _valid))
+
+    if premerge:
+        r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
+        if not r:
+            ui.debug(" premerge successful\n")
+            return 0
+        if premerge != 'keep':
+            util.copyfile(back, a) # restore from backup and try again
+    return 1 # continue merging
+
+@internaltool('merge', True,
+              _("merging %s incomplete! "
+                "(edit conflicts, then use 'hg resolve --mark')\n"))
+def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files):
+    """
+    Uses the internal non-interactive simple merge algorithm for merging
+    files. It will fail if there are any conflicts and leave markers in
+    the partially merged file."""
+    r = _premerge(repo, toolconf, files)
+    if r:
+        a, b, c, back = files
+
+        ui = repo.ui
+
+        r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
+        return True, r
+    return False, 0
+
+@internaltool('dump', True)
+def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files):
+    """
+    Creates three versions of the files to merge, containing the
+    contents of local, other and base. These files can then be used to
+    perform a merge manually. If the file to be merged is named
+    ``a.txt``, these files will accordingly be named ``a.txt.local``,
+    ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
+    same directory as ``a.txt``."""
+    r = _premerge(repo, toolconf, files)
+    if r:
+        a, b, c, back = files
+
+        fd = fcd.path()
+
+        util.copyfile(a, a + ".local")
+        repo.wwrite(fd + ".other", fco.data(), fco.flags())
+        repo.wwrite(fd + ".base", fca.data(), fca.flags())
+    return False, r
+
+def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files):
+    r = _premerge(repo, toolconf, files)
+    if r:
+        tool, toolpath, binary, symlink = toolconf
+        a, b, c, back = files
+        out = ""
+        env = dict(HG_FILE=fcd.path(),
+                   HG_MY_NODE=short(mynode),
+                   HG_OTHER_NODE=str(fco.changectx()),
+                   HG_BASE_NODE=str(fca.changectx()),
+                   HG_MY_ISLINK='l' in fcd.flags(),
+                   HG_OTHER_ISLINK='l' in fco.flags(),
+                   HG_BASE_ISLINK='l' in fca.flags())
+
+        ui = repo.ui
+
+        args = _toolstr(ui, tool, "args", '$local $base $other')
+        if "$output" in args:
+            out, a = a, back # read input from backup, write to original
+        replace = dict(local=a, base=b, other=c, output=out)
+        args = util.interpolate(r'\$', replace, args,
+                                lambda s: '"%s"' % util.localpath(s))
+        r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env,
+                        out=ui.fout)
+        return True, r
+    return False, 0
+
 def filemerge(repo, mynode, orig, fcd, fco, fca):
     """perform a 3-way merge in the working directory
 
@@ -153,25 +288,23 @@
     ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
                (tool, fd, binary, symlink))
 
-    if not tool or tool == 'internal:prompt':
-        tool = "internal:local"
-        if ui.promptchoice(_(" no tool found to merge %s\n"
-                             "keep (l)ocal or take (o)ther?") % fd,
-                           (_("&Local"), _("&Other")), 0):
-            tool = "internal:other"
-    if tool == "internal:local":
-        return 0
-    if tool == "internal:other":
-        repo.wwrite(fd, fco.data(), fco.flags())
-        return 0
-    if tool == "internal:fail":
-        return 1
+    if tool in internals:
+        func = internals[tool]
+        trymerge = func.trymerge
+        onfailure = func.onfailure
+    else:
+        func = _xmerge
+        trymerge = True
+        onfailure = _("merging %s failed!\n")
 
-    # do the actual merge
+    toolconf = tool, toolpath, binary, symlink
+
+    if not trymerge:
+        return func(repo, mynode, orig, fcd, fco, fca, toolconf)
+
     a = repo.wjoin(fd)
     b = temp("base", fca)
     c = temp("other", fco)
-    out = ""
     back = a + ".orig"
     util.copyfile(a, back)
 
@@ -182,56 +315,18 @@
 
     ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
 
-    # do we attempt to simplemerge first?
-    try:
-        premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
-    except error.ConfigError:
-        premerge = _toolstr(ui, tool, "premerge").lower()
-        valid = 'keep'.split()
-        if premerge not in valid:
-            _valid = ', '.join(["'" + v + "'" for v in valid])
-            raise error.ConfigError(_("%s.premerge not valid "
-                                      "('%s' is neither boolean nor %s)") %
-                                    (tool, premerge, _valid))
-
-    if premerge:
-        r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
-        if not r:
-            ui.debug(" premerge successful\n")
+    needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf,
+                        (a, b, c, back))
+    if not needcheck:
+        if r:
+            if onfailure:
+                ui.warn(onfailure % fd)
+        else:
             os.unlink(back)
-            os.unlink(b)
-            os.unlink(c)
-            return 0
-        if premerge != 'keep':
-            util.copyfile(back, a) # restore from backup and try again
 
-    env = dict(HG_FILE=fd,
-               HG_MY_NODE=short(mynode),
-               HG_OTHER_NODE=str(fco.changectx()),
-               HG_BASE_NODE=str(fca.changectx()),
-               HG_MY_ISLINK='l' in fcd.flags(),
-               HG_OTHER_ISLINK='l' in fco.flags(),
-               HG_BASE_ISLINK='l' in fca.flags())
-
-    if tool == "internal:merge":
-        r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
-    elif tool == 'internal:dump':
-        a = repo.wjoin(fd)
-        util.copyfile(a, a + ".local")
-        repo.wwrite(fd + ".other", fco.data(), fco.flags())
-        repo.wwrite(fd + ".base", fca.data(), fca.flags())
         os.unlink(b)
         os.unlink(c)
-        return 1 # unresolved
-    else:
-        args = _toolstr(ui, tool, "args", '$local $base $other')
-        if "$output" in args:
-            out, a = a, back # read input from backup, write to original
-        replace = dict(local=a, base=b, other=c, output=out)
-        args = util.interpolate(r'\$', replace, args,
-                                lambda s: '"%s"' % util.localpath(s))
-        r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env,
-                        out=ui.fout)
+        return r
 
     if not r and (_toolbool(ui, tool, "checkconflicts") or
                   'conflicts' in _toollist(ui, tool, "check")):
@@ -248,24 +343,24 @@
 
     if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
                                   'changed' in _toollist(ui, tool, "check")):
-        if filecmp.cmp(repo.wjoin(fd), back):
+        if filecmp.cmp(a, back):
             if ui.promptchoice(_(" output file %s appears unchanged\n"
                                  "was merge successful (yn)?") % fd,
                                (_("&Yes"), _("&No")), 1):
                 r = 1
 
     if _toolbool(ui, tool, "fixeol"):
-        _matcheol(repo.wjoin(fd), back)
+        _matcheol(a, back)
 
     if r:
-        if tool == "internal:merge":
-            ui.warn(_("merging %s incomplete! "
-                      "(edit conflicts, then use 'hg resolve --mark')\n") % fd)
-        else:
-            ui.warn(_("merging %s failed!\n") % fd)
+        if onfailure:
+            ui.warn(onfailure % fd)
     else:
         os.unlink(back)
 
     os.unlink(b)
     os.unlink(c)
     return r
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = internals.values()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/formatter.py	Sun Mar 04 17:14:30 2012 -0600
@@ -0,0 +1,71 @@
+# formatter.py - generic output formatting for mercurial
+#
+# Copyright 2012 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+class baseformatter(object):
+    def __init__(self, ui, topic, opts):
+        self._ui = ui
+        self._topic = topic
+        self._style = opts.get("style")
+        self._template = opts.get("template")
+        self._item = None
+    def __bool__(self):
+        '''return False if we're not doing real templating so we can
+        skip extra work'''
+        return True
+    def _showitem(self):
+        '''show a formatted item once all data is collected'''
+        pass
+    def startitem(self):
+        '''begin an item in the format list'''
+        if self._item is not None:
+            self._showitem()
+        self._item = {}
+    def data(self, **data):
+        '''insert data into item that's not shown in default output'''
+    def write(self, fields, deftext, *fielddata, **opts):
+        '''do default text output while assigning data to item'''
+        for k, v in zip(fields.split(), fielddata):
+            self._item[k] = v
+    def plain(self, text, **opts):
+        '''show raw text for non-templated mode'''
+        pass
+    def end(self):
+        '''end output for the formatter'''
+        if self._item is not None:
+            self._showitem()
+
+class plainformatter(baseformatter):
+    '''the default text output scheme'''
+    def __init__(self, ui, topic, opts):
+        baseformatter.__init__(self, ui, topic, opts)
+    def __bool__(self):
+        return False
+    def startitem(self):
+        pass
+    def data(self, **data):
+        pass
+    def write(self, fields, deftext, *fielddata, **opts):
+        self._ui.write(deftext % fielddata, **opts)
+    def plain(self, text, **opts):
+        self._ui.write(text, **opts)
+    def end(self):
+        pass
+
+class debugformatter(baseformatter):
+    def __init__(self, ui, topic, opts):
+        baseformatter.__init__(self, ui, topic, opts)
+        self._ui.write("%s = {\n" % self._topic)
+    def _showitem(self):
+        self._ui.write("    " + repr(self._item) + ",\n")
+    def end(self):
+        baseformatter.end(self)
+        self._ui.write("}\n")
+
+def formatter(ui, topic, opts):
+    if ui.configbool('ui', 'formatdebug'):
+        return debugformatter(ui, topic, opts)
+    return plainformatter(ui, topic, opts)
--- a/mercurial/graphmod.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/graphmod.py	Sun Mar 04 17:14:30 2012 -0600
@@ -18,6 +18,7 @@
 """
 
 from mercurial.node import nullrev
+import util
 
 CHANGESET = 'C'
 
@@ -67,7 +68,7 @@
         parents = set([p.rev() for p in ctx.parents() if p.node() in include])
         yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
 
-def colored(dag):
+def colored(dag, repo):
     """annotates a DAG with colored edge information
 
     For each DAG node this function emits tuples::
@@ -83,6 +84,23 @@
     seen = []
     colors = {}
     newcolor = 1
+    config = {}
+
+    for key, val in repo.ui.configitems('graph'):
+        if '.' in key:
+            branch, setting = key.rsplit('.', 1)
+            # Validation
+            if setting == "width" and val.isdigit():
+                config.setdefault(branch, {})[setting] = int(val)
+            elif setting == "color" and val.isalnum():
+                config.setdefault(branch, {})[setting] = val
+
+    if config:
+        getconf = util.lrucachefunc(
+            lambda rev: config.get(repo[rev].branch(), {}))
+    else:
+        getconf = lambda rev: {}
+
     for (cur, type, data, parents) in dag:
 
         # Compute seen and next
@@ -111,10 +129,18 @@
         edges = []
         for ecol, eid in enumerate(seen):
             if eid in next:
-                edges.append((ecol, next.index(eid), colors[eid]))
+                bconf = getconf(eid)
+                edges.append((
+                    ecol, next.index(eid), colors[eid],
+                    bconf.get('width', -1),
+                    bconf.get('color', '')))
             elif eid == cur:
                 for p in parents:
-                    edges.append((ecol, next.index(p), color))
+                    bconf = getconf(p)
+                    edges.append((
+                        ecol, next.index(p), color,
+                        bconf.get('width', -1),
+                        bconf.get('color', '')))
 
         # Yield and move on
         yield (cur, type, data, (col, color), edges)
--- a/mercurial/help.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/help.py	Sun Mar 04 17:14:30 2012 -0600
@@ -7,7 +7,7 @@
 
 from i18n import gettext, _
 import sys, os
-import extensions, revset, fileset, templatekw, templatefilters
+import extensions, revset, fileset, templatekw, templatefilters, filemerge
 import util
 
 def listexts(header, exts, indent=1):
@@ -105,6 +105,7 @@
     addtopichook(topic, add)
 
 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
+addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
--- a/mercurial/help/config.txt	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/help/config.txt	Sun Mar 04 17:14:30 2012 -0600
@@ -489,24 +489,6 @@
   myfeature = ~/.hgext/myfeature.py
 
 
-``hostfingerprints``
-""""""""""""""""""""
-
-Fingerprints of the certificates of known HTTPS servers.
-A HTTPS connection to a server with a fingerprint configured here will
-only succeed if the servers certificate matches the fingerprint.
-This is very similar to how ssh known hosts works.
-The fingerprint is the SHA-1 hash value of the DER encoded certificate.
-The CA chain and web.cacerts is not used for servers with a fingerprint.
-
-For example::
-
-    [hostfingerprints]
-    hg.intevation.org = 38:76:52:7c:87:26:9a:8f:4a:f8:d3:de:08:45:3b:ea:d6:4b:ee:cc
-
-This feature is only supported when using Python 2.6 or later.
-
-
 ``format``
 """"""""""
 
@@ -534,119 +516,33 @@
     option ensures that the on-disk format of newly created
     repositories will be compatible with Mercurial before version 1.7.
 
-``merge-patterns``
-""""""""""""""""""
-
-This section specifies merge tools to associate with particular file
-patterns. Tools matched here will take precedence over the default
-merge tool. Patterns are globs by default, rooted at the repository
-root.
+``graph``
+"""""""""
 
-Example::
+Web graph view configuration. This section let you change graph
+elements display properties by branches, for instance to make the
+``default`` branch stand out.
 
-  [merge-patterns]
-  **.c = kdiff3
-  **.jpg = myimgmerge
-
-``merge-tools``
-"""""""""""""""
+Each line has the following format::
 
-This section configures external merge tools to use for file-level
-merges.
+    <branch>.<argument> = <value>
 
-Example ``~/.hgrc``::
+where ``<branch>`` is the name of the branch being
+customized. Example::
 
-  [merge-tools]
-  # Override stock tool location
-  kdiff3.executable = ~/bin/kdiff3
-  # Specify command line
-  kdiff3.args = $base $local $other -o $output
-  # Give higher priority
-  kdiff3.priority = 1
-
-  # Define new tool
-  myHtmlTool.args = -m $local $other $base $output
-  myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
-  myHtmlTool.priority = 1
+    [graph]
+    # 2px width
+    default.width = 2
+    # red color
+    default.color = FF0000
 
 Supported arguments:
 
-``priority``
-  The priority in which to evaluate this tool.
-  Default: 0.
-
-``executable``
-  Either just the name of the executable or its pathname.  On Windows,
-  the path can use environment variables with ${ProgramFiles} syntax.
-  Default: the tool name.
-
-``args``
-  The arguments to pass to the tool executable. You can refer to the
-  files being merged as well as the output file through these
-  variables: ``$base``, ``$local``, ``$other``, ``$output``.
-  Default: ``$local $base $other``
-
-``premerge``
-  Attempt to run internal non-interactive 3-way merge tool before
-  launching external tool.  Options are ``true``, ``false``, or ``keep``
-  to leave markers in the file if the premerge fails.
-  Default: True
-
-``binary``
-  This tool can merge binary files. Defaults to False, unless tool
-  was selected by file pattern match.
-
-``symlink``
-  This tool can merge symlinks. Defaults to False, even if tool was
-  selected by file pattern match.
-
-``check``
-  A list of merge success-checking options:
+``width``
+    Set branch edges width in pixels.
 
-  ``changed``
-    Ask whether merge was successful when the merged file shows no changes.
-  ``conflicts``
-    Check whether there are conflicts even though the tool reported success.
-  ``prompt``
-    Always prompt for merge success, regardless of success reported by tool.
-
-``checkchanged``
-  True is equivalent to ``check = changed``.
-  Default: False
-
-``checkconflicts``
-  True is equivalent to ``check = conflicts``.
-  Default: False
-
-``fixeol``
-  Attempt to fix up EOL changes caused by the merge tool.
-  Default: False
-
-``gui``
-  This tool requires a graphical interface to run. Default: False
-
-``regkey``
-  Windows registry key which describes install location of this
-  tool. Mercurial will search for this key first under
-  ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
-  Default: None
-
-``regkeyalt``
-  An alternate Windows registry key to try if the first key is not
-  found.  The alternate key uses the same ``regname`` and ``regappend``
-  semantics of the primary key.  The most common use for this key
-  is to search for 32bit applications on 64bit operating systems.
-  Default: None
-
-``regname``
-  Name of value to read from specified registry key. Defaults to the
-  unnamed (default) value.
-
-``regappend``
-  String to append to the value read from the registry, typically
-  the executable name of the tool.
-  Default: None
-
+``color``
+    Set branch edges color in hexadecimal RGB notation.
 
 ``hooks``
 """""""""
@@ -827,6 +723,24 @@
 is treated as a failure.
 
 
+``hostfingerprints``
+""""""""""""""""""""
+
+Fingerprints of the certificates of known HTTPS servers.
+A HTTPS connection to a server with a fingerprint configured here will
+only succeed if the servers certificate matches the fingerprint.
+This is very similar to how ssh known hosts works.
+The fingerprint is the SHA-1 hash value of the DER encoded certificate.
+The CA chain and web.cacerts is not used for servers with a fingerprint.
+
+For example::
+
+    [hostfingerprints]
+    hg.intevation.org = 38:76:52:7c:87:26:9a:8f:4a:f8:d3:de:08:45:3b:ea:d6:4b:ee:cc
+
+This feature is only supported when using Python 2.6 or later.
+
+
 ``http_proxy``
 """"""""""""""
 
@@ -851,33 +765,118 @@
     Optional. Always use the proxy, even for localhost and any entries
     in ``http_proxy.no``. True or False. Default: False.
 
-``smtp``
-""""""""
+``merge-patterns``
+""""""""""""""""""
+
+This section specifies merge tools to associate with particular file
+patterns. Tools matched here will take precedence over the default
+merge tool. Patterns are globs by default, rooted at the repository
+root.
+
+Example::
 
-Configuration for extensions that need to send email messages.
+  [merge-patterns]
+  **.c = kdiff3
+  **.jpg = myimgmerge
+
+``merge-tools``
+"""""""""""""""
+
+This section configures external merge tools to use for file-level
+merges.
+
+Example ``~/.hgrc``::
 
-``host``
-    Host name of mail server, e.g. "mail.example.com".
+  [merge-tools]
+  # Override stock tool location
+  kdiff3.executable = ~/bin/kdiff3
+  # Specify command line
+  kdiff3.args = $base $local $other -o $output
+  # Give higher priority
+  kdiff3.priority = 1
+
+  # Define new tool
+  myHtmlTool.args = -m $local $other $base $output
+  myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
+  myHtmlTool.priority = 1
+
+Supported arguments:
 
-``port``
-    Optional. Port to connect to on mail server. Default: 25.
+``priority``
+  The priority in which to evaluate this tool.
+  Default: 0.
+
+``executable``
+  Either just the name of the executable or its pathname.  On Windows,
+  the path can use environment variables with ${ProgramFiles} syntax.
+  Default: the tool name.
+
+``args``
+  The arguments to pass to the tool executable. You can refer to the
+  files being merged as well as the output file through these
+  variables: ``$base``, ``$local``, ``$other``, ``$output``.
+  Default: ``$local $base $other``
 
-``tls``
-    Optional. Method to enable TLS when connecting to mail server: starttls,
-    smtps or none. Default: none.
+``premerge``
+  Attempt to run internal non-interactive 3-way merge tool before
+  launching external tool.  Options are ``true``, ``false``, or ``keep``
+  to leave markers in the file if the premerge fails.
+  Default: True
+
+``binary``
+  This tool can merge binary files. Defaults to False, unless tool
+  was selected by file pattern match.
+
+``symlink``
+  This tool can merge symlinks. Defaults to False, even if tool was
+  selected by file pattern match.
 
-``username``
-    Optional. User name for authenticating with the SMTP server.
-    Default: none.
+``check``
+  A list of merge success-checking options:
+
+  ``changed``
+    Ask whether merge was successful when the merged file shows no changes.
+  ``conflicts``
+    Check whether there are conflicts even though the tool reported success.
+  ``prompt``
+    Always prompt for merge success, regardless of success reported by tool.
+
+``checkchanged``
+  True is equivalent to ``check = changed``.
+  Default: False
 
-``password``
-    Optional. Password for authenticating with the SMTP server. If not
-    specified, interactive sessions will prompt the user for a
-    password; non-interactive sessions will fail. Default: none.
+``checkconflicts``
+  True is equivalent to ``check = conflicts``.
+  Default: False
+
+``fixeol``
+  Attempt to fix up EOL changes caused by the merge tool.
+  Default: False
+
+``gui``
+  This tool requires a graphical interface to run. Default: False
 
-``local_hostname``
-    Optional. It's the hostname that the sender can use to identify
-    itself to the MTA.
+``regkey``
+  Windows registry key which describes install location of this
+  tool. Mercurial will search for this key first under
+  ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
+  Default: None
+
+``regkeyalt``
+  An alternate Windows registry key to try if the first key is not
+  found.  The alternate key uses the same ``regname`` and ``regappend``
+  semantics of the primary key.  The most common use for this key
+  is to search for 32bit applications on 64bit operating systems.
+  Default: None
+
+``regname``
+  Name of value to read from specified registry key. Defaults to the
+  unnamed (default) value.
+
+``regappend``
+  String to append to the value read from the registry, typically
+  the executable name of the tool.
+  Default: None
 
 
 ``patch``
@@ -985,6 +984,35 @@
     checking that all new file revisions specified in manifests are
     present. Default is False.
 
+``smtp``
+""""""""
+
+Configuration for extensions that need to send email messages.
+
+``host``
+    Host name of mail server, e.g. "mail.example.com".
+
+``port``
+    Optional. Port to connect to on mail server. Default: 25.
+
+``tls``
+    Optional. Method to enable TLS when connecting to mail server: starttls,
+    smtps or none. Default: none.
+
+``username``
+    Optional. User name for authenticating with the SMTP server.
+    Default: none.
+
+``password``
+    Optional. Password for authenticating with the SMTP server. If not
+    specified, interactive sessions will prompt the user for a
+    password; non-interactive sessions will fail. Default: none.
+
+``local_hostname``
+    Optional. It's the hostname that the sender can use to identify
+    itself to the MTA.
+
+
 ``subpaths``
 """"""""""""
 
--- a/mercurial/help/merge-tools.txt	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/help/merge-tools.txt	Sun Mar 04 17:14:30 2012 -0600
@@ -34,33 +34,7 @@
 There are some internal merge tools which can be used. The internal
 merge tools are:
 
-``internal:merge``
-   Uses the internal non-interactive simple merge algorithm for merging
-   files. It will fail if there are any conflicts and leave markers in
-   the partially merged file.
-
-``internal:fail``
-   Rather than attempting to merge files that were modified on both
-   branches, it marks them as unresolved. The resolve command must be
-   used to resolve these conflicts.
-
-``internal:local``
-   Uses the local version of files as the merged version.
-
-``internal:other``
-   Uses the other version of files as the merged version.
-
-``internal:prompt``
-   Asks the user which of the local or the other version to keep as
-   the merged version.
-
-``internal:dump``
-   Creates three versions of the files to merge, containing the
-   contents of local, other and base. These files can then be used to
-   perform a merge manually. If the file to be merged is named
-   ``a.txt``, these files will accordingly be named ``a.txt.local``,
-   ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
-   same directory as ``a.txt``.
+.. internaltoolsmarker
 
 Internal tools are always available and do not require a GUI but will by default
 not handle symlinks or binary files.
--- a/mercurial/hgweb/webcommands.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/hgweb/webcommands.py	Sun Mar 04 17:14:30 2012 -0600
@@ -770,7 +770,7 @@
         startrev = uprev
 
     dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1))
-    tree = list(graphmod.colored(dag))
+    tree = list(graphmod.colored(dag, web.repo))
     canvasheight = (len(tree) + 1) * bg_height - 27
     data = []
     for (id, type, ctx, vtx, edges) in tree:
--- a/mercurial/localrepo.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/localrepo.py	Sun Mar 04 17:14:30 2012 -0600
@@ -1105,37 +1105,58 @@
 
             # check subrepos
             subs = []
-            removedsubs = set()
+            commitsubs = set()
+            newstate = wctx.substate.copy()
+            # only manage subrepos and .hgsubstate if .hgsub is present
             if '.hgsub' in wctx:
-                # only manage subrepos and .hgsubstate if .hgsub is present
+                # we'll decide whether to track this ourselves, thanks
+                if '.hgsubstate' in changes[0]:
+                    changes[0].remove('.hgsubstate')
+                if '.hgsubstate' in changes[2]:
+                    changes[2].remove('.hgsubstate')
+
+                # compare current state to last committed state
+                # build new substate based on last committed state
+                oldstate = wctx.p1().substate
+                for s in sorted(newstate.keys()):
+                    if not match(s):
+                        # ignore working copy, use old state if present
+                        if s in oldstate:
+                            newstate[s] = oldstate[s]
+                            continue
+                        if not force:
+                            raise util.Abort(
+                                _("commit with new subrepo %s excluded") % s)
+                    if wctx.sub(s).dirty(True):
+                        if not self.ui.configbool('ui', 'commitsubrepos'):
+                            raise util.Abort(
+                                _("uncommitted changes in subrepo %s") % s,
+                                hint=_("use --subrepos for recursive commit"))
+                        subs.append(s)
+                        commitsubs.add(s)
+                    else:
+                        bs = wctx.sub(s).basestate()
+                        newstate[s] = (newstate[s][0], bs, newstate[s][2])
+                        if oldstate.get(s, (None, None, None))[1] != bs:
+                            subs.append(s)
+
+                # check for removed subrepos
                 for p in wctx.parents():
-                    removedsubs.update(s for s in p.substate if match(s))
-                for s in wctx.substate:
-                    removedsubs.discard(s)
-                    if match(s) and wctx.sub(s).dirty():
-                        subs.append(s)
-                if (subs or removedsubs):
+                    r = [s for s in p.substate if s not in newstate]
+                    subs += [s for s in r if match(s)]
+                if subs:
                     if (not match('.hgsub') and
                         '.hgsub' in (wctx.modified() + wctx.added())):
                         raise util.Abort(
                             _("can't commit subrepos without .hgsub"))
-                    if '.hgsubstate' not in changes[0]:
-                        changes[0].insert(0, '.hgsubstate')
-                        if '.hgsubstate' in changes[2]:
-                            changes[2].remove('.hgsubstate')
+                    changes[0].insert(0, '.hgsubstate')
+
             elif '.hgsub' in changes[2]:
                 # clean up .hgsubstate when .hgsub is removed
                 if ('.hgsubstate' in wctx and
                     '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
                     changes[2].insert(0, '.hgsubstate')
 
-            if subs and not self.ui.configbool('ui', 'commitsubrepos', False):
-                changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
-                if changedsubs:
-                    raise util.Abort(_("uncommitted changes in subrepo %s")
-                                     % changedsubs[0],
-                                     hint=_("use --subrepos for recursive commit"))
-
             # make sure all explicit patterns are matched
             if not force and match.files():
                 matched = set(changes[0] + changes[1] + changes[2])
@@ -1171,16 +1192,15 @@
                 cctx._text = editor(self, cctx, subs)
             edited = (text != cctx._text)
 
-            # commit subs
-            if subs or removedsubs:
-                state = wctx.substate.copy()
-                for s in sorted(subs):
+            # commit subs and write new state
+            if subs:
+                for s in sorted(commitsubs):
                     sub = wctx.sub(s)
                     self.ui.status(_('committing subrepository %s\n') %
                         subrepo.subrelpath(sub))
                     sr = sub.commit(cctx._text, user, date)
-                    state[s] = (state[s][0], sr)
-                subrepo.writestate(self, state)
+                    newstate[s] = (newstate[s][0], sr)
+                subrepo.writestate(self, newstate)
 
             # Save commit message in case this transaction gets rolled back
             # (e.g. by a pretxncommit hook).  Leave the content alone on
--- a/mercurial/match.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/match.py	Sun Mar 04 17:14:30 2012 -0600
@@ -333,5 +333,5 @@
 
 def _anypats(patterns):
     for kind, name in patterns:
-        if kind in ('glob', 're', 'relglob', 'relre'):
+        if kind in ('glob', 're', 'relglob', 'relre', 'set'):
             return True
--- a/mercurial/merge.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/merge.py	Sun Mar 04 17:14:30 2012 -0600
@@ -81,22 +81,19 @@
             self.mark(dfile, 'r')
         return r
 
-def _checkunknown(wctx, mctx, folding):
+def _checkunknownfile(repo, wctx, mctx, f):
+    return (not repo.dirstate._ignore(f)
+        and os.path.exists(repo.wjoin(f))
+        and mctx[f].cmp(wctx[f]))
+
+def _checkunknown(repo, wctx, mctx):
     "check for collisions between unknown files and files in mctx"
-    if folding:
-        foldf = util.normcase
-    else:
-        foldf = lambda fn: fn
-    folded = {}
-    for fn in mctx:
-        folded[foldf(fn)] = fn
 
     error = False
-    for fn in wctx.unknown():
-        f = foldf(fn)
-        if f in folded and mctx[folded[f]].cmp(wctx[f]):
+    for f in mctx:
+        if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
             error = True
-            wctx._repo.ui.warn(_("%s: untracked file differs\n") % fn)
+            wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
     if error:
         raise util.Abort(_("untracked files in working directory differ "
                            "from files in requested revision"))
@@ -191,8 +188,7 @@
     elif pa == p2: # backwards
         pa = p1.p1()
     elif pa and repo.ui.configbool("merge", "followcopies", True):
-        dirs = repo.ui.configbool("merge", "followdirs", True)
-        copy, diverge = copies.mergecopies(repo, p1, p2, pa, dirs)
+        copy, diverge = copies.mergecopies(repo, p1, p2, pa)
         for of, fl in diverge.iteritems():
             act("divergent renames", "dr", of, fl)
 
@@ -248,7 +244,7 @@
                     act("prompt keep", "a", f)
             elif n[20:] == "a": # added, no remote
                 act("remote deleted", "f", f)
-            elif n[20:] != "u":
+            else:
                 act("other deleted", "r", f)
 
     for f, n in m2.iteritems():
@@ -268,7 +264,13 @@
                 act("remote moved to " + f, "m",
                     f2, f, f, fmerge(f2, f, f2), True)
         elif f not in ma:
-            act("remote created", "g", f, m2.flags(f))
+            if (not overwrite
+                and _checkunknownfile(repo, p1, p2, f)):
+                rflags = fmerge(f, f, f)
+                act("remote differs from untracked local",
+                    "m", f, f, f, rflags, False)
+            else:
+                act("remote created", "g", f, m2.flags(f))
         elif n != ma[f]:
             if repo.ui.promptchoice(
                 _("remote changed %s which local deleted\n"
@@ -542,6 +544,8 @@
             if not force and (wc.files() or wc.deleted()):
                 raise util.Abort(_("outstanding uncommitted changes"),
                                  hint=_("use 'hg status' to list changes"))
+            if not force:
+                _checkunknown(repo, wc, p2)
             for s in wc.substate:
                 if wc.sub(s).dirty():
                     raise util.Abort(_("outstanding uncommitted changes in "
@@ -558,14 +562,11 @@
                                    " --check to force update)"))
             else:
                 # Allow jumping branches if clean and specific rev given
-                overwrite = True
+                pa = p1
 
         ### calculate phase
         action = []
-        wc.status(unknown=True) # prime cache
         folding = not util.checkcase(repo.path)
-        if not force:
-            _checkunknown(wc, p2, folding)
         if folding:
             _checkcollision(p2, branchmerge and p1)
         action += _forgetremoved(wc, p2, branchmerge)
--- a/mercurial/revset.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/revset.py	Sun Mar 04 17:14:30 2012 -0600
@@ -112,7 +112,7 @@
 
 def getargs(x, min, max, err):
     l = getlist(x)
-    if len(l) < min or len(l) > max:
+    if len(l) < min or (max >= 0 and len(l) > max):
         raise error.ParseError(err)
     return l
 
@@ -441,29 +441,45 @@
     """
     return limit(repo, subset, x)
 
+def _follow(repo, subset, x, name, followfirst=False):
+    l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
+    c = repo['.']
+    if l:
+        x = getstring(l[0], _("%s expected a filename") % name)
+        if x in c:
+            cx = c[x]
+            s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
+            # include the revision responsible for the most recent version
+            s.add(cx.linkrev())
+        else:
+            return []
+    else:
+        cut = followfirst and 1 or None
+        cl = repo.changelog
+        s = set()
+        visit = [c.rev()]
+        while visit:
+            for prev in cl.parentrevs(visit.pop(0))[:cut]:
+                if prev not in s and prev != nodemod.nullrev:
+                    visit.append(prev)
+                    s.add(prev)
+        s.add(c.rev())
+
+    return [r for r in subset if r in s]
+
 def follow(repo, subset, x):
     """``follow([file])``
     An alias for ``::.`` (ancestors of the working copy's first parent).
     If a filename is specified, the history of the given file is followed,
     including copies.
     """
-    # i18n: "follow" is a keyword
-    l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
-    c = repo['.']
-    if l:
-        x = getstring(l[0], _("follow expected a filename"))
-        if x in c:
-            cx = c[x]
-            s = set(ctx.rev() for ctx in cx.ancestors())
-            # include the revision responsible for the most recent version
-            s.add(cx.linkrev())
-        else:
-            return []
-    else:
-        s = set(repo.changelog.ancestors(c.rev()))
-        s.add(c.rev())
+    return _follow(repo, subset, x, 'follow')
 
-    return [r for r in subset if r in s]
+def _followfirst(repo, subset, x):
+    # ``followfirst([file])``
+    # Like ``follow([file])`` but follows only the first parent of
+    # every revision or file revision.
+    return _follow(repo, subset, x, '_followfirst', followfirst=True)
 
 def getall(repo, subset, x):
     """``all()``
@@ -493,23 +509,64 @@
                 break
     return l
 
+def _matchfiles(repo, subset, x):
+    # _matchfiles takes a revset list of prefixed arguments:
+    #
+    #   [p:foo, i:bar, x:baz]
+    #
+    # builds a match object from them and filters subset. Allowed
+    # prefixes are 'p:' for regular patterns, 'i:' for include
+    # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
+    # a revision identifier, or the empty string to reference the
+    # working directory, from which the match object is
+    # initialized. At most one 'r:' argument can be passed.
+
+    # i18n: "_matchfiles" is a keyword
+    l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
+    pats, inc, exc = [], [], []
+    hasset = False
+    rev = None
+    for arg in l:
+        s = getstring(arg, _("_matchfiles requires string arguments"))
+        prefix, value = s[:2], s[2:]
+        if prefix == 'p:':
+            pats.append(value)
+        elif prefix == 'i:':
+            inc.append(value)
+        elif prefix == 'x:':
+            exc.append(value)
+        elif prefix == 'r:':
+            if rev is not None:
+                raise error.ParseError(_('_matchfiles expected at most one '
+                                         'revision'))
+            rev = value
+        else:
+            raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
+        if not hasset and matchmod.patkind(value) == 'set':
+            hasset = True
+    m = None
+    s = []
+    for r in subset:
+        c = repo[r]
+        if not m or (hasset and rev is None):
+            ctx = c
+            if rev is not None:
+                ctx = repo[rev or None]
+            m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
+                               exclude=exc, ctx=ctx)
+        for f in c.files():
+            if m(f):
+                s.append(r)
+                break
+    return s
+
 def hasfile(repo, subset, x):
     """``file(pattern)``
     Changesets affecting files matched by pattern.
     """
     # i18n: "file" is a keyword
     pat = getstring(x, _("file requires a pattern"))
-    m = None
-    s = []
-    for r in subset:
-        c = repo[r]
-        if not m or matchmod.patkind(pat) == 'set':
-            m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
-        for f in c.files():
-            if m(f):
-                s.append(r)
-                break
-    return s
+    return _matchfiles(repo, subset, ('string', 'p:' + pat))
 
 def head(repo, subset, x):
     """``head()``
@@ -936,6 +993,7 @@
     "filelog": filelog,
     "first": first,
     "follow": follow,
+    "_followfirst": _followfirst,
     "grep": grep,
     "head": head,
     "heads": heads,
@@ -943,6 +1001,7 @@
     "keyword": keyword,
     "last": last,
     "limit": limit,
+    "_matchfiles": _matchfiles,
     "max": maxrev,
     "merge": merge,
     "min": minrev,
@@ -1071,46 +1130,85 @@
         h = heads(default)
         b($1) = ancestors($1) - ancestors(default)
         '''
-        if isinstance(name, tuple): # parameter substitution
-            self.tree = name
-            self.replacement = value
-        else: # alias definition
-            m = self.funcre.search(name)
-            if m:
-                self.tree = ('func', ('symbol', m.group(1)))
-                self.args = [x.strip() for x in m.group(2).split(',')]
-                for arg in self.args:
-                    value = value.replace(arg, repr(arg))
-            else:
-                self.tree = ('symbol', name)
+        m = self.funcre.search(name)
+        if m:
+            self.name = m.group(1)
+            self.tree = ('func', ('symbol', m.group(1)))
+            self.args = [x.strip() for x in m.group(2).split(',')]
+            for arg in self.args:
+                value = value.replace(arg, repr(arg))
+        else:
+            self.name = name
+            self.tree = ('symbol', name)
+
+        self.replacement, pos = parse(value)
+        if pos != len(value):
+            raise error.ParseError(_('invalid token'), pos)
 
-            self.replacement, pos = parse(value)
-            if pos != len(value):
-                raise error.ParseError(_('invalid token'), pos)
+def _getalias(aliases, tree):
+    """If tree looks like an unexpanded alias, return it. Return None
+    otherwise.
+    """
+    if isinstance(tree, tuple) and tree:
+        if tree[0] == 'symbol' and len(tree) == 2:
+            name = tree[1]
+            alias = aliases.get(name)
+            if alias and alias.args is None and alias.tree == tree:
+                return alias
+        if tree[0] == 'func' and len(tree) > 1:
+            if tree[1][0] == 'symbol' and len(tree[1]) == 2:
+                name = tree[1][1]
+                alias = aliases.get(name)
+                if alias and alias.args is not None and alias.tree == tree[:2]:
+                    return alias
+    return None
 
-    def process(self, tree):
-        if isinstance(tree, tuple):
-            if self.args is None:
-                if tree == self.tree:
-                    return self.replacement
-            elif tree[:2] == self.tree:
-                l = getlist(tree[2])
-                if len(l) != len(self.args):
-                    raise error.ParseError(
-                        _('invalid number of arguments: %s') % len(l))
-                result = self.replacement
-                for a, v in zip(self.args, l):
-                    valalias = revsetalias(('string', a), v)
-                    result = valalias.process(result)
-                return result
-            return tuple(map(self.process, tree))
+def _expandargs(tree, args):
+    """Replace all occurences of ('string', name) with the
+    substitution value of the same name in args, recursively.
+    """
+    if not isinstance(tree, tuple):
+        return tree
+    if len(tree) == 2 and tree[0] == 'string':
+        return args.get(tree[1], tree)
+    return tuple(_expandargs(t, args) for t in tree)
+
+def _expandaliases(aliases, tree, expanding):
+    """Expand aliases in tree, recursively.
+
+    'aliases' is a dictionary mapping user defined aliases to
+    revsetalias objects.
+    """
+    if not isinstance(tree, tuple):
+        # Do not expand raw strings
         return tree
+    alias = _getalias(aliases, tree)
+    if alias is not None:
+        if alias in expanding:
+            raise error.ParseError(_('infinite expansion of revset alias "%s" '
+                                     'detected') % alias.name)
+        expanding.append(alias)
+        result = alias.replacement
+        if alias.args is not None:
+            l = getlist(tree[2])
+            if len(l) != len(alias.args):
+                raise error.ParseError(
+                    _('invalid number of arguments: %s') % len(l))
+            result = _expandargs(result, dict(zip(alias.args, l)))
+        # Recurse in place, the base expression may have been rewritten
+        result = _expandaliases(aliases, result, expanding)
+        expanding.pop()
+    else:
+        result = tuple(_expandaliases(aliases, t, expanding)
+                       for t in tree)
+    return result
 
 def findaliases(ui, tree):
+    aliases = {}
     for k, v in ui.configitems('revsetalias'):
         alias = revsetalias(k, v)
-        tree = alias.process(tree)
-    return tree
+        aliases[alias.name] = alias
+    return _expandaliases(aliases, tree, [])
 
 parse = parser.parser(tokenize, elements).parse
 
@@ -1221,5 +1319,20 @@
 
     return ret
 
+def prettyformat(tree):
+    def _prettyformat(tree, level, lines):
+        if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
+            lines.append((level, str(tree)))
+        else:
+            lines.append((level, '(%s' % tree[0]))
+            for s in tree[1:]:
+                _prettyformat(s, level + 1, lines)
+            lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
+
+    lines = []
+    _prettyformat(tree, 0, lines)
+    output = '\n'.join(('  '*l + s) for l, s in lines)
+    return output
+
 # tell hggettext to extract docstrings from these functions:
 i18nfunctions = symbols.values()
--- a/mercurial/scmutil.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/scmutil.py	Sun Mar 04 17:14:30 2012 -0600
@@ -582,7 +582,7 @@
         ret.append(p)
     return ret
 
-def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
+def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
     if pats == ("",):
         pats = []
     if not globbed and default == 'relpath':
@@ -593,7 +593,10 @@
     def badfn(f, msg):
         ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
     m.bad = badfn
-    return m
+    return m, pats
+
+def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
+    return matchandpats(ctx, pats, opts, globbed, default)[0]
 
 def matchall(repo):
     return matchmod.always(repo.root, repo.getcwd())
@@ -610,6 +613,9 @@
     added, unknown, deleted, removed = [], [], [], []
     audit_path = pathauditor(repo.root)
     m = match(repo[None], pats, opts)
+    rejected = []
+    m.bad = lambda x, y: rejected.append(x)
+
     for abs in repo.walk(m):
         target = repo.wjoin(abs)
         good = True
@@ -654,6 +660,11 @@
         finally:
             wlock.release()
 
+    for f in rejected:
+        if f in m.files():
+            return 1
+    return 0
+
 def updatedir(ui, repo, patches, similarity=0):
     '''Update dirstate after patch application according to metadata'''
     if not patches:
--- a/mercurial/subrepo.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/subrepo.py	Sun Mar 04 17:14:30 2012 -0600
@@ -275,6 +275,11 @@
         """
         raise NotImplementedError
 
+    def basestate(self):
+        """current working directory base state, disregarding .hgsubstate
+        state and working directory modifications"""
+        raise NotImplementedError
+
     def checknested(self, path):
         """check if path is a subrepository within this repository"""
         return False
@@ -446,6 +451,9 @@
             return True
         return w.dirty() # working directory changed
 
+    def basestate(self):
+        return self._repo['.'].hex()
+
     def checknested(self, path):
         return self._repo._checknested(self._repo.wjoin(path))
 
@@ -666,6 +674,9 @@
                 return False
         return True
 
+    def basestate(self):
+        return self._wcrev()
+
     def commit(self, text, user, date):
         # user and date are out of our hands since svn is centralized
         changed, extchanged = self._wcchanged()
@@ -907,6 +918,9 @@
         out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
         return code == 1
 
+    def basestate(self):
+        return self._gitstate()
+
     def get(self, state, overwrite=False):
         source, revision, kind = state
         if not revision:
--- a/mercurial/templates/gitweb/graph.tmpl	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/templates/gitweb/graph.tmpl	Sun Mar 04 17:14:30 2012 -0600
@@ -51,16 +51,6 @@
 var graph = new Graph();
 graph.scale({bg_height});
 
-graph.edge = function(x0, y0, x1, y1, color) \{
-	
-	this.setColor(color, 0.0, 0.65);
-	this.ctx.beginPath();
-	this.ctx.moveTo(x0, y0);
-	this.ctx.lineTo(x1, y1);
-	this.ctx.stroke();
-	
-}
-
 var revlink = '<li style="_STYLE"><span class="desc">';
 revlink += '<a class="list" href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID"><b>_DESC</b></a>';
 revlink += '</span> _TAGS';
--- a/mercurial/templates/monoblue/graph.tmpl	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/templates/monoblue/graph.tmpl	Sun Mar 04 17:14:30 2012 -0600
@@ -49,16 +49,6 @@
     var graph = new Graph();
     graph.scale({bg_height});
 
-    graph.edge = function(x0, y0, x1, y1, color) \{
-
-        this.setColor(color, 0.0, 0.65);
-        this.ctx.beginPath();
-        this.ctx.moveTo(x0, y0);
-        this.ctx.lineTo(x1, y1);
-        this.ctx.stroke();
-
-    }
-
     var revlink = '<li style="_STYLE"><span class="desc">';
     revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
     revlink += '</span>_TAGS<span class="info">_DATE, by _USER</span></li>';
--- a/mercurial/templates/paper/graph.tmpl	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/templates/paper/graph.tmpl	Sun Mar 04 17:14:30 2012 -0600
@@ -62,16 +62,6 @@
 var graph = new Graph();
 graph.scale({bg_height});
 
-graph.edge = function(x0, y0, x1, y1, color) \{
-	
-	this.setColor(color, 0.0, 0.65);
-	this.ctx.beginPath();
-	this.ctx.moveTo(x0, y0);
-	this.ctx.lineTo(x1, y1);
-	this.ctx.stroke();
-	
-}
-
 var revlink = '<li style="_STYLE"><span class="desc">';
 revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
 revlink += '</span>_TAGS<span class="info">_DATE, by _USER</span></li>';
--- a/mercurial/templates/spartan/graph.tmpl	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/templates/spartan/graph.tmpl	Sun Mar 04 17:14:30 2012 -0600
@@ -43,16 +43,6 @@
 var graph = new Graph();
 graph.scale({bg_height});
 
-graph.edge = function(x0, y0, x1, y1, color) \{
-	
-	this.setColor(color, 0.0, 0.65);
-	this.ctx.beginPath();
-	this.ctx.moveTo(x0, y0);
-	this.ctx.lineTo(x1, y1);
-	this.ctx.stroke();
-	
-}
-
 var revlink = '<li style="_STYLE"><span class="desc">';
 revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
 revlink += '</span><span class="info">_DATE, by _USER</span></li>';
--- a/mercurial/templates/static/mercurial.js	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/templates/static/mercurial.js	Sun Mar 04 17:14:30 2012 -0600
@@ -58,25 +58,44 @@
 		
 		// Set the colour.
 		//
-		// Picks a distinct colour based on an internal wheel; the bg
-		// parameter provides the value that should be assigned to the 'zero'
-		// colours and the fg parameter provides the multiplier that should be
-		// applied to the foreground colours.
-		
-		color %= colors.length;
-		var red = (colors[color][0] * fg) || bg;
-		var green = (colors[color][1] * fg) || bg;
-		var blue = (colors[color][2] * fg) || bg;
-		red = Math.round(red * 255);
-		green = Math.round(green * 255);
-		blue = Math.round(blue * 255);
-		var s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
+		// If color is a string, expect an hexadecimal RGB
+		// value and apply it unchanged. If color is a number,
+		// pick a distinct colour based on an internal wheel;
+		// the bg parameter provides the value that should be
+		// assigned to the 'zero' colours and the fg parameter
+		// provides the multiplier that should be applied to
+		// the foreground colours.
+		var s;
+		if(typeof color == "string") {
+			s = "#" + color;
+		} else { //typeof color == "number"
+			color %= colors.length;
+			var red = (colors[color][0] * fg) || bg;
+			var green = (colors[color][1] * fg) || bg;
+			var blue = (colors[color][2] * fg) || bg;
+			red = Math.round(red * 255);
+			green = Math.round(green * 255);
+			blue = Math.round(blue * 255);
+			s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
+		}
 		this.ctx.strokeStyle = s;
 		this.ctx.fillStyle = s;
 		return s;
 		
 	}
 
+	this.edge = function(x0, y0, x1, y1, color, width) {
+		
+		this.setColor(color, 0.0, 0.65);
+		if(width >= 0)
+			 this.ctx.lineWidth = width;
+		this.ctx.beginPath();
+		this.ctx.moveTo(x0, y0);
+		this.ctx.lineTo(x1, y1);
+		this.ctx.stroke();
+		
+	}
+
 	this.render = function(data) {
 		
 		var backgrounds = '';
@@ -93,13 +112,20 @@
 			var edges = cur[2];
 			var fold = false;
 			
+			var prevWidth = this.ctx.lineWidth;
 			for (var j in edges) {
 				
 				line = edges[j];
 				start = line[0];
 				end = line[1];
 				color = line[2];
-
+				var width = line[3];
+				if(width < 0)
+					 width = prevWidth;
+				var branchcolor = line[4];
+				if(branchcolor)
+					color = branchcolor;
+				
 				if (end > this.columns || start > this.columns) {
 					this.columns += 1;
 				}
@@ -113,9 +139,10 @@
 				x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
 				y1 = this.bg[1] + this.bg_height / 2;
 				
-				this.edge(x0, y0, x1, y1, color);
+				this.edge(x0, y0, x1, y1, color, width);
 				
 			}
+			this.ctx.lineWidth = prevWidth;
 			
 			// Draw the revision node in the right column
 			
--- a/mercurial/ui.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/ui.py	Sun Mar 04 17:14:30 2012 -0600
@@ -7,7 +7,7 @@
 
 from i18n import _
 import errno, getpass, os, socket, sys, tempfile, traceback
-import config, scmutil, util, error
+import config, scmutil, util, error, formatter
 
 class ui(object):
     def __init__(self, src=None):
@@ -46,6 +46,9 @@
     def copy(self):
         return self.__class__(self)
 
+    def formatter(self, topic, opts):
+        return formatter.formatter(self, topic, opts)
+
     def _trusted(self, fp, f):
         st = util.fstat(fp)
         if util.isowner(st):
--- a/mercurial/windows.py	Sun Mar 04 17:12:12 2012 -0600
+++ b/mercurial/windows.py	Sun Mar 04 17:14:30 2012 -0600
@@ -123,7 +123,7 @@
         msvcrt.setmode(fno(), os.O_BINARY)
 
 def pconvert(path):
-    return '/'.join(path.split(os.sep))
+    return path.replace(os.sep, '/')
 
 def localpath(path):
     return path.replace('/', '\\')
--- a/tests/bzr-definitions	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/bzr-definitions	Sun Mar 04 17:14:30 2012 -0600
@@ -9,7 +9,7 @@
 
 glog()
 {
-    hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
+    hg glog --template '{rev}@{branch} "{desc|firstline}" files: {files}\n' "$@"
 }
 
 manifest()
--- a/tests/test-check-code-hg.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-check-code-hg.t	Sun Mar 04 17:14:30 2012 -0600
@@ -477,9 +477,6 @@
    >     except:
    warning: naked except clause
   mercurial/localrepo.py:0:
-   >                                      hint=_("use --subrepos for recursive commit"))
-   warning: line over 80 characters
-  mercurial/localrepo.py:0:
    >                         # we return an integer indicating remote head count change
    warning: line over 80 characters
   mercurial/localrepo.py:0:
--- a/tests/test-convert-bzr-ghosts.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-convert-bzr-ghosts.t	Sun Mar 04 17:14:30 2012 -0600
@@ -30,7 +30,7 @@
   1 Initial layout setup
   0 Commit with ghost revision
   $ glog -R source-hg
-  o  1 "Commit with ghost revision" files: somefile
+  o  1@source "Commit with ghost revision" files: somefile
   |
-  o  0 "Initial layout setup" files: somefile
+  o  0@source "Initial layout setup" files: somefile
   
--- a/tests/test-convert-bzr-merges.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-convert-bzr-merges.t	Sun Mar 04 17:14:30 2012 -0600
@@ -48,17 +48,17 @@
   1 Added brach2 file
   0 Merged branches
   $ glog -R source-hg
-  o    5 "(octopus merge fixup)" files:
+  o    5@source "(octopus merge fixup)" files:
   |\
-  | o    4 "Merged branches" files: file-branch2
+  | o    4@source "Merged branches" files: file-branch2
   | |\
-  o---+  3 "Added brach2 file" files: file-branch2
+  o---+  3@source-branch2 "Added brach2 file" files: file-branch2
    / /
-  | o  2 "Added parent file" files: file-parent
+  | o  2@source "Added parent file" files: file-parent
   | |
-  o |  1 "Added branch1 file" files: file file-branch1
+  o |  1@source-branch1 "Added branch1 file" files: file file-branch1
   |/
-  o  0 "Initial add" files: file
+  o  0@source "Initial add" files: file
   
   $ manifest source-hg tip
   % manifest of tip
--- a/tests/test-convert-bzr.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-convert-bzr.t	Sun Mar 04 17:14:30 2012 -0600
@@ -7,6 +7,17 @@
   $ mkdir test-createandrename
   $ cd test-createandrename
   $ bzr init -q source
+
+test empty repo conversion (issue3233)
+
+  $ hg convert source source-hg
+  initializing destination source-hg repository
+  scanning source...
+  sorting...
+  converting...
+
+back to the rename stuff
+
   $ cd source
   $ echo a > a
   $ echo c > c
@@ -25,16 +36,15 @@
   $ bzr commit -q -m 'rename a into b, create a, rename c into d'
   $ cd ..
   $ hg convert source source-hg
-  initializing destination source-hg repository
   scanning source...
   sorting...
   converting...
   1 Initial add: a, c, e
   0 rename a into b, create a, rename c into d
   $ glog -R source-hg
-  o  1 "rename a into b, create a, rename c into d" files: a b c d e f
+  o  1@source "rename a into b, create a, rename c into d" files: a b c d e f
   |
-  o  0 "Initial add: a, c, e" files: a c e
+  o  0@source "Initial add: a, c, e" files: a c e
   
 
 manifest
@@ -54,7 +64,7 @@
   converting...
   0 Initial add: a, c, e
   $ glog -R source-1-hg
-  o  0 "Initial add: a, c, e" files: a c e
+  o  0@source "Initial add: a, c, e" files: a c e
   
 
 test with filemap
@@ -77,22 +87,12 @@
 convert from lightweight checkout
 
   $ bzr checkout --lightweight source source-light
-  $ hg convert source-light source-light-hg
+  $ hg convert -s bzr source-light source-light-hg
   initializing destination source-light-hg repository
   warning: lightweight checkouts may cause conversion failures, try with a regular branch instead.
-  scanning source...
-  sorting...
-  converting...
-  1 Initial add: a, c, e
-  0 rename a into b, create a, rename c into d
-
-lightweight manifest
-
-  $ hg manifest -R source-light-hg -r tip
-  a
-  b
-  d
-  f
+  $TESTTMP/test-createandrename/source-light does not look like a Bazaar repository
+  abort: source-light: missing or unsupported repository
+  [255]
 
 extract timestamps that look just like hg's {date|isodate}:
 yyyy-mm-dd HH:MM zzzz (no seconds!)
@@ -147,13 +147,13 @@
   1 Editing b
   0 Merged improve branch
   $ glog -R source-hg
-  o    3 "Merged improve branch" files:
+  o    3@source "Merged improve branch" files:
   |\
-  | o  2 "Editing b" files: b
+  | o  2@source-improve "Editing b" files: b
   | |
-  o |  1 "Editing a" files: a
+  o |  1@source "Editing a" files: a
   |/
-  o  0 "Initial add" files: a b
+  o  0@source "Initial add" files: a b
   
   $ cd ..
 
@@ -208,3 +208,77 @@
   $ hg cat syma; echo
   a
 
+Multiple branches
+
+  $ bzr init-repo -q --no-trees repo
+  $ bzr init -q repo/trunk
+  $ bzr co repo/trunk repo-trunk
+  $ cd repo-trunk
+  $ echo a > a
+  $ bzr add a
+  adding a
+  $ bzr ci -qm adda --commit-time '2012-01-01 00:00:01 +0000'
+  $ bzr tag trunk-tag
+  Created tag trunk-tag.
+  $ bzr switch -b branch
+  Tree is up to date at revision 1.
+  Switched to branch: *repo/branch/ (glob)
+  $ echo b > b
+  $ bzr add b
+  adding b
+  $ bzr ci -qm addb --commit-time '2012-01-01 00:00:02 +0000'
+  $ bzr tag branch-tag
+  Created tag branch-tag.
+  $ bzr switch --force ../repo/trunk
+  Updated to revision 1.
+  Switched to branch: */repo/trunk/ (glob)
+  $ echo a >> a
+  $ bzr ci -qm changea --commit-time '2012-01-01 00:00:03 +0000'
+  $ cd ..
+  $ hg convert --datesort repo repo-bzr
+  initializing destination repo-bzr repository
+  scanning source...
+  sorting...
+  converting...
+  2 adda
+  1 addb
+  0 changea
+  updating tags
+  $ (cd repo-bzr; glog)
+  o  3@default "update tags" files: .hgtags
+  |
+  o  2@default "changea" files: a
+  |
+  | o  1@branch "addb" files: b
+  |/
+  o  0@default "adda" files: a
+  
+
+Test tags (converted identifiers are not stable because bzr ones are
+not and get incorporated in extra fields).
+
+  $ hg -R repo-bzr tags
+  tip                                3:* (glob)
+  branch-tag                         1:* (glob)
+  trunk-tag                          0:* (glob)
+
+Nested repositories (issue3254)
+
+  $ bzr init-repo -q --no-trees repo/inner
+  $ bzr init -q repo/inner/trunk
+  $ bzr co repo/inner/trunk inner-trunk
+  $ cd inner-trunk
+  $ echo b > b
+  $ bzr add b
+  adding b
+  $ bzr ci -qm addb
+  $ cd ..
+  $ hg convert --datesort repo noinner-bzr
+  initializing destination noinner-bzr repository
+  scanning source...
+  sorting...
+  converting...
+  2 adda
+  1 addb
+  0 changea
+  updating tags
--- a/tests/test-glog.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-glog.t	Sun Mar 04 17:14:30 2012 -0600
@@ -83,8 +83,26 @@
   >   hg commit -Aqd "$rev 0" -m "($rev) $msg"
   > }
 
+  $ cat > printrevset.py <<EOF
+  > from mercurial import extensions, revset, commands
+  > from hgext import graphlog
+  >  
+  > def uisetup(ui):
+  >     def printrevset(orig, ui, repo, *pats, **opts):
+  >         if opts.get('print_revset'):
+  >             expr = graphlog.revset(repo, pats, opts)[0]
+  >             tree = revset.parse(expr)[0]
+  >             ui.write(tree, "\n")
+  >             return 0
+  >         return orig(ui, repo, *pats, **opts)
+  >     entry = extensions.wrapcommand(commands.table, 'log', printrevset)
+  >     entry[1].append(('', 'print-revset', False,
+  >                      'print generated revset and exit (DEPRECATED)'))
+  > EOF
+
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "graphlog=" >> $HGRCPATH
+  $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
 
   $ hg init repo
   $ cd repo
@@ -1359,9 +1377,13 @@
 
 Do not crash or produce strange graphs if history is buggy
 
+  $ hg branch branch
+  marked working directory as branch branch
+  (branches are permanent and global, did you want a bookmark?)
   $ commit 36 "buggy merge: identical parents" 35 35
   $ hg glog -l5
-  @  changeset:   36:95fa8febd08a
+  @  changeset:   36:08a19a744424
+  |  branch:      branch
   |  tag:         tip
   |  parent:      35:9159c3644c5e
   |  parent:      35:9159c3644c5e
@@ -1396,76 +1418,336 @@
 
 Test log -G options
 
-  $ hg log -G -u 'something nice'
-  $ hg log -G -b 'something nice'
-  abort: unknown revision 'something nice'!
-  [255]
-  $ hg log -G -k 'something nice'
-  $ hg log -G --only-branch 'something nice'
-  abort: unknown revision 'something nice'!
-  [255]
-  $ hg log -G --include 'some file' --exclude 'another file'
-  $ hg log -G --follow  --template 'nodetag {rev}\n' | grep nodetag | wc -l
-  \s*36 (re)
-  $ hg log -G --removed --template 'nodetag {rev}\n' | grep nodetag | wc -l
-  \s*0 (re)
-  $ hg log -G --only-merges --template 'nodetag {rev}\n' | grep nodetag | wc -l
-  \s*28 (re)
-  $ hg log -G --no-merges --template 'nodetag {rev}\n'
-  o  nodetag 35
-  |
-  o    nodetag 34
-  |\
-  | \
-  | |\
-  | | \
-  | | |\
-  | | | \
-  | | | |\
-  | | | | \
-  | | | | |\
-  +-+-+-+-----o  nodetag 33
-  | | | | | |
-  +---------o  nodetag 29
-  | | | | |
-  +-+-+---o  nodetag 27
-  | | | |/
-  | | | o  nodetag 3
-  | | |/
-  | | o  nodetag 2
-  | |/
-  | o  nodetag 1
-  |/
-  o  nodetag 0
-  
+  $ testlog() {
+  >   hg log -G --print-revset "$@"
+  >   hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
+  >     | sed 's/.*nodetag/nodetag/' > log.nodes
+  >   hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
+  >     | sed 's/.*nodetag/nodetag/' > glog.nodes
+  >   diff -u log.nodes glog.nodes
+  > }
+
+glog always reorders nodes which explains the difference with log
+
+  $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
+  ('group', ('group', ('or', ('or', ('or', ('or', ('or', ('symbol', '27'), ('symbol', '25')), ('symbol', '21')), ('symbol', '34')), ('symbol', '32')), ('symbol', '31'))))
+  --- log.nodes	* (glob)
+  +++ glog.nodes	* (glob)
+  @@ -1,6 +1,6 @@
+  -nodetag 27
+  -nodetag 25
+  -nodetag 21
+   nodetag 34
+   nodetag 32
+   nodetag 31
+  +nodetag 27
+  +nodetag 25
+  +nodetag 21
+  [1]
+  $ testlog -u test -u not-a-user
+  ('group', ('group', ('or', ('func', ('symbol', 'user'), ('string', 'test')), ('func', ('symbol', 'user'), ('string', 'not-a-user')))))
+  $ testlog -b not-a-branch
+  ('group', ('group', ('func', ('symbol', 'branch'), ('string', 'not-a-branch'))))
+  abort: unknown revision 'not-a-branch'!
+  abort: unknown revision 'not-a-branch'!
+  $ testlog -b default -b branch --only-branch branch
+  ('group', ('group', ('or', ('or', ('func', ('symbol', 'branch'), ('string', 'default')), ('func', ('symbol', 'branch'), ('string', 'branch'))), ('func', ('symbol', 'branch'), ('string', 'branch')))))
+  $ testlog -k expand -k merge
+  ('group', ('group', ('or', ('func', ('symbol', 'keyword'), ('string', 'expand')), ('func', ('symbol', 'keyword'), ('string', 'merge')))))
+  $ testlog --only-merges
+  ('group', ('func', ('symbol', 'merge'), None))
+  $ testlog --no-merges
+  ('group', ('not', ('func', ('symbol', 'merge'), None)))
+  $ testlog --date '2 0 to 4 0'
+  ('group', ('func', ('symbol', 'date'), ('string', '2 0 to 4 0')))
   $ hg log -G -d 'brace ) in a date'
   abort: invalid date: 'brace ) in a date'
   [255]
-  $ hg log -G -P 32 --template '{rev}\n'
-  @  36
+  $ testlog --prune 31 --prune 32
+  ('group', ('group', ('and', ('not', ('group', ('or', ('string', '31'), ('func', ('symbol', 'ancestors'), ('string', '31'))))), ('not', ('group', ('or', ('string', '32'), ('func', ('symbol', 'ancestors'), ('string', '32'))))))))
+
+Dedicated repo for --follow and paths filtering. The g is crafted to
+have 2 filelog topological heads in a linear changeset graph.
+
+  $ cd ..
+  $ hg init follow
+  $ cd follow
+  $ echo a > a
+  $ echo aa > aa
+  $ echo f > f
+  $ hg ci -Am "add a"
+  adding a
+  adding aa
+  adding f
+  $ hg cp a b
+  $ hg cp f g
+  $ hg ci -m "copy a b"
+  $ mkdir dir
+  $ hg mv b dir
+  $ echo g >> g
+  $ echo f >> f
+  $ hg ci -m "mv b dir/b"
+  $ hg mv a b
+  $ hg cp -f f g
+  $ echo a > d
+  $ hg add d
+  $ hg ci -m "mv a b; add d"
+  $ hg mv dir/b e
+  $ hg ci -m "mv dir/b e"
+  $ hg glog --template '({rev}) {desc|firstline}\n'
+  @  (4) mv dir/b e
   |
-  o  35
+  o  (3) mv a b; add d
+  |
+  o  (2) mv b dir/b
+  |
+  o  (1) copy a b
   |
-  o  34
-  |
-  | o  33
-  | |
-  $ hg log -G --follow a
-  abort: -G/--graph option is incompatible with --follow with file argument
-  [255]
+  o  (0) add a
+  
+
+  $ testlog a
+  ('group', ('group', ('func', ('symbol', 'filelog'), ('string', 'a'))))
+  $ testlog a b
+  ('group', ('group', ('or', ('func', ('symbol', 'filelog'), ('string', 'a')), ('func', ('symbol', 'filelog'), ('string', 'b')))))
+
+Test falling back to slow path for non-existing files
+
+  $ testlog a c
+  ('group', ('group', ('func', ('symbol', '_matchfiles'), ('list', ('list', ('string', 'r:'), ('string', 'p:a')), ('string', 'p:c')))))
+
+Test multiple --include/--exclude/paths
+
+  $ testlog --include a --include e --exclude b --exclude e a e
+  ('group', ('group', ('func', ('symbol', '_matchfiles'), ('list', ('list', ('list', ('list', ('list', ('list', ('string', 'r:'), ('string', 'p:a')), ('string', 'p:e')), ('string', 'i:a')), ('string', 'i:e')), ('string', 'x:b')), ('string', 'x:e')))))
+
+Test glob expansion of pats
+
+  $ expandglobs=`python -c "import mercurial.util; \
+  >   print mercurial.util.expandglobs and 'true' or 'false'"`
+  $ if [ $expandglobs = "true" ]; then
+  >    testlog 'a*';
+  > else
+  >    testlog a*;
+  > fi;
+  ('group', ('group', ('func', ('symbol', 'filelog'), ('string', 'aa'))))
+
+Test --follow on a directory
+
+  $ testlog -f dir
+  abort: cannot follow file not in parent revision: "dir"
+  abort: cannot follow file not in parent revision: "dir"
+  abort: cannot follow file not in parent revision: "dir"
+
+Test --follow on file not in parent revision
+
+  $ testlog -f a
+  abort: cannot follow file not in parent revision: "a"
+  abort: cannot follow file not in parent revision: "a"
+  abort: cannot follow file not in parent revision: "a"
+
+Test --follow and patterns
+
+  $ testlog -f 'glob:*'
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+
+Test --follow on a single rename
+
+  $ hg up -q 2
+  $ testlog -f a
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'a'))))
+
+Test --follow and multiple renames
+
+  $ hg up -q tip
+  $ testlog -f e
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'e'))))
+
+Test --follow and multiple filelog heads
+
+  $ hg up -q 2
+  $ testlog -f g
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'g'))))
+  $ cat log.nodes
+  nodetag 2
+  nodetag 1
+  nodetag 0
+  $ hg up -q tip
+  $ testlog -f g
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'g'))))
+  $ cat log.nodes
+  nodetag 3
+  nodetag 2
+  nodetag 0
+
+Test --follow and multiple files
+
+  $ testlog -f g e
+  ('group', ('group', ('or', ('func', ('symbol', 'follow'), ('string', 'g')), ('func', ('symbol', 'follow'), ('string', 'e')))))
+  $ cat log.nodes
+  nodetag 4
+  nodetag 3
+  nodetag 2
+  nodetag 1
+  nodetag 0
+
+Test --follow-first
+
+  $ hg up -q 3
+  $ echo ee > e
+  $ hg ci -Am "add another e" e
+  created new head
+  $ hg merge --tool internal:other 4
+  0 files updated, 1 files merged, 1 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ echo merge > e
+  $ hg ci -m "merge 5 and 4"
+  $ testlog --follow-first
+  ('group', ('func', ('symbol', '_followfirst'), None))
 
-Test multiple revision specifications are correctly handled
+Cannot compare with log --follow-first FILE as it never worked
 
-  $ hg log -G -r 27 -r 25 -r 21 -r 34 -r 32 -r 31 --template '{rev}\n'
-  o  34
-  |
-  o    32
+  $ hg log -G --print-revset --follow-first e
+  ('group', ('group', ('func', ('symbol', '_followfirst'), ('string', 'e'))))
+  $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
+  @    6 merge 5 and 4
+  |\
+  o |  5 add another e
+  | |
+
+Test --copies
+
+  $ hg log -G --copies --template "{rev} {desc|firstline} \
+  >   copies: {file_copies_switch}\n"
+  @    6 merge 5 and 4   copies:
   |\
-  | o    31
-  | |\
-  o | |  27
-  |/ /
-  | o  25
+  | o  5 add another e   copies:
+  | |
+  o |  4 mv dir/b e   copies: e (dir/b)
   |/
-  o    21
-  |\
+  o  3 mv a b; add d   copies: b (a)g (f)
+  |
+  o  2 mv b dir/b   copies: dir/b (b)
+  |
+  o  1 copy a b   copies: b (a)g (f)
+  |
+  o  0 add a   copies:
+  
+Test "set:..." and parent revision
+
+  $ hg up -q 4
+  $ testlog "set:copied()"
+  ('group', ('group', ('func', ('symbol', '_matchfiles'), ('list', ('string', 'r:'), ('string', 'p:set:copied()')))))
+  $ testlog --include "set:copied()"
+  ('group', ('group', ('func', ('symbol', '_matchfiles'), ('list', ('string', 'r:'), ('string', 'i:set:copied()')))))
+  $ testlog -r "sort(file('set:copied()'), -rev)"
+  ('group', ('group', ('func', ('symbol', 'sort'), ('list', ('func', ('symbol', 'file'), ('string', 'set:copied()')), ('negate', ('symbol', 'rev'))))))
+
+Test --removed
+
+  $ testlog --removed
+  ('func', ('symbol', 'all'), None)
+  $ testlog --removed a
+  ('group', ('group', ('func', ('symbol', '_matchfiles'), ('list', ('string', 'r:'), ('string', 'p:a')))))
+  $ testlog --removed --follow a
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+
+Test --patch and --stat with --follow and --follow-first
+
+  $ hg up -q 3
+  $ hg log -G --git --patch b
+  o  changeset:   1:216d4c92cf98
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     copy a b
+  |
+  |  diff --git a/a b/b
+  |  copy from a
+  |  copy to b
+  |
+
+  $ hg log -G --git --stat b
+  o  changeset:   1:216d4c92cf98
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     copy a b
+  |
+  |   a |  0
+  |   1 files changed, 0 insertions(+), 0 deletions(-)
+  |
+
+  $ hg log -G --git --patch --follow b
+  o  changeset:   1:216d4c92cf98
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     copy a b
+  |
+  |  diff --git a/a b/b
+  |  copy from a
+  |  copy to b
+  |
+  o  changeset:   0:f8035bb17114
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add a
+  
+     diff --git a/a b/a
+     new file mode 100644
+     --- /dev/null
+     +++ b/a
+     @@ -0,0 +1,1 @@
+     +a
+  
+
+  $ hg log -G --git --stat --follow b
+  o  changeset:   1:216d4c92cf98
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     copy a b
+  |
+  |   a |  0
+  |   1 files changed, 0 insertions(+), 0 deletions(-)
+  |
+  o  changeset:   0:f8035bb17114
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add a
+  
+      a |  1 +
+      1 files changed, 1 insertions(+), 0 deletions(-)
+  
+
+  $ hg up -q 6
+  $ hg log -G --git --patch --follow-first e
+  @    changeset:   6:fc281d8ff18d
+  |\   tag:         tip
+  | |  parent:      5:99b31f1c2782
+  | |  parent:      4:17d952250a9d
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     merge 5 and 4
+  | |
+  | |  diff --git a/e b/e
+  | |  --- a/e
+  | |  +++ b/e
+  | |  @@ -1,1 +1,1 @@
+  | |  -ee
+  | |  +merge
+  | |
+  o |  changeset:   5:99b31f1c2782
+  | |  parent:      3:5918b8d165d1
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     add another e
+  | |
+  | |  diff --git a/e b/e
+  | |  new file mode 100644
+  | |  --- /dev/null
+  | |  +++ b/e
+  | |  @@ -0,0 +1,1 @@
+  | |  +ee
+  | |
--- a/tests/test-graft.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-graft.t	Sun Mar 04 17:14:30 2012 -0600
@@ -114,7 +114,6 @@
   grafting revision 1
     searching for copies back to rev 1
     unmatched files in local:
-     a.orig
      b
     all copies found (* = to merge, ! = divergent):
      b -> a *
@@ -130,8 +129,6 @@
    b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
   grafting revision 5
     searching for copies back to rev 1
-    unmatched files in local:
-     a.orig
   resolving manifests
    overwrite: False, partial: False
    ancestor: 4c60f11aa304, local: 6f5ea6ac8b70+, remote: 97f8bfe72746
@@ -141,8 +138,6 @@
   e
   grafting revision 4
     searching for copies back to rev 1
-    unmatched files in local:
-     a.orig
   resolving manifests
    overwrite: False, partial: False
    ancestor: 4c60f11aa304, local: 77eb504366ab+, remote: 9c233e8e184d
--- a/tests/test-hgweb-commands.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-hgweb-commands.t	Sun Mar 04 17:14:30 2012 -0600
@@ -24,6 +24,14 @@
   marked working directory as branch stable
   (branches are permanent and global, did you want a bookmark?)
   $ hg ci -Ambranch
+  $ hg branch unstable
+  marked working directory as branch unstable
+  (branches are permanent and global, did you want a bookmark?)
+  $ hg ci -Ambranch
+  $ echo [graph] >> .hg/hgrc
+  $ echo default.width = 3 >> .hg/hgrc
+  $ echo stable.width = 3 >> .hg/hgrc
+  $ echo stable.color = FF0000 >> .hg/hgrc
   $ hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -43,6 +51,22 @@
   
    <entry>
     <title>branch</title>
+    <id>http://*:$HGPORT/#changeset-ba87b23d29ca67a305625d81a20ac279c1e3f444</id> (glob)
+    <link href="http://*:$HGPORT/rev/ba87b23d29ca"/> (glob)
+    <author>
+     <name>test</name>
+     <email>&#116;&#101;&#115;&#116;</email>
+    </author>
+    <updated>1970-01-01T00:00:00+00:00</updated>
+    <published>1970-01-01T00:00:00+00:00</published>
+    <content type="xhtml">
+     <div xmlns="http://www.w3.org/1999/xhtml">
+      <pre xml:space="preserve">branch</pre>
+     </div>
+    </content>
+   </entry>
+   <entry>
+    <title>branch</title>
     <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
     <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
     <author>
@@ -105,6 +129,22 @@
   
    <entry>
     <title>branch</title>
+    <id>http://*:$HGPORT/#changeset-ba87b23d29ca67a305625d81a20ac279c1e3f444</id> (glob)
+    <link href="http://*:$HGPORT/rev/ba87b23d29ca"/> (glob)
+    <author>
+     <name>test</name>
+     <email>&#116;&#101;&#115;&#116;</email>
+    </author>
+    <updated>1970-01-01T00:00:00+00:00</updated>
+    <published>1970-01-01T00:00:00+00:00</published>
+    <content type="xhtml">
+     <div xmlns="http://www.w3.org/1999/xhtml">
+      <pre xml:space="preserve">branch</pre>
+     </div>
+    </content>
+   </entry>
+   <entry>
+    <title>branch</title>
     <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
     <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
     <author>
@@ -208,14 +248,14 @@
   </div>
   <ul>
   <li class="active">log</li>
-  <li><a href="/graph/1d22e65f027e">graph</a></li>
+  <li><a href="/graph/ba87b23d29ca">graph</a></li>
   <li><a href="/tags">tags</a></li>
   <li><a href="/bookmarks">bookmarks</a></li>
   <li><a href="/branches">branches</a></li>
   </ul>
   <ul>
-  <li><a href="/rev/1d22e65f027e">changeset</a></li>
-  <li><a href="/file/1d22e65f027e">browse</a></li>
+  <li><a href="/rev/ba87b23d29ca">changeset</a></li>
+  <li><a href="/file/ba87b23d29ca">browse</a></li>
   </ul>
   <ul>
   
@@ -237,9 +277,9 @@
   </form>
   
   <div class="navigate">
-  <a href="/shortlog/2?revcount=30">less</a>
-  <a href="/shortlog/2?revcount=120">more</a>
-  | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a> 
+  <a href="/shortlog/3?revcount=30">less</a>
+  <a href="/shortlog/3?revcount=120">more</a>
+  | rev 3: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a> 
   </div>
   
   <table class="bigtable">
@@ -251,14 +291,19 @@
    <tr class="parity0">
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
+    <td class="description"><a href="/rev/ba87b23d29ca">branch</a><span class="branchhead">unstable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
    </tr>
    <tr class="parity1">
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
+    <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> </td>
+   </tr>
+   <tr class="parity0">
+    <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+    <td class="author">test</td>
     <td class="description"><a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a><span class="branchhead">default</span> </td>
    </tr>
-   <tr class="parity0">
+   <tr class="parity1">
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
     <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
@@ -267,9 +312,9 @@
   </table>
   
   <div class="navigate">
-  <a href="/shortlog/2?revcount=30">less</a>
-  <a href="/shortlog/2?revcount=120">more</a>
-  | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a> 
+  <a href="/shortlog/3?revcount=30">less</a>
+  <a href="/shortlog/3?revcount=120">more</a>
+  | rev 3: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a> 
   </div>
   
   </div>
@@ -637,18 +682,19 @@
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags'
   200 Script output follows
   
-  tip	1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
+  tip	ba87b23d29ca67a305625d81a20ac279c1e3f444
   1.0	2ef0ac749a14e4f57a5a822464a0902c6f7f448f
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches'
   200 Script output follows
   
-  stable	1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe	open
+  unstable	ba87b23d29ca67a305625d81a20ac279c1e3f444	open
+  stable	1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe	inactive
   default	a4f92ed23982be056b9852de5dfe873eaac7f0de	inactive
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-bookmarks'
   200 Script output follows
   
   anotherthing	2ef0ac749a14e4f57a5a822464a0902c6f7f448f
-  something	1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
+  something	ba87b23d29ca67a305625d81a20ac279c1e3f444
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb'
   200 Script output follows
   
@@ -688,7 +734,7 @@
   <a href="/tags?style=gitweb">tags</a> |
   <a href="/bookmarks?style=gitweb">bookmarks</a> |
   <a href="/branches?style=gitweb">branches</a> |
-  <a href="/file/1d22e65f027e?style=gitweb">files</a> |
+  <a href="/file/ba87b23d29ca?style=gitweb">files</a> |
   <a href="/help?style=gitweb">help</a>
   <br/>
   </div>
@@ -707,9 +753,23 @@
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><i>test</i></td>
   <td>
+  <a class="list" href="/rev/ba87b23d29ca?style=gitweb">
+  <b>branch</b>
+  <span class="logtags"><span class="branchtag" title="unstable">unstable</span> <span class="tagtag" title="tip">tip</span> <span class="bookmarktag" title="something">something</span> </span>
+  </a>
+  </td>
+  <td class="link" nowrap>
+  <a href="/rev/ba87b23d29ca?style=gitweb">changeset</a> |
+  <a href="/file/ba87b23d29ca?style=gitweb">files</a>
+  </td>
+  </tr>
+  <tr class="parity1">
+  <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
+  <td><i>test</i></td>
+  <td>
   <a class="list" href="/rev/1d22e65f027e?style=gitweb">
   <b>branch</b>
-  <span class="logtags"><span class="branchtag" title="stable">stable</span> <span class="tagtag" title="tip">tip</span> <span class="bookmarktag" title="something">something</span> </span>
+  <span class="logtags"><span class="branchtag" title="stable">stable</span> </span>
   </a>
   </td>
   <td class="link" nowrap>
@@ -717,7 +777,7 @@
   <a href="/file/1d22e65f027e?style=gitweb">files</a>
   </td>
   </tr>
-  <tr class="parity1">
+  <tr class="parity0">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><i>test</i></td>
   <td>
@@ -731,7 +791,7 @@
   <a href="/file/a4f92ed23982?style=gitweb">files</a>
   </td>
   </tr>
-  <tr class="parity0">
+  <tr class="parity1">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><i>test</i></td>
   <td>
@@ -777,11 +837,11 @@
   </tr>
   <tr class="parity1">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
-  <td><a class="list" href="/rev/1d22e65f027e?style=gitweb"><b>something</b></a></td>
+  <td><a class="list" href="/rev/ba87b23d29ca?style=gitweb"><b>something</b></a></td>
   <td class="link">
-  <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
-  <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
-  <a href="/file/1d22e65f027e?style=gitweb">files</a>
+  <a href="/rev/ba87b23d29ca?style=gitweb">changeset</a> |
+  <a href="/log/ba87b23d29ca?style=gitweb">changelog</a> |
+  <a href="/file/ba87b23d29ca?style=gitweb">files</a>
   </td>
   </tr>
   <tr class="light"><td colspan="3"><a class="list" href="/bookmarks?style=gitweb">...</a></td></tr>
@@ -792,6 +852,16 @@
   
   <tr class="parity0">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
+  <td><a class="list" href="/shortlog/ba87b23d29ca?style=gitweb"><b>ba87b23d29ca</b></a></td>
+  <td class="">unstable</td>
+  <td class="link">
+  <a href="/changeset/ba87b23d29ca?style=gitweb">changeset</a> |
+  <a href="/log/ba87b23d29ca?style=gitweb">changelog</a> |
+  <a href="/file/ba87b23d29ca?style=gitweb">files</a>
+  </td>
+  </tr>
+  <tr class="parity1">
+  <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td>
   <td class="">stable</td>
   <td class="link">
@@ -800,7 +870,7 @@
   <a href="/file/1d22e65f027e?style=gitweb">files</a>
   </td>
   </tr>
-  <tr class="parity1">
+  <tr class="parity0">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
   <td class="">default</td>
@@ -861,17 +931,17 @@
   <div class="page_nav">
   <a href="/summary?style=gitweb">summary</a> |
   <a href="/shortlog?style=gitweb">shortlog</a> |
-  <a href="/log/2?style=gitweb">changelog</a> |
+  <a href="/log/3?style=gitweb">changelog</a> |
   graph |
   <a href="/tags?style=gitweb">tags</a> |
   <a href="/bookmarks?style=gitweb">bookmarks</a> |
   <a href="/branches?style=gitweb">branches</a> |
-  <a href="/file/1d22e65f027e?style=gitweb">files</a> |
+  <a href="/file/ba87b23d29ca?style=gitweb">files</a> |
   <a href="/help?style=gitweb">help</a>
   <br/>
-  <a href="/graph/2?style=gitweb&revcount=30">less</a>
-  <a href="/graph/2?style=gitweb&revcount=120">more</a>
-  | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
+  <a href="/graph/3?style=gitweb&revcount=30">less</a>
+  <a href="/graph/3?style=gitweb&revcount=120">more</a>
+  | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-3</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
   </div>
   
   <div class="title">&nbsp;</div>
@@ -880,27 +950,17 @@
   
   <div id="wrapper">
   <ul id="nodebgs"></ul>
-  <canvas id="graph" width="480" height="129"></canvas>
+  <canvas id="graph" width="480" height="168"></canvas>
   <ul id="graphnodes"></ul>
   </div>
   
   <script>
   <!-- hide script content
   
-  var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
+  var data = [["ba87b23d29ca", [0, 1], [[0, 0, 1, 3, "FF0000"]], "branch", "test", "1970-01-01", ["unstable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1, 3, ""]], "branch", "test", "1970-01-01", ["stable", true], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1, 3, ""]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
   var graph = new Graph();
   graph.scale(39);
   
-  graph.edge = function(x0, y0, x1, y1, color) {
-  	
-  	this.setColor(color, 0.0, 0.65);
-  	this.ctx.beginPath();
-  	this.ctx.moveTo(x0, y0);
-  	this.ctx.lineTo(x1, y1);
-  	this.ctx.stroke();
-  	
-  }
-  
   var revlink = '<li style="_STYLE"><span class="desc">';
   revlink += '<a class="list" href="/rev/_NODEID?style=gitweb" title="_NODEID"><b>_DESC</b></a>';
   revlink += '</span> _TAGS';
@@ -960,9 +1020,9 @@
   </script>
   
   <div class="page_nav">
-  <a href="/graph/2?style=gitweb&revcount=30">less</a>
-  <a href="/graph/2?style=gitweb&revcount=120">more</a>
-  | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a> 
+  <a href="/graph/3?style=gitweb&revcount=30">less</a>
+  <a href="/graph/3?style=gitweb&revcount=120">more</a>
+  | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-3</a> <a href="/graph/tip?style=gitweb">tip</a> 
   </div>
   
   <script type="text/javascript">process_dates()</script>
@@ -991,7 +1051,7 @@
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads'
   200 Script output follows
   
-  1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
+  ba87b23d29ca67a305625d81a20ac279c1e3f444
 
 branches
 
@@ -1005,10 +1065,11 @@
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup&roots=0000000000000000000000000000000000000000'
   200 Script output follows
   
-  x\x9c\xbdTMHUA\x14\xbe\xa8\xf9\xec\xda&\x10\x11*\xb8\x88\x81\x99\xbef\xe6\xce\xbdw\xc6\xf2a\x16E\x1b\x11[%\x98\xcc\xaf\x8f\x8c\xf7\xc0\xf7\x82 (esc)
-  4\x11KP2m\x95\xad*\xabE\x05AP\xd0\xc22Z\x14\xf9\x03\xb9j\xa3\x9b$\xa4MJ\xb4\x90\xc0\x9a\x9bO0\x10\xdf\x13\xa2\x81\x0f\x869g\xe6|\xe7\x9c\xef\x8ceY\xf7\xa2KO\xd2\xb7K\x16~\\n\xe9\xad\x90w\x86\xab\x93W\x8e\xdf\xb0r\\Y\xee6(\xa2)\xf6\x95\xc6\x01\xe4\x1az\x80R\xe8kN\x98\xe7R\xa4\xa9K@\xe0!A\xb4k\xa7U*m\x03\x07\xd8\x92\x1d\xd2\xc9\xa4\x1d\xc2\xe6,\xa5\xcc+\x1f\xef\xafDgi\xef\xab\x1d\x1d\xb7\x9a\xe7[W\xfbc\x8f\xde-\xcd\xe7\xcaz\xb3\xbb\x19\xd3\x81\x10>c>\x08\x00"X\x11\xc2\x84@\xd2\xe7B*L\x00\x01P\x04R\xc3@\xbaB0\xdb8#\x83:\x83\xa2h\xbc=\xcd\xdaS\xe1Y,L\xd3\xa0\xf2\xa8\x94J:\xe6\xd8\x81Q\xe0\xe8d\xa7#\xe2,\xd1\xaeR*\xed \xa5\x01\x13\x01\xa6\x0cb\xe3;\xbe\xaf\xfcK[^wK\xe1N\xaf\xbbk\xe8B\xd1\xf4\xc1\x07\xb3\xab[\x10\xfdkmvwcB\xa6\xa4\xd4G\xc4D\xc2\x141\xad\x91\x10\x00\x08J\x81\xcb}\xee	\xee+W\xba\x8a\x80\x90|\xd4\xa0\xd6\xa0\xd4T\xde\xe1\x9d,!\xe2\xb5\xa94\xe3\xe7\xd5\x9f\x06\x18\xcba\x03aP\xb8f\xcd\x04\x1a_\\9\xf1\xed\xe4\x9e\xe5\xa6\xd1\xd2\x9f\x03\xa7o\xae\x90H\xf3\xfb\xef\xffH3\xadk (esc)
-  \xb0\x90\x92\x88\xb9\x14"\x068\xc2\x1e@\x00\xbb\x8a)\xd3'\x859 (esc)
-  \xa8\x80\x84S \xa5\xbd-g\x13`\xe4\xdc\xc3H^\xdf\xe2\xc0TM\xc7\xf4BO\xcf\xde\xae\xe5\xae#\x1frM(K\x97`F\x19\x16s\x05GD\xb9\x01\xc1\x00+\x8c|\x9fp\xc11\xf0\x14\x00\x9cJ\x82<\xe0\x12\x9f\xc1\x90\xd0\xf5\xc8\x19>Pr\xaa\xeaW\xf5\xc4\xae\xd1\xfc\x17\xcf'\x13u\xb1\x9e\xcdHnC\x0e\xcc`\xc8\xa0&\xac\x0e\xf1|\x8c\x10$\xc4\x8c\xa2p\x05`\xdc\x08 \x80\xc4\xd7Rr-\x94\x10\x102\xedi;\xf3f\xf1z\x16\x86\xdb\xd8d\xe5\xe7\x8b\xf5\x8d\rzp\xb2\xfe\xac\xf5\xf2\xd3\xfe\xfckws\xedt\x96b\xd5l\x1c\x0b\x85\xb5\x170\x8f\x11\x84\xb0\x8f\x19\xa0\x00	_\x07\x1ac\xa2\xc3\x89Z\xe7\x96\xf9 \xccNFg\xc7F\xaa\x8a+\x9a\x9cc_\x17\x1b\x17\x9e]z38<\x97+\xb5,",\xc8\xc8?\\\x91\xff\x17.~U\x96\x97\xf5%\xdeN<\x8e\xf5\x97%\xe7^\xcfL\xed~\xda\x96k\xdc->\x86\x02\x83"\x96H\xa6\xe3\xaas=-\xeb7\xe5\xda\x8f\xbc (no-eol) (esc)
+  x\x9c\xbdTMHTQ\x14\x1e\xfc\xef\xd9&\x10\x11*x\x88\x81\x9aN\xf7\xddw\xdf{\xf7Y\x0efR\xb4\x11\xb1U\x82\xc5\xfd\x9d!c\x06\x9c'd\xa0\x99X\x82\x92i\xablUZ-*\x08\x84\x82\x02KkQ\xf8\x13\xe4\xaa\x8dn\x94\x906)\xd5B\x02\xeb\xbe\x9c\x01\x85\xc9\x996\x1d\xf8x\x97{\xefy\xe7;\xe7|\xe7\x06\x02\x81\xb1\xe0\xda\x13\xefN\xd1\xca\x8f\xcb-\xbde\xfc\xeepU\xecJ\xc3\xcd@\x86\x96\xc6\xb7^`\xe9"[H\xe4\x18T\x1a\x16p]\xc3\x96\x14\x13\xcbt\xa1tM\x0c\x1c\x0b2,M\xcd\x13qO\x03:\xd089"c1\xcd\x87FI\\\xa8\xbf|\xbc\xbf\x11\\p{_\xe5\xb6\xddn^j\xdd\xec\x0f=z\xb7\xb6\x94)\xebT\xbe\x89\xa3 (esc)
+  \x1f6!6p\x00\xc4H`L\x18\x83\xdc\xa6\x8c\x0b\x84\x01\x06\x06s\xb84\x1cn2F4u\x19*\xd4*\x14\x04#a\x8f\x84\xe3\xfe^\xc8OS\xa1\xfc8\xe7\x82\xebj[7\x82@\x97\xb1v\x9dEH4,\xe2\xc2\xd3\xa1\x90\x800\x07\xb9\xc4@\xea\xee\xe4\xc1\xd2\xcf\xe7\xb3\xba[\xf2\xf6X\xdd]C\x1d\x05\xf3\x87\x1f,l\xeeBt\x87\xa5\xf2\xdd\x9e\x90*\xa9kC\xac"!\x17\x12)!c\x000\xd7\x05&\xb5\xa9\xc5\xa8-Ln (esc)
+  \x0c|\xf2A\x85\x1a\x85bUy\x9d\xb6\x93(\x8b\xd4\xc4=B/\x8a?\rP'G\x15\x98B\xde\xd6\xa9Zy/\xfb'j+f\xc2\xe3\xb9\xb4\xf5\xea\x98\xf6\xa6sz\xf9{\xc3.\xa4vX*\xdf\x04\x0f\xff[\xb4\x8dGG4\xc1$\xe1:\xb9\xbaq\xf2\xeb\xa9\xfd\xebM\xa3\xc5?\x07\xce\xdc\xda\xc0\xf9\xcd\xef\xbf\xa5\xd3g\xd2\xd2\xa8\xa5uKu\x01(8$\xa6k@\x02(D\x16\x80\x00\x99\x82\x08\xa5\r\x81(t\\f`\xea\x02\xce\xb5\x7f\xba\xac\x02\x8c\\x\x98\x9f\xd5\xb7:0W\xdd6\xbf\xd2\xd3s\xa0k\xbd\xeb\xd8L\xa6	\xa5Q\x86\x91Pc\x80\x98\x8cB,L\x07#\x80\x04\x82\xb6\x8d)\xa3\x08X\x02\x00\xear\x0c-`b\x9b\x18>\xa1\x1b\xf9g\xe9@\xd1\xe9\xca_US{G\xb3\x9f?\x9b\x8d\xd6\x86zR\x91LE\xe8/\xdd& (esc)
+  C
+  \xd5~u\xb0e#\x08\r\x8c\xd5\xf83\x93\x01B\x95\xe8\x1c\x03\xdb\x92s*\x99`\xcc0\x88\xb4d\xb2\xbd\x85\xc9,\x14\xb7\xf1\xd9\xf2\xe5Ku\x8d\xf5rp\xb6\xee\\\xe0\xc5\xa7C\xd9\xd7\xefe\xda\xe94\xc5\xaa\xde>\x8a\x02I\xcb!\x16\xc1\x10"\x1b\x11\xe0\x02\xc8l\xe9H\x84\xb0\xf4\xa78\xc9-\xf1(\xa9\x15\x0f.\x8c\x8fT\x16\x965\xe9'\xbe\xac6\xaeLtN\x0f\x0e/fJ-\x8d\x08s\x12#\xe7[\xfe\xff\x0b\x17\xb9\xc6KK\xfa\xa2o\xa7\x1e\x87\xfaKb\x8b\xaf?\xcc\xed{z>\xd3\xb8\xbb\xcc}\x8eB\x01\x89\xc6\xbc\x88hO\xa6\x15\xf8\rr4\xb3\xe5 (no-eol) (esc)
 
 stream_out
 
@@ -1153,7 +1214,7 @@
 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \
   >     | grep '^var data ='
-  var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
+  var data = [["548001d11f45", [0, 1], [[0, 0, 1, -1, ""]], "\u80fd", "test", "1970-01-01", ["unstable", true], ["tip"], ["something"]], ["ba87b23d29ca", [0, 1], [[0, 0, 1, 3, "FF0000"]], "branch", "test", "1970-01-01", ["unstable", false], [], []], ["1d22e65f027e", [0, 1], [[0, 0, 1, 3, ""]], "branch", "test", "1970-01-01", ["stable", true], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1, 3, ""]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
 
 ERRORS ENCOUNTERED
 
--- a/tests/test-hgweb-empty.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-hgweb-empty.t	Sun Mar 04 17:14:30 2012 -0600
@@ -250,16 +250,6 @@
   var graph = new Graph();
   graph.scale(39);
   
-  graph.edge = function(x0, y0, x1, y1, color) {
-  	
-  	this.setColor(color, 0.0, 0.65);
-  	this.ctx.beginPath();
-  	this.ctx.moveTo(x0, y0);
-  	this.ctx.lineTo(x1, y1);
-  	this.ctx.stroke();
-  	
-  }
-  
   var revlink = '<li style="_STYLE"><span class="desc">';
   revlink += '<a href="/rev/_NODEID" title="_NODEID">_DESC</a>';
   revlink += '</span>_TAGS<span class="info">_DATE, by _USER</span></li>';
--- a/tests/test-issue612.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-issue612.t	Sun Mar 04 17:14:30 2012 -0600
@@ -24,11 +24,11 @@
 
   $ hg merge
   merging src/a.c and source/a.c to source/a.c
-  1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
   $ hg status
   M source/a.c
   R src/a.c
-  ? source/a.o
+  ? src/a.o
 
--- a/tests/test-mq-subrepo-svn.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-mq-subrepo-svn.t	Sun Mar 04 17:14:30 2012 -0600
@@ -37,7 +37,6 @@
   $ hg status -S -X '**/format'
   A .hgsub
   $ hg qnew -m0 0.diff
-  committing subrepository sub
   $ cd sub
   $ echo a > a
   $ svn add a
--- a/tests/test-mq-subrepo.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-mq-subrepo.t	Sun Mar 04 17:14:30 2012 -0600
@@ -105,7 +105,6 @@
   % update substate when adding .hgsub w/clean updated subrepo
   A .hgsub
   % qnew -m0 0.diff
-  committing subrepository sub
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -121,7 +120,6 @@
   % update substate when modifying .hgsub w/clean updated subrepo
   M .hgsub
   % qnew -m1 1.diff
-  committing subrepository sub2
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -166,7 +164,6 @@
   % update substate when adding .hgsub w/clean updated subrepo
   A .hgsub
   % qrefresh
-  committing subrepository sub
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -183,7 +180,6 @@
   % update substate when modifying .hgsub w/clean updated subrepo
   M .hgsub
   % qrefresh
-  committing subrepository sub2
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -225,7 +221,6 @@
   $ echo sub = sub > .hgsub
   $ hg add .hgsub
   $ hg qnew -m0 0.diff
-  committing subrepository sub
   $ hg debugsub
   path sub
    source   sub
@@ -277,7 +272,6 @@
   diff --git a/.hgsub b/.hgsub
   new file mode 100644
   examine changes to '.hgsub'? [Ynsfdaq?] 
-  committing subrepository sub
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -310,7 +304,6 @@
    sub = sub
   +sub2 = sub2
   record this change to '.hgsub'? [Ynsfdaq?] 
-  committing subrepository sub2
   path sub
    source   sub
    revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
@@ -360,4 +353,3 @@
   $ echo sub = sub >> .hgsub
   $ hg add .hgsub
   $ hg qnew 0.diff
-  committing subrepository sub
--- a/tests/test-mq.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-mq.t	Sun Mar 04 17:14:30 2012 -0600
@@ -70,7 +70,7 @@
    qgoto         push or pop patches until named patch is at top of stack
    qguard        set or print guards for a patch
    qheader       print the header of the topmost or specified patch
-   qimport       import a patch
+   qimport       import a patch or existing changeset
    qnew          create a new patch
    qnext         print the name of the next pushable patch
    qpop          pop the current patch off the stack
--- a/tests/test-phases.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-phases.t	Sun Mar 04 17:14:30 2012 -0600
@@ -402,3 +402,34 @@
   |
   o  0 public A
   
+test partial failure
+
+  $ hg phase --public 7
+  $ hg phase --draft '5 or 7'
+  cannot move 1 changesets to a more permissive phase, use --force
+  phase changed for 1 changesets
+  [1]
+  $ hg log -G --template "{rev} {phase} {desc}\n"
+  @    7 public merge B' and E
+  |\
+  | o  6 public B'
+  | |
+  +---o  5 draft H
+  | |
+  o |  4 public E
+  | |
+  o |  3 public D
+  | |
+  o |  2 public C
+  |/
+  o  1 public B
+  |
+  o  0 public A
+  
+
+test complete failure
+
+  $ hg phase --draft 7
+  cannot move 1 changesets to a more permissive phase, use --force
+  no phases changed
+  [1]
--- a/tests/test-rename-dir-merge.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-rename-dir-merge.t	Sun Mar 04 17:14:30 2012 -0600
@@ -27,7 +27,6 @@
     searching for copies back to rev 1
     unmatched files in local:
      a/c
-     a/d
     unmatched files in other:
      b/a
      b/b
@@ -37,33 +36,29 @@
     checking for directory renames
     dir a/ -> b/
     file a/c -> b/c
-    file a/d -> b/d
   resolving manifests
    overwrite: False, partial: False
    ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
-   a/d: remote renamed directory to b/d -> d
    a/c: remote renamed directory to b/c -> d
    a/b: other deleted -> r
    a/a: other deleted -> r
    b/a: remote created -> g
    b/b: remote created -> g
-  updating: a/a 1/6 files (16.67%)
+  updating: a/a 1/5 files (20.00%)
   removing a/a
-  updating: a/b 2/6 files (33.33%)
+  updating: a/b 2/5 files (40.00%)
   removing a/b
-  updating: a/c 3/6 files (50.00%)
+  updating: a/c 3/5 files (60.00%)
   moving a/c to b/c
-  updating: a/d 4/6 files (66.67%)
-  moving a/d to b/d
-  updating: b/a 5/6 files (83.33%)
+  updating: b/a 4/5 files (80.00%)
   getting b/a
-  updating: b/b 6/6 files (100.00%)
+  updating: b/b 5/5 files (100.00%)
   getting b/b
-  4 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  3 files updated, 0 files merged, 2 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
   $ echo a/* b/*
-  a/* b/a b/b b/c b/d
+  a/d b/a b/b b/c
   $ hg st -C
   M b/a
   M b/b
@@ -72,7 +67,7 @@
   R a/a
   R a/b
   R a/c
-  ? b/d
+  ? a/d
   $ hg ci -m "3 merge 2+1"
   $ hg debugrename b/c
   b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
@@ -84,7 +79,6 @@
     unmatched files in local:
      b/a
      b/b
-     b/d
     unmatched files in other:
      a/c
     all copies found (* = to merge, ! = divergent):
@@ -103,11 +97,11 @@
   (branch merge, don't forget to commit)
 
   $ echo a/* b/*
-  a/* b/a b/b b/c b/d
+  a/d b/a b/b b/c
   $ hg st -C
   A b/c
     a/c
-  ? b/d
+  ? a/d
   $ hg ci -m "4 merge 1+2"
   created new head
   $ hg debugrename b/c
--- a/tests/test-revset-dirstate-parents.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-revset-dirstate-parents.t	Sun Mar 04 17:14:30 2012 -0600
@@ -13,11 +13,17 @@
   $ cd repo
 
   $ try 'p1()'
-  ('func', ('symbol', 'p1'), None)
+  (func
+    ('symbol', 'p1')
+    None)
   $ try 'p2()'
-  ('func', ('symbol', 'p2'), None)
+  (func
+    ('symbol', 'p2')
+    None)
   $ try 'parents()'
-  ('func', ('symbol', 'parents'), None)
+  (func
+    ('symbol', 'parents')
+    None)
 
 null revision
   $ log 'p1()'
--- a/tests/test-revset.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-revset.t	Sun Mar 04 17:14:30 2012 -0600
@@ -94,19 +94,25 @@
   ('symbol', 'a')
   0
   $ try b-a
-  ('minus', ('symbol', 'b'), ('symbol', 'a'))
+  (minus
+    ('symbol', 'b')
+    ('symbol', 'a'))
   1
   $ try _a_b_c_
   ('symbol', '_a_b_c_')
   6
   $ try _a_b_c_-a
-  ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
+  (minus
+    ('symbol', '_a_b_c_')
+    ('symbol', 'a'))
   6
   $ try .a.b.c.
   ('symbol', '.a.b.c.')
   7
   $ try .a.b.c.-a
-  ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
+  (minus
+    ('symbol', '.a.b.c.')
+    ('symbol', 'a'))
   7
   $ try -- '-a-b-c-' # complains
   hg: parse error at 7: not a prefix: end
@@ -114,7 +120,15 @@
   $ log -a-b-c- # succeeds with fallback
   4
   $ try -- -a-b-c--a # complains
-  ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
+  (minus
+    (minus
+      (minus
+        (negate
+          ('symbol', 'a'))
+        ('symbol', 'b'))
+      ('symbol', 'c'))
+    (negate
+      ('symbol', 'a')))
   abort: unknown revision '-a'!
   [255]
   $ try é
@@ -124,7 +138,9 @@
 quoting needed
 
   $ try '"-a-b-c-"-a'
-  ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
+  (minus
+    ('string', '-a-b-c-')
+    ('symbol', 'a'))
   4
 
   $ log '1 or 2'
@@ -136,15 +152,32 @@
   $ log '1 and 2'
   $ log '1&2'
   $ try '1&2|3' # precedence - and is higher
-  ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
+  (or
+    (and
+      ('symbol', '1')
+      ('symbol', '2'))
+    ('symbol', '3'))
   3
   $ try '1|2&3'
-  ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
+  (or
+    ('symbol', '1')
+    (and
+      ('symbol', '2')
+      ('symbol', '3')))
   1
   $ try '1&2&3' # associativity
-  ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
+  (and
+    (and
+      ('symbol', '1')
+      ('symbol', '2'))
+    ('symbol', '3'))
   $ try '1|(2|3)'
-  ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
+  (or
+    ('symbol', '1')
+    (group
+      (or
+        ('symbol', '2')
+        ('symbol', '3'))))
   1
   2
   3
@@ -226,13 +259,19 @@
   $ log 'grep("issue\d+")'
   6
   $ try 'grep("(")' # invalid regular expression
-  ('func', ('symbol', 'grep'), ('string', '('))
+  (func
+    ('symbol', 'grep')
+    ('string', '('))
   hg: parse error: invalid match pattern: unbalanced parenthesis
   [255]
   $ try 'grep("\bissue\d+")'
-  ('func', ('symbol', 'grep'), ('string', '\x08issue\\d+'))
+  (func
+    ('symbol', 'grep')
+    ('string', '\x08issue\\d+'))
   $ try 'grep(r"\bissue\d+")'
-  ('func', ('symbol', 'grep'), ('string', '\\bissue\\d+'))
+  (func
+    ('symbol', 'grep')
+    ('string', '\\bissue\\d+'))
   6
   $ try 'grep(r"\")'
   hg: parse error at 7: unterminated string
@@ -430,41 +469,158 @@
 
   $ echo '[revsetalias]' >> .hg/hgrc
   $ echo 'm = merge()' >> .hg/hgrc
+  $ echo 'sincem = descendants(m)' >> .hg/hgrc
   $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
   $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
   $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
 
   $ try m
   ('symbol', 'm')
-  ('func', ('symbol', 'merge'), None)
+  (func
+    ('symbol', 'merge')
+    None)
+  6
+
+test alias recursion
+
+  $ try sincem
+  ('symbol', 'sincem')
+  (func
+    ('symbol', 'descendants')
+    (func
+      ('symbol', 'merge')
+      None))
   6
+  7
+
+test infinite recursion
+
+  $ echo 'recurse1 = recurse2' >> .hg/hgrc
+  $ echo 'recurse2 = recurse1' >> .hg/hgrc
+  $ try recurse1
+  ('symbol', 'recurse1')
+  hg: parse error: infinite expansion of revset alias "recurse1" detected
+  [255]
+
+test nesting and variable passing
+
+  $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
+  $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
+  $ echo 'nested3($1) = max($1)' >> .hg/hgrc
+  $ try 'nested(2:5)'
+  (func
+    ('symbol', 'nested')
+    (range
+      ('symbol', '2')
+      ('symbol', '5')))
+  (func
+    ('symbol', 'max')
+    (range
+      ('symbol', '2')
+      ('symbol', '5')))
+  5
+
+test variable isolation, variable placeholders are rewritten as string
+then parsed and matched again as string. Check they do not leak too
+far away.
+
+  $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
+  $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
+  $ try 'callinjection(2:5)'
+  (func
+    ('symbol', 'callinjection')
+    (range
+      ('symbol', '2')
+      ('symbol', '5')))
+  (func
+    ('symbol', 'descendants')
+    (func
+      ('symbol', 'max')
+      ('string', '$1')))
+  abort: unknown revision '$1'!
+  [255]
+
   $ try 'd(2:5)'
-  ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
-  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date'))))
+  (func
+    ('symbol', 'd')
+    (range
+      ('symbol', '2')
+      ('symbol', '5')))
+  (func
+    ('symbol', 'reverse')
+    (func
+      ('symbol', 'sort')
+      (list
+        (range
+          ('symbol', '2')
+          ('symbol', '5'))
+        ('symbol', 'date'))))
   4
   5
   3
   2
   $ try 'rs(2 or 3, date)'
-  ('func', ('symbol', 'rs'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date')))
-  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date'))))
+  (func
+    ('symbol', 'rs')
+    (list
+      (or
+        ('symbol', '2')
+        ('symbol', '3'))
+      ('symbol', 'date')))
+  (func
+    ('symbol', 'reverse')
+    (func
+      ('symbol', 'sort')
+      (list
+        (or
+          ('symbol', '2')
+          ('symbol', '3'))
+        ('symbol', 'date'))))
   3
   2
   $ try 'rs()'
-  ('func', ('symbol', 'rs'), None)
+  (func
+    ('symbol', 'rs')
+    None)
   hg: parse error: invalid number of arguments: 0
   [255]
   $ try 'rs(2)'
-  ('func', ('symbol', 'rs'), ('symbol', '2'))
+  (func
+    ('symbol', 'rs')
+    ('symbol', '2'))
   hg: parse error: invalid number of arguments: 1
   [255]
   $ try 'rs(2, data, 7)'
-  ('func', ('symbol', 'rs'), ('list', ('list', ('symbol', '2'), ('symbol', 'data')), ('symbol', '7')))
+  (func
+    ('symbol', 'rs')
+    (list
+      (list
+        ('symbol', '2')
+        ('symbol', 'data'))
+      ('symbol', '7')))
   hg: parse error: invalid number of arguments: 3
   [255]
   $ try 'rs4(2 or 3, x, x, date)'
-  ('func', ('symbol', 'rs4'), ('list', ('list', ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'x')), ('symbol', 'x')), ('symbol', 'date')))
-  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date'))))
+  (func
+    ('symbol', 'rs4')
+    (list
+      (list
+        (list
+          (or
+            ('symbol', '2')
+            ('symbol', '3'))
+          ('symbol', 'x'))
+        ('symbol', 'x'))
+      ('symbol', 'date')))
+  (func
+    ('symbol', 'reverse')
+    (func
+      ('symbol', 'sort')
+      (list
+        (or
+          ('symbol', '2')
+          ('symbol', '3'))
+        ('symbol', 'date'))))
   3
   2
 
--- a/tests/test-subrepo-deep-nested-change.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-deep-nested-change.t	Sun Mar 04 17:14:30 2012 -0600
@@ -18,7 +18,6 @@
   adding sub1/.hgsub (glob)
   adding sub1/sub1 (glob)
   $ hg commit -R sub1 -m "sub1 import"
-  committing subrepository sub2
 
 Preparing the 'main' repo which depends on the subrepo 'sub1'
 
@@ -33,7 +32,6 @@
   adding main/.hgsub (glob)
   adding main/main (glob)
   $ hg commit -R main -m "main import"
-  committing subrepository sub1
 
 Cleaning both repositories, just as a clone -U
 
--- a/tests/test-subrepo-git.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-git.t	Sun Mar 04 17:14:30 2012 -0600
@@ -34,7 +34,6 @@
   $ git clone -q ../gitroot s
   $ hg add .hgsub
   $ hg commit -m 'new git subrepo'
-  committing subrepository s
   $ hg debugsub
   path s
    source   ../gitroot
@@ -55,7 +54,6 @@
   $ hg status --subrepos
   M s/g
   $ hg commit -m 'update git subrepo'
-  committing subrepository s
   $ hg debugsub
   path s
    source   ../gitroot
@@ -222,7 +220,6 @@
   $ git pull -q >/dev/null 2>/dev/null
   $ cd ..
   $ hg commit -m 'git upstream sync'
-  committing subrepository s
   $ hg debugsub
   path s
    source   ../gitroot
@@ -287,7 +284,6 @@
   $ echo inner = inner > .hgsub
   $ hg add .hgsub
   $ hg commit -m 'nested sub'
-  committing subrepository inner
 
 nested commit
 
@@ -339,27 +335,32 @@
   $ hg update 1 -q
   $ hg rm .hgsubstate
   $ hg commit .hgsubstate -m 'no substate'
-  created new head
+  nothing changed
+  [1]
   $ hg tag -l nosubstate
   $ hg manifest
   .hgsub
+  .hgsubstate
   a
 
   $ hg status -S
+  R .hgsubstate
   $ hg sum | grep commit
-  commit: 1 subrepos
+  commit: 1 removed, 1 subrepos (new branch head)
 
   $ hg commit -m 'restore substate'
-  committing subrepository s
+  nothing changed
+  [1]
   $ hg manifest
   .hgsub
   .hgsubstate
   a
   $ hg sum | grep commit
-  commit: (clean)
+  commit: 1 removed, 1 subrepos (new branch head)
 
   $ hg update -qC nosubstate
   $ ls s
+  g
 
 issue3109: false positives in git diff-index
 
--- a/tests/test-subrepo-missing.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-missing.t	Sun Mar 04 17:14:30 2012 -0600
@@ -7,12 +7,10 @@
   $ echo 'subrepo = subrepo' > .hgsub
   $ hg ci -Am addsubrepo
   adding .hgsub
-  committing subrepository subrepo
   $ echo b > subrepo/b
   $ hg -R subrepo ci -Am addb
   adding b
   $ hg ci -m updatedsub
-  committing subrepository subrepo
 
 delete .hgsub and revert it
 
--- a/tests/test-subrepo-recursion.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-recursion.t	Sun Mar 04 17:14:30 2012 -0600
@@ -79,11 +79,9 @@
 
   $ cd ..
   $ hg commit -m 0-2-1
-  committing subrepository bar
 
   $ cd ..
   $ hg commit -m 1-2-1
-  committing subrepository foo
 
 Change working directory:
 
--- a/tests/test-subrepo-relative-path.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-relative-path.t	Sun Mar 04 17:14:30 2012 -0600
@@ -20,7 +20,6 @@
   adding main/.hgsub (glob)
   adding main/main (glob)
   $ hg commit -R main -m "main import"
-  committing subrepository sub
 
 Cleaning both repositories, just as a clone -U
 
--- a/tests/test-subrepo-svn.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo-svn.t	Sun Mar 04 17:14:30 2012 -0600
@@ -69,8 +69,6 @@
   $ svn co --quiet "$SVNREPO"/src subdir/s
   $ hg add .hgsub
   $ hg ci -m1
-  committing subrepository s
-  committing subrepository subdir/s
 
 make sure we avoid empty commits (issue2445)
 
@@ -432,7 +430,6 @@
   $ echo "s =        [svn]       $SVNREPO/src" >> .hgsub
   $ hg add .hgsub
   $ hg ci -m addsub
-  committing subrepository s
   $ echo a > a
   $ hg ci -Am adda
   adding a
@@ -440,7 +437,6 @@
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ svn up -qr6 s
   $ hg ci -m updatesub
-  committing subrepository s
   created new head
   $ echo pyc > s/dir/epsilon.pyc
   $ hg up 1
@@ -462,14 +458,12 @@
   $ echo "obstruct =        [svn]       $SVNREPO/externals" >> .hgsub
   $ svn co -r5 --quiet "$SVNREPO"/externals obstruct
   $ hg commit -m 'Start making obstructed working copy'
-  committing subrepository obstruct
   $ hg book other
   $ hg co -r 'p1(tip)'
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo "obstruct =        [svn]       $SVNREPO/src" >> .hgsub
   $ svn co -r5 --quiet "$SVNREPO"/src obstruct
   $ hg commit -m 'Other branch which will be obstructed'
-  committing subrepository obstruct
   created new head
 
 Switching back to the head where we have another path mapped to the
@@ -530,12 +524,10 @@
   Checked out revision 10.
   $ echo "recreated =        [svn]       $SVNREPO/branch" >> .hgsub
   $ hg ci -m addsub
-  committing subrepository recreated
   $ cd recreated
   $ svn up -q
   $ cd ..
   $ hg ci -m updatesub
-  committing subrepository recreated
   $ hg up -r-2
   D    *recreated/somethingnew (glob)
   A    *recreated/somethingold (glob)
--- a/tests/test-subrepo.t	Sun Mar 04 17:12:12 2012 -0600
+++ b/tests/test-subrepo.t	Sun Mar 04 17:14:30 2012 -0600
@@ -37,7 +37,6 @@
   commit: 1 added, 1 subrepos
   update: (current)
   $ hg ci -m1
-  committing subrepository s
 
 Revert can't (yet) revert subrepos:
 
@@ -105,7 +104,6 @@
   $ echo b > s/a
   $ hg -R s ci -ms1
   $ hg --config ui.commitsubrepos=no ci -m3
-  committing subrepository s
 
 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
 
@@ -455,7 +453,6 @@
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg ci -Am1
   adding .hgsub
-  committing subrepository s
   $ hg branch br
   marked working directory as branch br
   (branches are permanent and global, did you want a bookmark?)
@@ -464,7 +461,6 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg ci -Am1
   adding b
-  committing subrepository s
   $ hg up default
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo c > c
@@ -483,7 +479,6 @@
   $ echo d > d
   $ hg ci -Am1
   adding d
-  committing subrepository s
   $ hg up 3
   2 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg -R s up 5
@@ -491,7 +486,6 @@
   $ echo e > e
   $ hg ci -Am1
   adding e
-  committing subrepository s
 
   $ hg up 5
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -519,8 +513,6 @@
   $ hg -R testdelete add
   adding testdelete/.hgsub (glob)
   $ hg -R testdelete ci -m "nested 1 & 2 added"
-  committing subrepository nested
-  committing subrepository nested2
   $ echo nested = nested > testdelete/.hgsub
   $ hg -R testdelete ci -m "nested 2 deleted"
   $ cat testdelete/.hgsubstate
@@ -550,8 +542,6 @@
   $ hg -R main add
   adding main/.hgsub (glob)
   $ hg -R main ci -m "add subrepos"
-  committing subrepository nested_absolute
-  committing subrepository nested_relative
   $ cd ..
   $ hg clone mercurial/main mercurial2/main
   updating to branch default
@@ -574,7 +564,6 @@
   $ echo s = s > repo/.hgsub
   $ hg -R repo ci -Am1
   adding .hgsub
-  committing subrepository s
   $ hg clone repo repo2
   updating to branch default
   cloning subrepo s from $TESTTMP/sub/repo/s (glob)
@@ -589,7 +578,6 @@
   $ hg -R repo2/s ci -m3
   created new head
   $ hg -R repo2 ci -m3
-  committing subrepository s
   $ hg -q -R repo2 push
   abort: push creates new remote head 9d66565e64e1!
   (did you forget to merge? use push -f to force)
@@ -699,7 +687,6 @@
   $ echo subrepo-2 = subrepo-2 >> .hgsub
   $ hg add .hgsub
   $ hg ci -m 'Added subrepos'
-  committing subrepository subrepo-1
   committing subrepository subrepo-2
   $ hg st subrepo-2/file
 
@@ -857,17 +844,16 @@
 
   $ hg rm -f .hgsubstate
   $ hg ci -mrm
-  committing subrepository s
-  committing subrepository t
-  created new head
+  nothing changed
+  [1]
   $ hg log -vr tip
-  changeset:   14:3941e0aa5236
+  changeset:   13:925c17564ef8
   tag:         tip
-  parent:      11:365661e5936a
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
+  files:       .hgsubstate
   description:
-  rm
+  13
   
   
 
@@ -875,9 +861,11 @@
 
   $ hg rm .hgsub
   $ hg ci -mrm2
+  created new head
   $ hg log -vr tip
-  changeset:   15:8b31de9d13d1
+  changeset:   14:2400bccd50af
   tag:         tip
+  parent:      11:365661e5936a
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   files:       .hgsub .hgsubstate
@@ -888,13 +876,13 @@
 Test issue3153: diff -S with deleted subrepos
 
   $ hg diff --nodates -S -c .
-  diff -r 3941e0aa5236 -r 8b31de9d13d1 .hgsub
+  diff -r 365661e5936a -r 2400bccd50af .hgsub
   --- a/.hgsub
   +++ /dev/null
   @@ -1,2 +0,0 @@
   -s = s
   -t = t
-  diff -r 3941e0aa5236 -r 8b31de9d13d1 .hgsubstate
+  diff -r 365661e5936a -r 2400bccd50af .hgsubstate
   --- a/.hgsubstate
   +++ /dev/null
   @@ -1,2 +0,0 @@
@@ -909,7 +897,6 @@
   $ hg add .hgsub
   $ hg init s
   $ hg ci -m0
-  committing subrepository s
 Adding with an explicit path in a subrepo adds the file
   $ echo c1 > f1
   $ echo c2 > s/f2
@@ -923,7 +910,6 @@
   $ hg ci -R s -m0
   $ hg ci -Am1
   adding f1
-  committing subrepository s
 Adding with an explicit path in a subrepo with -S has the same behavior
   $ echo c3 > f3
   $ echo c4 > s/f4
@@ -937,7 +923,6 @@
   $ hg ci -R s -m1
   $ hg ci -Ama2
   adding f3
-  committing subrepository s
 Adding without a path or pattern silently ignores subrepos
   $ echo c5 > f5
   $ echo c6 > s/f6
@@ -956,7 +941,6 @@
   adding f6
   adding f7
   $ hg ci -m3
-  committing subrepository s
 Adding without a path or pattern with -S also adds files in subrepos
   $ echo c8 > f8
   $ echo c9 > s/f9
@@ -975,7 +959,6 @@
   A s/f9
   $ hg ci -R s -m3
   $ hg ci -m4
-  committing subrepository s
 Adding with a pattern silently ignores subrepos
   $ echo c11 > fm11
   $ echo c12 > fn12
@@ -998,7 +981,6 @@
   adding fn14
   $ hg ci -Am5
   adding fn12
-  committing subrepository s
 Adding with a pattern with -S also adds matches in subrepos
   $ echo c15 > fm15
   $ echo c16 > fn16
@@ -1021,7 +1003,6 @@
   adding fn18
   $ hg ci -Am6
   adding fn16
-  committing subrepository s
 
 Test behavior of forget for explicit path in subrepo:
 Forgetting an explicit path in a subrepo untracks the file