# HG changeset patch
# User Vadim Gelfer
# Date 1148279965 25200
# Node ID 737deea2442c63f03f2c317dac697990c3add98f
# Parent de0c05afa5113c1602e30f8f27de31bd11de596c# Parent d5c9ba87a7387f6c8a2cd266cec991f9bb4ff35e
merge with crew.
diff -r de0c05afa511 -r 737deea2442c contrib/mercurial.el
--- 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."
diff -r de0c05afa511 -r 737deea2442c contrib/win32/ReadMe.html
--- 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.
Configuration notes
- The default editor is 'vi'. You can set the EDITOR environment variable
- (or HGEDITOR) to specify your preference.
+ 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:
+
+[ui]
+editor = whatever
+
+
Reporting problems
diff -r de0c05afa511 -r 737deea2442c doc/hgrc.5.txt
--- 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) /.hg/hgrc::
Per-repository configuration options that only apply in a
diff -r de0c05afa511 -r 737deea2442c hgext/notify.py
--- 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':
diff -r de0c05afa511 -r 737deea2442c mercurial/hgweb/__init__.py
--- 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 = {}
diff -r de0c05afa511 -r 737deea2442c mercurial/localrepo.py
--- 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
diff -r de0c05afa511 -r 737deea2442c mercurial/manifest.py
--- 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 '')
diff -r de0c05afa511 -r 737deea2442c mercurial/packagescan.py
--- 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
#
@@ -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 """
# 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):
"""
diff -r de0c05afa511 -r 737deea2442c mercurial/util.py
--- 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 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()
diff -r de0c05afa511 -r 737deea2442c mercurial/util_win32.py
--- 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')
diff -r de0c05afa511 -r 737deea2442c setup.py
--- 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
diff -r de0c05afa511 -r 737deea2442c tests/test-globalopts
--- 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
diff -r de0c05afa511 -r 737deea2442c tests/test-tags
--- 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
diff -r de0c05afa511 -r 737deea2442c tests/test-tags.out
--- 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