changeset 2334:737deea2442c

merge with crew.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Sun, 21 May 2006 23:39:25 -0700
parents de0c05afa511 (current diff) d5c9ba87a738 (diff)
children f0680b2d1d64
files
diffstat 14 files changed, 254 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/mercurial.el	Sun May 21 23:39:07 2006 -0700
+++ b/contrib/mercurial.el	Sun May 21 23:39:25 2006 -0700
@@ -382,14 +382,27 @@
       (set-buffer hg-prev-buffer))
     (let ((path (or default (buffer-file-name))))
       (if (or (not path) current-prefix-arg)
-	  (expand-file-name
-	   (read-file-name (format "File, directory or pattern%s: "
-				   (or prompt ""))
-			   (and path (file-name-directory path))
-			   nil nil
-			   (and path (file-name-nondirectory path))
-			   'hg-file-history))
-	path))))
+          (expand-file-name
+           (eval (list* 'read-file-name
+                        (format "File, directory or pattern%s: "
+                                (or prompt ""))
+                        (and path (file-name-directory path))
+                        nil nil
+                        (and path (file-name-nondirectory path))
+                        (if hg-running-xemacs
+                            (cons (quote 'hg-file-history) nil)
+                          nil))))
+        path))))
+
+(defun hg-read-number (&optional prompt default)
+  "Read a integer value."
+  (save-excursion
+    (if (or (not default) current-prefix-arg)
+        (string-to-number
+         (eval (list* 'read-string
+                      (or prompt "") 
+                      (if default (cons (format "%d" default) nil) nil))))
+      default)))
 
 (defun hg-read-config ()
   "Return an alist of (key . value) pairs of Mercurial config data.
@@ -950,36 +963,55 @@
     (kill-entire-line))
   (run-hooks 'hg-log-mode-hook))
 
-(defun hg-log (path &optional rev1 rev2)
-  "Display the revision history of PATH, between REV1 and REV2.
-REV1 defaults to hg-log-limit changes from the tip revision, while
-REV2 defaults to the tip.
+(defun hg-log (path &optional rev1 rev2 log-limit)
+  "Display the revision history of PATH.
+History is displayed between REV1 and REV2.
+Number of displayed changesets is limited to LOG-LIMIT.
+REV1 defaults to the tip, while
+REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
+LOG-LIMIT defaults to `hg-log-limit'.
 With a prefix argument, prompt for each parameter."
   (interactive (list (hg-read-file-name " to log")
-		     (hg-read-rev " to start with" "-1")
-		     (hg-read-rev " to end with" (format "-%d" hg-log-limit))))
+                     (hg-read-rev " to start with"
+                                  "tip")
+                     (hg-read-rev " to end with"
+                                  (format "%d" (- hg-rev-completion-limit)))
+                     (hg-read-number "Output limited to: "
+                                     hg-log-limit)))
   (let ((a-path (hg-abbrev-file-name path))
-	(r1 (or rev1 (format "-%d" hg-log-limit)))
-	(r2 (or rev2 rev1 "-1")))
+        (r1 (or rev1 (format "-%d" hg-rev-completion-limit)))
+        (r2 (or rev2 rev1 "tip"))
+        (limit (format "%d" (or log-limit hg-log-limit))))
     (hg-view-output ((if (equal r1 r2)
-			 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
-		       (format "Mercurial: Log from rev %s to %s of %s"
-			       r1 r2 a-path)))
-      (let ((revs (format "%s:%s" r1 r2)))
-	(if (> (length path) (length (hg-root path)))
-	    (call-process (hg-binary) nil t nil "log" "-r" revs path)
-	  (call-process (hg-binary) nil t nil "log" "-r" revs)))
+                         (format "Mercurial: Log of rev %s of %s" rev1 a-path)
+                       (format 
+                        "Mercurial: at most %s log(s) from rev %s to %s of %s"
+                        limit r1 r2 a-path)))
+      (eval (list* 'call-process (hg-binary) nil t nil
+                   "log"
+                   "-r" (format "%s:%s" r1 r2)
+                   "-l" limit
+                   (if (> (length path) (length (hg-root path)))
+                       (cons path nil)
+                     nil)))
       (hg-log-mode))))
 
-(defun hg-log-repo (path &optional rev1 rev2)
+(defun hg-log-repo (path &optional rev1 rev2 log-limit)
   "Display the revision history of the repository containing PATH.
-History is displayed between REV1, which defaults to the tip, and
-REV2, which defaults to the initial revision.
-Variable hg-log-limit controls the number of log entries displayed."
+History is displayed between REV1 and REV2.
+Number of displayed changesets is limited to LOG-LIMIT,
+REV1 defaults to the tip, while
+REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
+LOG-LIMIT defaults to `hg-log-limit'.
+With a prefix argument, prompt for each parameter."
   (interactive (list (hg-read-file-name " to log")
-		     (hg-read-rev " to start with" "tip")
-		     (hg-read-rev " to end with" (format "-%d" hg-log-limit))))
-  (hg-log (hg-root path) rev1 rev2))
+                     (hg-read-rev " to start with"
+                                  "tip")
+                     (hg-read-rev " to end with" 
+                                  (format "%d" (- hg-rev-completion-limit)))
+                     (hg-read-number "Output limited to: "
+                                     hg-log-limit)))
+  (hg-log (hg-root path) rev1 rev2 log-limit))
 
 (defun hg-outgoing (&optional repo)
   "Display changesets present locally that are not present in REPO."
--- a/contrib/win32/ReadMe.html	Sun May 21 23:39:07 2006 -0700
+++ b/contrib/win32/ReadMe.html	Sun May 21 23:39:25 2006 -0700
@@ -90,8 +90,14 @@
       other Mercurial commands should work fine for you.</p>
 
     <h1>Configuration notes</h1>
-    <p>The default editor is 'vi'. You can set the EDITOR environment variable
-    (or HGEDITOR) to specify your preference. </p>
+    <p>The default editor for commit messages is 'vi'. You can set the EDITOR
+    (or HGEDITOR) environment variable to specify your preference or set it in
+    mercurial.ini:</p>
+    <pre>
+[ui]
+editor = whatever
+</pre>
+
 
     <h1>Reporting problems</h1>
 
--- a/doc/hgrc.5.txt	Sun May 21 23:39:07 2006 -0700
+++ b/doc/hgrc.5.txt	Sun May 21 23:39:25 2006 -0700
@@ -36,11 +36,14 @@
     files override per-installation options.
 
 (Unix)    $HOME/.hgrc::
-(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini
+(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
+(Windows) $HOME\Mercurial.ini::
     Per-user configuration file, for the user running Mercurial.
     Options in this file apply to all Mercurial commands executed by
     any user in any directory.  Options in this file override
     per-installation and per-system options.
+    On Windows system, one of these is chosen exclusively according
+    to definition of HOME environment variable.
 
 (Unix, Windows) <repo>/.hg/hgrc::
     Per-repository configuration options that only apply in a
--- a/hgext/notify.py	Sun May 21 23:39:07 2006 -0700
+++ b/hgext/notify.py	Sun May 21 23:39:25 2006 -0700
@@ -99,7 +99,9 @@
 
     def __init__(self, ui, repo, hooktype):
         self.ui = ui
-        self.ui.readconfig(self.ui.config('notify', 'config'))
+        cfg = self.ui.config('notify', 'config')
+        if cfg:
+            self.ui.readconfig(cfg)
         self.repo = repo
         self.stripcount = int(self.ui.config('notify', 'strip', 0))
         self.root = self.strip(self.repo.root)
@@ -123,7 +125,7 @@
 
         path = util.pconvert(path)
         count = self.stripcount
-        while path and count >= 0:
+        while count > 0:
             c = path.find('/')
             if c == -1:
                 break
@@ -225,6 +227,8 @@
             if not msgtext.endswith('\n'):
                 self.ui.write('\n')
         else:
+            self.ui.status(_('notify: sending %d subscribers %d changes\n') %
+                             (len(self.subs), count))
             mail = self.ui.sendmail()
             mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
 
@@ -250,7 +254,12 @@
     if used as changegroup hook, send one email for all changesets in
     changegroup. else send one email per changeset.'''
     n = notifier(ui, repo, hooktype)
-    if not n.subs or n.skipsource(source):
+    if not n.subs:
+        ui.debug(_('notify: no subscribers to this repo\n'))
+        return
+    if n.skipsource(source):
+        ui.debug(_('notify: changes have source "%s" - skipping\n') %
+                  source)
         return
     node = bin(node)
     if hooktype == 'changegroup':
--- a/mercurial/hgweb/__init__.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/hgweb/__init__.py	Sun May 21 23:39:25 2006 -0700
@@ -519,7 +519,8 @@
         mnode = hex(mn)
         mf = man.read(mn)
         rev = man.rev(mn)
-        node = self.repo.changelog.node(rev)
+        changerev = man.linkrev(mn)
+        node = self.repo.changelog.node(changerev)
         mff = man.readflags(mn)
 
         files = {}
--- a/mercurial/localrepo.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/localrepo.py	Sun May 21 23:39:25 2006 -0700
@@ -166,37 +166,44 @@
                     return
                 s = l.split(" ", 1)
                 if len(s) != 2:
-                    self.ui.warn(_("%s: ignoring invalid tag\n") % context)
+                    self.ui.warn(_("%s: cannot parse entry\n") % context)
                     return
                 node, key = s
+                key = key.strip()
                 try:
                     bin_n = bin(node)
                 except TypeError:
-                    self.ui.warn(_("%s: ignoring invalid tag\n") % context)
+                    self.ui.warn(_("%s: node '%s' is not well formed\n") %
+                                 (context, node))
                     return
                 if bin_n not in self.changelog.nodemap:
-                    self.ui.warn(_("%s: ignoring invalid tag\n") % context)
+                    self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
+                                 (context, key))
                     return
-                self.tagscache[key.strip()] = bin_n
+                self.tagscache[key] = bin_n
 
-            # read each head of the tags file, ending with the tip
+            # read the tags file from each head, ending with the tip,
             # and add each tag found to the map, with "newer" ones
             # taking precedence
+            heads = self.heads()
+            heads.reverse()
             fl = self.file(".hgtags")
-            h = fl.heads()
-            h.reverse()
-            for r in h:
+            for node in heads:
+                change = self.changelog.read(node)
+                rev = self.changelog.rev(node)
+                fn, ff = self.manifest.find(change[0], '.hgtags')
+                if fn is None: continue
                 count = 0
-                for l in fl.read(r).splitlines():
+                for l in fl.read(fn).splitlines():
                     count += 1
-                    parsetag(l, ".hgtags:%d" % count)
-
+                    parsetag(l, _(".hgtags (rev %d:%s), line %d") %
+                             (rev, short(node), count))
             try:
                 f = self.opener("localtags")
                 count = 0
                 for l in f:
                     count += 1
-                    parsetag(l, "localtags:%d" % count)
+                    parsetag(l, _("localtags, line %d") % count)
             except IOError:
                 pass
 
--- a/mercurial/manifest.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/manifest.py	Sun May 21 23:39:25 2006 -0700
@@ -43,48 +43,61 @@
     def diff(self, a, b):
         return mdiff.textdiff(str(a), str(b))
 
+    def _search(self, m, s, lo=0, hi=None):
+        '''return a tuple (start, end) that says where to find s within m.
+
+        If the string is found m[start:end] are the line containing
+        that string.  If start == end the string was not found and
+        they indicate the proper sorted insertion point.  This was
+        taken from bisect_left, and modified to find line start/end as
+        it goes along.
+
+        m should be a buffer or a string
+        s is a string'''
+        def advance(i, c):
+            while i < lenm and m[i] != c:
+                i += 1
+            return i
+        lenm = len(m)
+        if not hi:
+            hi = lenm
+        while lo < hi:
+            mid = (lo + hi) // 2
+            start = mid
+            while start > 0 and m[start-1] != '\n':
+                start -= 1
+            end = advance(start, '\0')
+            if m[start:end] < s:
+                # we know that after the null there are 40 bytes of sha1
+                # this translates to the bisect lo = mid + 1
+                lo = advance(end + 40, '\n') + 1
+            else:
+                # this translates to the bisect hi = mid
+                hi = start
+        end = advance(lo, '\0')
+        found = m[lo:end]
+        if cmp(s, found) == 0:
+            # we know that after the null there are 40 bytes of sha1
+            end = advance(end + 40, '\n')
+            return (lo, end+1)
+        else:
+            return (lo, lo)
+
+    def find(self, node, f):
+        '''look up entry for a single file efficiently.
+        return (node, flag) pair if found, (None, None) if not.'''
+        if self.mapcache and node == self.mapcache[0]:
+            return self.mapcache[1].get(f), self.mapcache[2].get(f)
+        text = self.revision(node)
+        start, end = self._search(text, f)
+        if start == end:
+            return None, None
+        l = text[start:end]
+        f, n = l.split('\0')
+        return bin(n[:40]), n[40:-1] == 'x'
+
     def add(self, map, flags, transaction, link, p1=None, p2=None,
             changed=None):
-
-        # returns a tuple (start, end).  If the string is found
-        # m[start:end] are the line containing that string.  If start == end
-        # the string was not found and they indicate the proper sorted 
-        # insertion point.  This was taken from bisect_left, and modified
-        # to find line start/end as it goes along.
-        #
-        # m should be a buffer or a string
-        # s is a string
-        #
-        def manifestsearch(m, s, lo=0, hi=None):
-            def advance(i, c):
-                while i < lenm and m[i] != c:
-                    i += 1
-                return i
-            lenm = len(m)
-            if not hi:
-                hi = lenm
-            while lo < hi:
-                mid = (lo + hi) // 2
-                start = mid
-                while start > 0 and m[start-1] != '\n':
-                    start -= 1
-                end = advance(start, '\0')
-                if m[start:end] < s:
-                    # we know that after the null there are 40 bytes of sha1
-                    # this translates to the bisect lo = mid + 1
-                    lo = advance(end + 40, '\n') + 1
-                else:
-                    # this translates to the bisect hi = mid
-                    hi = start
-            end = advance(lo, '\0')
-            found = m[lo:end]
-            if cmp(s, found) == 0: 
-                # we know that after the null there are 40 bytes of sha1
-                end = advance(end + 40, '\n')
-                return (lo, end+1)
-            else:
-                return (lo, lo)
-
         # apply the changes collected during the bisect loop to our addlist
         # return a delta suitable for addrevision
         def addlistdelta(addlist, x):
@@ -137,7 +150,7 @@
             for w in work:
                 f = w[0]
                 # bs will either be the index of the item or the insert point
-                start, end = manifestsearch(addbuf, f, start)
+                start, end = self._search(addbuf, f, start)
                 if w[1] == 0:
                     l = "%s\000%s%s\n" % (f, hex(map[f]),
                                           flags[f] and "x" or '')
--- a/mercurial/packagescan.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/packagescan.py	Sun May 21 23:39:25 2006 -0700
@@ -1,5 +1,6 @@
 # packagescan.py - Helper module for identifing used modules.
 # Used for the py2exe distutil.
+# This module must be the first mercurial module imported in setup.py
 #
 # Copyright 2005 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
 #
@@ -8,25 +9,58 @@
 import glob
 import os
 import sys
-import demandload
 import ihooks
+import types
+import string
+
+# Install this module as fake demandload module
+sys.modules['mercurial.demandload'] = sys.modules[__name__]
 
-requiredmodules = {} # Will contain the modules imported by demandload
+# Requiredmodules contains the modules imported by demandload.
+# Please note that demandload can be invoked before the 
+# mercurial.packagescan.scan method is invoked in case a mercurial
+# module is imported.
+requiredmodules = {} 
 def demandload(scope, modules):
-    """ fake demandload function that collects the required modules """
+    """ fake demandload function that collects the required modules 
+        foo            import foo
+        foo bar        import foo, bar
+        foo.bar        import foo.bar
+        foo:bar        from foo import bar
+        foo:bar,quux   from foo import bar, quux
+        foo.bar:quux   from foo.bar import quux"""
+
     for m in modules.split():
         mod = None
         try:
-            module, submodules = m.split(':')
-            submodules = submodules.split(',')
+            module, fromlist = m.split(':')
+            fromlist = fromlist.split(',')
         except:
             module = m
-            submodules = []
-        mod = __import__(module, scope, scope, submodules)
-        scope[module] = mod
-        requiredmodules[mod.__name__] = 1
+            fromlist = []
+        mod = __import__(module, scope, scope, fromlist)
+        if fromlist == []:
+            # mod is only the top package, but we need all packages
+            comp = module.split('.')
+            i = 1
+            mn = comp[0]
+            while True:
+                # mn and mod.__name__ might not be the same
+                scope[mn] = mod
+                requiredmodules[mod.__name__] = 1
+                if len(comp) == i: break
+                mod = getattr(mod,comp[i]) 
+                mn = string.join(comp[:i+1],'.')
+                i += 1
+        else:
+            # mod is the last package in the component list
+            requiredmodules[mod.__name__] = 1
+            for f in fromlist:
+                scope[f] = getattr(mod,f)
+                if type(scope[f]) == types.ModuleType:
+                    requiredmodules[scope[f].__name__] = 1
 
-def getmodules(libpath,packagename):
+def scan(libpath,packagename):
     """ helper for finding all required modules of package <packagename> """
     # Use the package in the build directory
     libpath = os.path.abspath(libpath)
@@ -45,8 +79,6 @@
     pymodulefiles = glob.glob('*.py')
     extmodulefiles = glob.glob('*.pyd')
     os.chdir(cwd)
-    # Install a fake demandload module
-    sys.modules['mercurial.demandload'] = sys.modules['mercurial.packagescan']
     # Import all python modules and by that run the fake demandload
     for m in pymodulefiles:
         if m == '__init__.py': continue
@@ -62,8 +94,9 @@
         fullname = packagename+'.'+mname
         __import__(fullname,tmp,tmp)
         requiredmodules[fullname] = 1
-    includes = requiredmodules.keys()
-    return includes
+
+def getmodules():
+    return requiredmodules.keys()
 
 def importfrom(filename):
     """
--- a/mercurial/util.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/util.py	Sun May 21 23:39:25 2006 -0700
@@ -94,7 +94,7 @@
     """apply the patch <patchname> to the working directory.
     a list of patched files is returned"""
     patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
-    fp = os.popen('"%s" -p%d < "%s"' % (patcher, strip, patchname))
+    fp = os.popen('%s -p%d < "%s"' % (patcher, strip, patchname))
     files = {}
     for line in fp:
         line = line.rstrip()
--- a/mercurial/util_win32.py	Sun May 21 23:39:07 2006 -0700
+++ b/mercurial/util_win32.py	Sun May 21 23:39:25 2006 -0700
@@ -194,7 +194,7 @@
         # We are on win < nt: fetch the APPDATA directory location and use
         # the parent directory as the user home dir.
         appdir = shell.SHGetPathFromIDList(
-            qshell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
+            shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
         userdir = os.path.dirname(appdir)
     return os.path.join(userdir, 'mercurial.ini')
 
--- a/setup.py	Sun May 21 23:39:07 2006 -0700
+++ b/setup.py	Sun May 21 23:39:25 2006 -0700
@@ -13,6 +13,8 @@
 from distutils.core import setup, Extension
 from distutils.command.install_data import install_data
 
+# mercurial.packagescan must be the first mercurial module imported
+import mercurial.packagescan
 import mercurial.version
 
 # py2exe needs to be installed to work
@@ -36,7 +38,6 @@
     # Due to the use of demandload py2exe is not finding the modules.
     # packagescan.getmodules creates a list of modules included in
     # the mercurial package plus depdent modules.
-    import mercurial.packagescan
     from py2exe.build_exe import py2exe as build_exe
 
     class py2exe_for_demandload(build_exe):
@@ -54,12 +55,10 @@
                 self.includes = []
             else:
                 self.includes = self.includes.split(',')
-            self.includes += mercurial.packagescan.getmodules(self.build_lib,
-                                                              'mercurial')
-            self.includes += mercurial.packagescan.getmodules(self.build_lib,
-                                                              'mercurial/hgweb')
-            self.includes += mercurial.packagescan.getmodules(self.build_lib,
-                                                              'hgext')
+            mercurial.packagescan.scan(self.build_lib,'mercurial')
+            mercurial.packagescan.scan(self.build_lib,'mercurial/hgweb')
+            mercurial.packagescan.scan(self.build_lib,'hgext')
+            self.includes += mercurial.packagescan.getmodules()
             build_exe.finalize_options(self)
 except ImportError:
     py2exe_for_demandload = None
--- a/tests/test-globalopts	Sun May 21 23:39:07 2006 -0700
+++ b/tests/test-globalopts	Sun May 21 23:39:25 2006 -0700
@@ -62,7 +62,7 @@
 hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
 
 echo %% --version
-hg --version -q | sed 's/version [a-f0-9+]*/version xxx/'
+hg --version -q | sed 's/version \([a-f0-9+]*\|unknown\)/version xxx/'
 
 echo %% -h/--help
 hg -h
--- a/tests/test-tags	Sun May 21 23:39:07 2006 -0700
+++ b/tests/test-tags	Sun May 21 23:39:25 2006 -0700
@@ -32,12 +32,31 @@
 hg status
 
 hg commit -m "merge" -d "1000000 0"
+
+# create fake head, make sure tag not visible afterwards
+cp .hgtags tags
+hg tag -d "1000000 0" last
+hg rm .hgtags
+hg commit -m "remove" -d "1000000 0"
+
+mv tags .hgtags
+hg add .hgtags
+hg commit -m "readd" -d "1000000 0"
+
+hg tags
+
 # invalid tags
 echo "spam" >> .hgtags
 echo >> .hgtags
 echo "foo bar" >> .hgtags
 echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags
 hg commit -m "tags" -d "1000000 0"
+
+# report tag parse error on other head
+hg up 3
+echo 'x y' >> .hgtags
+hg commit -m "head" -d "1000000 0"
+
 hg tags
 hg tip
 
--- a/tests/test-tags.out	Sun May 21 23:39:07 2006 -0700
+++ b/tests/test-tags.out	Sun May 21 23:39:25 2006 -0700
@@ -16,17 +16,26 @@
 (branch merge, don't forget to commit)
 8216907a933d+8a3ca90d111d+ tip
 M .hgtags
-.hgtags:2: ignoring invalid tag
-.hgtags:4: ignoring invalid tag
-localtags:1: ignoring invalid tag
-tip                                4:fd868a874787a7b5af31e1675666ce691c803035
+tip                                6:c6af9d771a81bb9c7f267ec03491224a9f8ba1cd
 first                              0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4
-changeset:   4:fd868a874787
-.hgtags:2: ignoring invalid tag
-.hgtags:4: ignoring invalid tag
-localtags:1: ignoring invalid tag
+.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry
+.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed
+localtags, line 1: tag 'invalid' refers to unknown node
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry
+.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed
+.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed
+localtags, line 1: tag 'invalid' refers to unknown node
+tip                                8:4ca6f1b1a68c77be687a03aaeb1614671ba59b20
+first                              0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4
+changeset:   8:4ca6f1b1a68c
+.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry
+.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed
+.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed
+localtags, line 1: tag 'invalid' refers to unknown node
 tag:         tip
+parent:      3:b2ef3841386b
 user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
-summary:     tags
+summary:     head