changeset 705:574869103985

Merge with TAH manifest hash: 197e0d1a0d7376a9eb72381330462f06490ab821
author mpm@selenic.com
date Thu, 14 Jul 2005 22:56:55 -0800
parents 51eb248d3348 (current diff) 5ca319a641e1 (diff)
children 5107a7b6b14a 9e0f3ba4a9c2
files contrib/convert-repo doc/hgrc.5.txt mercurial/commands.py mercurial/hg.py mercurial/lock.py mercurial/util.py tests/test-bad-pull tests/test-bad-pull.out tests/test-basic.out tests/test-copy.out tests/test-flags.out tests/test-tag.out tests/test-up-local-change.out
diffstat 12 files changed, 331 insertions(+), 246 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hgrc.5.txt	Thu Jul 14 22:37:46 2005 -0800
+++ b/doc/hgrc.5.txt	Thu Jul 14 22:56:55 2005 -0800
@@ -93,22 +93,22 @@
 ui::
   User interface controls.
   debug;;
-    Print debugging information.  True or False.  Default is True.
+    Print debugging information.  True or False.  Default is False.
   editor;;
-    The editor to use during a commit.  Default is "vi".
+    The editor to use during a commit.  Default is $EDITOR or "vi".
+  interactive;;
+    Allow to prompt the user.  True or False.  Default is True.
   merge;;
     The conflict resolution program to use during a manual merge.
-    Default is "hgeditor".
+    Default is "hgmerge".
   quiet;;
-    Reduce the amount of output printed.  True or False.  Default is
-    False.
+    Reduce the amount of output printed.  True or False.  Default is False.
   username;;
     The committer of a changeset created when running "commit".
     Typically a person's name and email address, e.g. "Fred Widget
-    <fred@example.com>".  Default is username@hostname.
+    <fred@example.com>".  Default is $EMAIL or username@hostname.
   verbose;;
-    Increase the amount of output printed.  True or False.  Default is
-    False.
+    Increase the amount of output printed.  True or False.  Default is False.
 
 AUTHOR
 ------
--- a/mercurial/commands.py	Thu Jul 14 22:37:46 2005 -0800
+++ b/mercurial/commands.py	Thu Jul 14 22:56:55 2005 -0800
@@ -5,20 +5,22 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-from demandload import *
-demandload(globals(), "os re sys signal")
+from demandload import demandload
+demandload(globals(), "os re sys signal shutil")
 demandload(globals(), "fancyopts ui hg util")
 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
 demandload(globals(), "errno socket version struct")
 
-class UnknownCommand(Exception): pass
+class UnknownCommand(Exception):
+    """Exception raised if command is not in the command table."""
 
 def filterfiles(filters, files):
-    l = [ x for x in files if x in filters ]
+    l = [x for x in files if x in filters]
 
     for t in filters:
-        if t and t[-1] != "/": t += "/"
-        l += [ x for x in files if x.startswith(t) ]
+        if t and t[-1] != "/":
+            t += "/"
+        l += [x for x in files if x.startswith(t)]
     return l
 
 def relfilter(repo, files):
@@ -30,21 +32,25 @@
 def relpath(repo, args):
     cwd = repo.getcwd()
     if cwd:
-        return [ util.pconvert(os.path.normpath(os.path.join(cwd, x))) for x in args ]
+        return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
+                for x in args]
     return args
 
 revrangesep = ':'
 
-def revrange(ui, repo, revs = [], revlog = None):
+def revrange(ui, repo, revs, revlog=None):
     if revlog is None:
         revlog = repo.changelog
     revcount = revlog.count()
     def fix(val, defval):
-        if not val: return defval
+        if not val:
+            return defval
         try:
             num = int(val)
-            if str(num) != val: raise ValueError
-            if num < 0: num += revcount
+            if str(num) != val:
+                raise ValueError
+            if num < 0:
+                num += revcount
             if not (0 <= num < revcount):
                 raise ValueError
         except ValueError:
@@ -85,11 +91,14 @@
         'b': lambda: os.path.basename(repo.root),
         }
 
-    if node: expander.update(node_expander)
+    if node:
+        expander.update(node_expander)
     if node and revwidth is not None:
         expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
-    if total is not None: expander['N'] = lambda: str(total)
-    if seqno is not None: expander['n'] = lambda: str(seqno)
+    if total is not None:
+        expander['N'] = lambda: str(total)
+    if seqno is not None:
+        expander['n'] = lambda: str(seqno)
     if total is not None and seqno is not None:
         expander['n'] = lambda:str(seqno).zfill(len(str(total)))
 
@@ -106,7 +115,7 @@
         i += 1
     return ''.join(newname)
 
-def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
+def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
     def date(c):
         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
 
@@ -120,13 +129,15 @@
     if node2:
         change = repo.changelog.read(node2)
         mmap2 = repo.manifest.read(change[0])
-        def read(f): return repo.file(f).read(mmap2[f])
         date2 = date(change)
+        def read(f):
+            return repo.file(f).read(mmap2[f])
     else:
         date2 = time.asctime()
         if not node1:
             node1 = repo.dirstate.parents()[0]
-        def read(f): return repo.wfile(f).read()
+        def read(f):
+            return repo.wfile(f).read()
 
     if ui.quiet:
         r = None
@@ -222,8 +233,8 @@
         "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
     )
 
-def help(ui, cmd=None):
-    '''show help for a given command or all commands'''
+def help_(ui, cmd=None):
+    """show help for a given command or all commands"""
     if cmd:
         try:
             i = find(cmd)
@@ -231,12 +242,16 @@
 
             if i[1]:
                 for s, l, d, c in i[1]:
-                    opt=' '
-                    if s: opt = opt + '-' + s + ' '
-                    if l: opt = opt + '--' + l + ' '
-                    if d: opt = opt + '(' + str(d) + ')'
+                    opt = ' '
+                    if s:
+                        opt = opt + '-' + s + ' '
+                    if l:
+                        opt = opt + '--' + l + ' '
+                    if d:
+                        opt = opt + '(' + str(d) + ')'
                     ui.write(opt, "\n")
-                    if c: ui.write('   %s\n' % c)
+                    if c:
+                        ui.write('   %s\n' % c)
                 ui.write("\n")
 
             ui.write(i[0].__doc__, "\n")
@@ -273,9 +288,9 @@
 
 # Commands start here, listed alphabetically
 
-def add(ui, repo, file, *files):
+def add(ui, repo, file1, *files):
     '''add the specified files on the next commit'''
-    repo.add(relpath(repo, (file,) + files))
+    repo.add(relpath(repo, (file1,) + files))
 
 def addremove(ui, repo, *files):
     """add all new files, delete all missing files"""
@@ -296,7 +311,7 @@
     repo.add(u)
     repo.remove(d)
 
-def annotate(u, repo, file, *files, **ops):
+def annotate(u, repo, file1, *files, **ops):
     """show changeset information per file line"""
     def getnode(rev):
         return hg.short(repo.changelog.node(rev))
@@ -326,24 +341,26 @@
         node = repo.changelog.lookup(ops['revision'])
     change = repo.changelog.read(node)
     mmap = repo.manifest.read(change[0])
-    for f in relpath(repo, (file,) + files):
+    for f in relpath(repo, (file1,) + files):
         lines = repo.file(f).annotate(mmap[f])
         pieces = []
 
         for o, f in opmap:
             if ops[o]:
-                l = [ f(n) for n,t in lines ]
+                l = [f(n) for n, dummy in lines]
                 m = max(map(len, l))
-                pieces.append([ "%*s" % (m, x) for x in l])
+                pieces.append(["%*s" % (m, x) for x in l])
 
-        for p,l in zip(zip(*pieces), lines):
+        for p, l in zip(zip(*pieces), lines):
             u.write(" ".join(p) + ": " + l[1])
 
-def cat(ui, repo, file, rev = [], **opts):
+def cat(ui, repo, file1, rev=None, **opts):
     """output the latest or given revision of a file"""
-    r = repo.file(relpath(repo, [file])[0])
-    n = r.tip()
-    if rev: n = r.lookup(rev)
+    r = repo.file(relpath(repo, [file1])[0])
+    if rev:
+        n = r.lookup(rev)
+    else:
+        n = r.tip()
     if opts['output'] and opts['output'] != '-':
         try:
             outname = make_filename(repo, r, opts['output'], node=n)
@@ -356,7 +373,7 @@
         fp = sys.stdout
     fp.write(r.read(n))
 
-def clone(ui, source, dest = None, **opts):
+def clone(ui, source, dest=None, **opts):
     """make a copy of an existing repository"""
     if dest is None:
         dest = os.path.basename(os.path.normpath(source))
@@ -365,33 +382,34 @@
         ui.warn("abort: destination '%s' already exists\n" % dest)
         return 1
 
-    class dircleanup:
-        def __init__(self, dir):
-            import shutil
+    class Dircleanup:
+        def __init__(self, dir_):
             self.rmtree = shutil.rmtree
-            self.dir = dir
-            os.mkdir(dir)
+            self.dir_ = dir_
+            os.mkdir(dir_)
         def close(self):
-            self.dir = None
+            self.dir_ = None
         def __del__(self):
-            if self.dir:
-                self.rmtree(self.dir, True)
+            if self.dir_:
+                self.rmtree(self.dir_, True)
 
-    d = dircleanup(dest)
-    link = 0
+    d = Dircleanup(dest)
     abspath = source
     source = ui.expandpath(source)
     other = hg.repository(ui, source)
 
     if other.dev() != -1:
         abspath = os.path.abspath(source)
-
-    if other.dev() != -1 and os.stat(dest).st_dev == other.dev():
-        ui.note("cloning by hardlink\n")
-        util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
+        copyfile = (os.stat(dest).st_dev == other.dev()
+                    and getattr(os, 'link', None) or shutil.copy2)
+        if copyfile is not shutil.copy2:
+            ui.note("cloning by hardlink\n")
+        util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
+                      copyfile)
         try:
-            os.remove(os.path.join(dest, ".hg", "dirstate"))
-        except: pass
+            os.unlink(os.path.join(dest, ".hg", "dirstate"))
+        except IOError:
+            pass
 
         repo = hg.repository(ui, dest)
 
@@ -411,9 +429,12 @@
 def commit(ui, repo, *files, **opts):
     """commit the specified files or all outstanding changes"""
     text = opts['text']
-    if not text and opts['logfile']:
-        try: text = open(opts['logfile']).read()
-        except IOError: pass
+    logfile = opts['logfile']
+    if not text and logfile:
+        try:
+            text = open(logfile).read()
+        except IOError, why:
+            ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
 
     if opts['addremove']:
         addremove(ui, repo, *files)
@@ -462,12 +483,12 @@
     dc = repo.dirstate.map
     keys = dc.keys()
     keys.sort()
-    for file in keys:
-        ui.write("%c %s\n" % (dc[file][0], file))
+    for file_ in keys:
+        ui.write("%c %s\n" % (dc[file_][0], file_))
 
-def debugindex(ui, file):
+def debugindex(ui, file_):
     """dump the contents of an index file"""
-    r = hg.revlog(hg.opener(""), file, "")
+    r = hg.revlog(hg.opener(""), file_, "")
     ui.write("   rev    offset  length   base linkrev" +
              " p1           p2           nodeid\n")
     for i in range(r.count()):
@@ -476,9 +497,9 @@
                 i, e[0], e[1], e[2], e[3],
             hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
 
-def debugindexdot(ui, file):
+def debugindexdot(ui, file_):
     """dump an index DAG as a .dot file"""
-    r = hg.revlog(hg.opener(""), file, "")
+    r = hg.revlog(hg.opener(""), file_, "")
     ui.write("digraph G {\n")
     for i in range(r.count()):
         e = r.index[i]
@@ -546,9 +567,9 @@
         seqno += 1
         doexport(ui, repo, cset, seqno, total, revwidth, opts)
 
-def forget(ui, repo, file, *files):
+def forget(ui, repo, file1, *files):
     """don't add the specified files on the next commit"""
-    repo.forget(relpath(repo, (file,) + files))
+    repo.forget(relpath(repo, (file1,) + files))
 
 def heads(ui, repo):
     """show current repository heads"""
@@ -581,7 +602,7 @@
     try:
         import psyco
         psyco.full()
-    except:
+    except ImportError:
         pass
 
     patches = (patch1,) + patches
@@ -593,27 +614,32 @@
         ui.status("applying %s\n" % patch)
         pf = os.path.join(d, patch)
 
-        text = ""
-        for l in file(pf):
-            if l.startswith("--- ") or l.startswith("diff -r"): break
-            text += l
-
-        # parse values that exist when importing the result of an hg export
-        hgpatch = user = snippet = None
-        ui.debug('text:\n')
-        for t in text.splitlines():
-            ui.debug(t,'\n')
-            if t == '# HG changeset patch' or hgpatch == True:
+        text = []
+        user = None
+        hgpatch = False
+        for line in file(pf):
+            line = line.rstrip()
+            if line.startswith("--- ") or line.startswith("diff -r"):
+                break
+            elif hgpatch:
+                # parse values when importing the result of an hg export
+                if line.startswith("# User "):
+                    user = line[7:]
+                    ui.debug('User: %s\n' % user)
+                elif not line.startswith("# ") and line:
+                    text.append(line)
+                    hgpatch = False
+            elif line == '# HG changeset patch':
                 hgpatch = True
-                if t.startswith("# User "):
-                    user = t[7:]
-                    ui.debug('User: %s\n' % user)
-                if not t.startswith("# ") and t.strip() and not snippet: snippet = t
-        if snippet: text = snippet + '\n' + text
-        ui.debug('text:\n%s\n' % text)
+            else:
+                text.append(line)
 
         # make sure text isn't empty
-        if not text: text = "imported patch %s\n" % patch
+        if not text:
+            text = "imported patch %s\n" % patch
+        else:
+            text = "%s\n" % '\n'.join(text)
+        ui.debug('text:\n%s\n' % text)
 
         f = os.popen("patch -p%d < %s" % (strip, pf))
         files = []
@@ -639,7 +665,7 @@
     if source:
         ui.warn("no longer supported: use \"hg clone\" instead\n")
         sys.exit(1)
-    repo = hg.repository(ui, ".", create=1)
+    hg.repository(ui, ".", create=1)
 
 def locate(ui, repo, *pats, **opts):
     """locate files matching specific patterns"""
@@ -647,24 +673,24 @@
         ui.warn("error: patterns may not contain '%s'\n" % os.sep)
         ui.warn("use '-i <dir>' instead\n")
         sys.exit(1)
-    def compile(pats, head = '^', tail = os.sep, on_empty = True):
+    def compile(pats, head='^', tail=os.sep, on_empty=True):
         if not pats:
             class c:
-                def match(self, x): return on_empty
+                def match(self, x):
+                    return on_empty
             return c()
-        regexp = r'%s(?:%s)%s' % (
-            head,
-            '|'.join([fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
-                      for p in pats]),
-            tail)
+        fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
+                  for p in pats]
+        regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
         return re.compile(regexp)
-    exclude = compile(opts['exclude'], on_empty = False)
+    exclude = compile(opts['exclude'], on_empty=False)
     include = compile(opts['include'])
-    pat = compile([os.path.normcase(p) for p in pats], head = '', tail = '$')
-    end = '\n'
-    if opts['print0']: end = '\0'
-    if opts['rev']: node = repo.manifest.lookup(opts['rev'])
-    else: node = repo.manifest.tip()
+    pat = compile(pats, head='', tail='$')
+    end = opts['print0'] and '\0' or '\n'
+    if opts['rev']:
+        node = repo.manifest.lookup(opts['rev'])
+    else:
+        node = repo.manifest.tip()
     manifest = repo.manifest.read(node)
     cwd = repo.getcwd()
     cwd_plus = cwd and (cwd + os.sep)
@@ -673,12 +699,16 @@
         f = os.path.normcase(f)
         if exclude.match(f) or not(include.match(f) and
                                    f.startswith(cwd_plus) and
-                                   pat.match(os.path.basename(f))): continue
-        if opts['fullpath']: f = os.path.join(repo.root, f)
-        elif cwd: f = f[len(cwd_plus):]
+                                   pat.match(os.path.basename(f))):
+            continue
+        if opts['fullpath']:
+            f = os.path.join(repo.root, f)
+        elif cwd:
+            f = f[len(cwd_plus):]
         found.append(f)
     found.sort()
-    for f in found: ui.write(f, end)
+    for f in found:
+        ui.write(f, end)
 
 def log(ui, repo, f=None, **opts):
     """show the revision history of the repository or a single file"""
@@ -712,21 +742,20 @@
             changenode = repo.changelog.node(i)
             prev, other = repo.changelog.parents(changenode)
             dodiff(sys.stdout, ui, repo, files, prev, changenode)
-            ui.write("\n")
-        ui.write("\n")
+            ui.write("\n\n")
 
-def manifest(ui, repo, rev = []):
+def manifest(ui, repo, rev=None):
     """output the latest or given revision of the project manifest"""
-    n = repo.manifest.tip()
     if rev:
         try:
             # assume all revision numbers are for changesets
             n = repo.lookup(rev)
             change = repo.changelog.read(n)
             n = change[0]
-        except:
+        except hg.RepoError:
             n = repo.manifest.lookup(rev)
-
+    else:
+        n = repo.manifest.tip()
     m = repo.manifest.read(n)
     mf = repo.manifest.readflags(n)
     files = m.keys()
@@ -735,7 +764,7 @@
     for f in files:
         ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
 
-def parents(ui, repo, node = None):
+def parents(ui, repo, node=None):
     '''show the parents of the current working dir'''
     if node:
         p = repo.changelog.parents(repo.lookup(hg.bin(node)))
@@ -775,8 +804,10 @@
 
     text = rc['text']
     if not text and rc['logfile']:
-        try: text = open(rc['logfile']).read()
-        except IOError: pass
+        try:
+            text = open(rc['logfile']).read()
+        except IOError:
+            pass
     if not text and not rc['logfile']:
         ui.warn("abort: missing commit text\n")
         return 1
@@ -793,9 +824,9 @@
     """roll back an interrupted transaction"""
     repo.recover()
 
-def remove(ui, repo, file, *files):
+def remove(ui, repo, file1, *files):
     """remove the specified files on the next commit"""
-    repo.remove(relpath(repo, (file,) + files))
+    repo.remove(relpath(repo, (file1,) + files))
 
 def revert(ui, repo, *names, **opts):
     """revert modified files or dirs back to their unmodified states"""
@@ -819,14 +850,18 @@
     def choose(name):
         def body(name):
             for r in relnames:
-                if not name.startswith(r): continue
+                if not name.startswith(r):
+                    continue
                 rest = name[len(r):]
-                if not rest: return r, True
+                if not rest:
+                    return r, True
                 depth = rest.count(os.sep)
                 if not r:
-                    if depth == 0 or not opts['nonrecursive']: return r, True
+                    if depth == 0 or not opts['nonrecursive']:
+                        return r, True
                 elif rest[0] == os.sep:
-                    if depth == 1 or not opts['nonrecursive']: return r, True
+                    if depth == 1 or not opts['nonrecursive']:
+                        return r, True
             return None, False
         relname, ret = body(name)
         if ret:
@@ -875,7 +910,8 @@
                 lock = repo.lock()
                 respond("")
             if cmd == "unlock":
-                if lock: lock.release()
+                if lock:
+                    lock.release()
                 lock = None
                 respond("")
             elif cmd == "branches":
@@ -887,7 +923,7 @@
                 respond("".join(r))
             elif cmd == "between":
                 arg, pairs = getarg()
-                pairs = [ map(hg.bin, p.split("-")) for p in pairs.split(" ") ]
+                pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
                 r = []
                 for b in repo.between(pairs):
                     r.append(" ".join(map(hg.hex, b)) + "\n")
@@ -900,7 +936,8 @@
                 cg = repo.changegroup(nodes)
                 while 1:
                     d = cg.read(4096)
-                    if not d: break
+                    if not d:
+                        break
                     fout.write(d)
 
                 fout.flush()
@@ -915,8 +952,10 @@
                 respond("")
 
     def openlog(opt, default):
-        if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
-        else: return default
+        if opts[opt] and opts[opt] != '-':
+            return open(opts[opt], 'w')
+        else:
+            return default
 
     httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
                                 opts["address"], opts["port"],
@@ -929,7 +968,8 @@
         else:
             try:
                 addr = socket.gethostbyaddr(addr)[0]
-            except: pass
+            except socket.error:
+                pass
         if port != 80:
             ui.status('listening at http://%s:%d/\n' % (addr, port))
         else:
@@ -947,12 +987,16 @@
     (c, a, d, u) = repo.changes(None, None)
     (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
 
-    for f in c: ui.write("C ", f, "\n")
-    for f in a: ui.write("A ", f, "\n")
-    for f in d: ui.write("R ", f, "\n")
-    for f in u: ui.write("? ", f, "\n")
+    for f in c:
+        ui.write("C ", f, "\n")
+    for f in a:
+        ui.write("A ", f, "\n")
+    for f in d:
+        ui.write("R ", f, "\n")
+    for f in u:
+        ui.write("? ", f, "\n")
 
-def tag(ui, repo, name, rev = None, **opts):
+def tag(ui, repo, name, rev=None, **opts):
     """add a tag for the current tip or a given revision"""
 
     if name == "tip":
@@ -978,10 +1022,10 @@
             ui.status("(please commit .hgtags manually)\n")
             return -1
 
-    add = 0
-    if not os.path.exists(repo.wjoin(".hgtags")): add = 1
+    add = not os.path.exists(repo.wjoin(".hgtags"))
     repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
-    if add: repo.add([".hgtags"])
+    if add:
+        repo.add([".hgtags"])
 
     if not opts['text']:
         opts['text'] = "Added tag %s for changeset %s" % (name, r)
@@ -1045,94 +1089,115 @@
 table = {
     "^add": (add, [], "hg add [files]"),
     "addremove": (addremove, [], "hg addremove [files]"),
-    "^annotate": (annotate,
-                     [('r', 'revision', '', 'revision'),
-                      ('u', 'user', None, 'show user'),
-                      ('n', 'number', None, 'show revision number'),
-                      ('c', 'changeset', None, 'show changeset')],
-                     'hg annotate [-u] [-c] [-n] [-r id] [files]'),
-    "cat": (cat, [('o', 'output', "", 'output to file')], 'hg cat [-o outfile] <file> [rev]'),
-    "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
-              'hg clone [options] <source> [dest]'),
-    "^commit|ci": (commit,
-                  [('t', 'text', "", 'commit text'),
-                   ('A', 'addremove', None, 'run add/remove during commit'),
-                   ('l', 'logfile', "", 'commit text file'),
-                   ('d', 'date', "", 'date code'),
-                   ('u', 'user', "", 'user')],
-                  'hg commit [files]'),
+    "^annotate":
+        (annotate,
+         [('r', 'revision', '', 'revision'),
+          ('u', 'user', None, 'show user'),
+          ('n', 'number', None, 'show revision number'),
+          ('c', 'changeset', None, 'show changeset')],
+         'hg annotate [-u] [-c] [-n] [-r id] [files]'),
+    "cat":
+        (cat,
+         [('o', 'output', "", 'output to file')],
+         'hg cat [-o outfile] <file> [rev]'),
+    "^clone":
+        (clone,
+         [('U', 'noupdate', None, 'skip update after cloning')],
+         'hg clone [options] <source> [dest]'),
+    "^commit|ci":
+        (commit,
+         [('t', 'text', "", 'commit text'),
+          ('A', 'addremove', None, 'run add/remove during commit'),
+          ('l', 'logfile', "", 'commit text file'),
+          ('d', 'date', "", 'date code'),
+          ('u', 'user', "", 'user')],
+         'hg commit [files]'),
     "copy": (copy, [], 'hg copy <source> <dest>'),
     "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
     "debugstate": (debugstate, [], 'debugstate'),
     "debugindex": (debugindex, [], 'debugindex <file>'),
     "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
-    "^diff": (diff, [('r', 'rev', [], 'revision')],
-             'hg diff [-r A] [-r B] [files]'),
-    "^export": (export, [('o', 'output', "", 'output to file')],
-               "hg export [-o file] <changeset> ..."),
+    "^diff":
+        (diff,
+         [('r', 'rev', [], 'revision')],
+         'hg diff [-r A] [-r B] [files]'),
+    "^export":
+        (export,
+         [('o', 'output', "", 'output to file')],
+         "hg export [-o file] <changeset> ..."),
     "forget": (forget, [], "hg forget [files]"),
     "heads": (heads, [], 'hg heads'),
-    "help": (help, [], 'hg help [command]'),
+    "help": (help_, [], 'hg help [command]'),
     "identify|id": (identify, [], 'hg identify'),
-    "import|patch": (import_,
-                     [('p', 'strip', 1, 'path strip'),
-                      ('b', 'base', "", 'base path')],
-                     "hg import [options] <patches>"),
+    "import|patch":
+        (import_,
+         [('p', 'strip', 1, 'path strip'),
+          ('b', 'base', "", 'base path')],
+         "hg import [options] <patches>"),
     "^init": (init, [], 'hg init'),
-    "locate": (locate,
-               [('0', 'print0', None, 'end records with NUL'),
-                ('f', 'fullpath', None, 'print complete paths'),
-                ('i', 'include', [], 'include path in search'),
-                ('r', 'rev', '', 'revision'),
-                ('x', 'exclude', [], 'exclude path from search')],
-               'hg locate [options] [files]'),
-    "^log|history": (log,
-                    [('r', 'rev', [], 'revision'),
-                     ('p', 'patch', None, 'show patch')],
-                    'hg log [-r A] [-r B] [-p] [file]'),
+    "locate":
+        (locate,
+         [('0', 'print0', None, 'end records with NUL'),
+          ('f', 'fullpath', None, 'print complete paths'),
+          ('i', 'include', [], 'include path in search'),
+          ('r', 'rev', '', 'revision'),
+          ('x', 'exclude', [], 'exclude path from search')],
+         'hg locate [options] [files]'),
+    "^log|history":
+        (log,
+         [('r', 'rev', [], 'revision'),
+          ('p', 'patch', None, 'show patch')],
+         'hg log [-r A] [-r B] [-p] [file]'),
     "manifest": (manifest, [], 'hg manifest [rev]'),
     "parents": (parents, [], 'hg parents [node]'),
-    "^pull": (pull,
-                  [('u', 'update', None, 'update working directory')],
-                  'hg pull [options] [source]'),
+    "^pull":
+        (pull,
+         [('u', 'update', None, 'update working directory')],
+         'hg pull [options] [source]'),
     "^push": (push, [], 'hg push <destination>'),
-    "rawcommit": (rawcommit,
-                  [('p', 'parent', [], 'parent'),
-                   ('d', 'date', "", 'date code'),
-                   ('u', 'user', "", 'user'),
-                   ('F', 'files', "", 'file list'),
-                   ('t', 'text', "", 'commit text'),
-                   ('l', 'logfile', "", 'commit text file')],
-                  'hg rawcommit [options] [files]'),
+    "rawcommit":
+        (rawcommit,
+         [('p', 'parent', [], 'parent'),
+          ('d', 'date', "", 'date code'),
+          ('u', 'user', "", 'user'),
+          ('F', 'files', "", 'file list'),
+          ('t', 'text', "", 'commit text'),
+          ('l', 'logfile', "", 'commit text file')],
+         'hg rawcommit [options] [files]'),
     "recover": (recover, [], "hg recover"),
     "^remove|rm": (remove, [], "hg remove [files]"),
-    "^revert": (revert,
-               [("n", "nonrecursive", None, "don't recurse into subdirs"),
-                ("r", "rev", "", "revision")],
-               "hg revert [files|dirs]"),
+    "^revert":
+        (revert,
+         [("n", "nonrecursive", None, "don't recurse into subdirs"),
+          ("r", "rev", "", "revision")],
+         "hg revert [files|dirs]"),
     "root": (root, [], "hg root"),
-    "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
-                       ('E', 'errorlog', '', 'error log file'),
-                       ('p', 'port', 8000, 'listen port'),
-                       ('a', 'address', '', 'interface address'),
-                       ('n', 'name', os.getcwd(), 'repository name'),
-                       ('', 'stdio', None, 'for remote clients'),
-                       ('t', 'templates', "", 'template map')],
-              "hg serve [options]"),
+    "^serve":
+        (serve,
+         [('A', 'accesslog', '', 'access log file'),
+          ('E', 'errorlog', '', 'error log file'),
+          ('p', 'port', 8000, 'listen port'),
+          ('a', 'address', '', 'interface address'),
+          ('n', 'name', os.getcwd(), 'repository name'),
+          ('', 'stdio', None, 'for remote clients'),
+          ('t', 'templates', "", 'template map')],
+         "hg serve [options]"),
     "^status": (status, [], 'hg status'),
-    "tag": (tag,  [('l', 'local', None, 'make the tag local'),
-                   ('t', 'text', "", 'commit text'),
-                   ('d', 'date', "", 'date code'),
-                   ('u', 'user', "", 'user')],
-            'hg tag [options] <name> [rev]'),
+    "tag":
+        (tag,
+         [('l', 'local', None, 'make the tag local'),
+          ('t', 'text', "", 'commit text'),
+          ('d', 'date', "", 'date code'),
+          ('u', 'user', "", 'user')],
+         'hg tag [options] <name> [rev]'),
     "tags": (tags, [], 'hg tags'),
     "tip": (tip, [], 'hg tip'),
     "undo": (undo, [], 'hg undo'),
     "^update|up|checkout|co":
-            (update,
-             [('m', 'merge', None, 'allow merging of conflicts'),
-              ('C', 'clean', None, 'overwrite locally modified files')],
-             'hg update [options] [node]'),
+        (update,
+         [('m', 'merge', None, 'allow merging of conflicts'),
+          ('C', 'clean', None, 'overwrite locally modified files')],
+         'hg update [options] [node]'),
     "verify": (verify, [], 'hg verify'),
     "version": (show_version, [], 'hg version'),
     }
@@ -1145,7 +1210,7 @@
               ('', 'traceback', None, 'print traceback on exception'),
               ('y', 'noninteractive', None, 'run non-interactively'),
               ('', 'version', None, 'output version information and exit'),
-              ]
+             ]
 
 norepo = "clone init version help debugindex debugindexdot"
 
@@ -1156,7 +1221,8 @@
 
     raise UnknownCommand(cmd)
 
-class SignalInterrupt(Exception): pass
+class SignalInterrupt(Exception):
+    """Exception raised on SIGTERM and SIGHUP."""
 
 def catchterm(*args):
     raise SignalInterrupt
@@ -1164,7 +1230,8 @@
 def run():
     sys.exit(dispatch(sys.argv[1:]))
 
-class ParseError(Exception): pass
+class ParseError(Exception):
+    """Exception raised on errors in parsing the command line."""
 
 def parse(args):
     options = {}
@@ -1178,7 +1245,7 @@
     if options["version"]:
         return ("version", show_version, [], options, cmdoptions)
     elif not args:
-        return ("help", help, [], options, cmdoptions)
+        return ("help", help_, [], options, cmdoptions)
     else:
         cmd, args = args[0], args[1:]
 
@@ -1186,7 +1253,6 @@
 
     # combine global options into local
     c = list(i[1])
-    l = len(c)
     for o in globalopts:
         c.append((o[0], o[1], options[o[1]], o[3]))
 
@@ -1205,8 +1271,10 @@
 
 def dispatch(args):
     signal.signal(signal.SIGTERM, catchterm)
-    try: signal.signal(signal.SIGHUP, catchterm)
-    except: pass
+    try:
+        signal.signal(signal.SIGHUP, catchterm)
+    except AttributeError:
+        pass
 
     try:
         cmd, func, args, options, cmdoptions = parse(args)
@@ -1214,19 +1282,19 @@
         u = ui.ui()
         if inst.args[0]:
             u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
-            help(u, inst.args[0])
+            help_(u, inst.args[0])
         else:
             u.warn("hg: %s\n" % inst.args[1])
-            help(u)
+            help_(u)
         sys.exit(-1)
     except UnknownCommand, inst:
         u = ui.ui()
         u.warn("hg: unknown command '%s'\n" % inst.args[0])
-        help(u)
+        help_(u)
         sys.exit(1)
 
     u = ui.ui(options["verbose"], options["debug"], options["quiet"],
-                     not options["noninteractive"])
+              not options["noninteractive"])
 
     try:
         try:
@@ -1282,6 +1350,6 @@
             raise
         u.debug(inst, "\n")
         u.warn("%s: invalid arguments\n" % cmd)
-        help(u, cmd)
+        help_(u, cmd)
 
     sys.exit(-1)
--- a/mercurial/hg.py	Thu Jul 14 22:37:46 2005 -0800
+++ b/mercurial/hg.py	Thu Jul 14 22:56:55 2005 -0800
@@ -1498,8 +1498,8 @@
         self.ui.debug("file %s: other %s ancestor %s\n" %
                               (fn, short(other), short(base)))
 
-        cmd = os.environ.get("HGMERGE", "hgmerge") or \
-              self.ui.config("ui", "merge")
+        cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
+               or "hgmerge")
         r = os.system("%s %s %s %s" % (cmd, a, b, c))
         if r:
             self.ui.warn("merging %s failed!\n" % fn)
--- a/mercurial/lock.py	Thu Jul 14 22:37:46 2005 -0800
+++ b/mercurial/lock.py	Thu Jul 14 22:56:55 2005 -0800
@@ -37,7 +37,7 @@
         try:
             util.makelock(str(pid), self.f)
             self.held = 1
-        except:
+        except (OSError, IOError):
             raise LockHeld(util.readlock(self.f))
 
     def release(self):
--- a/mercurial/util.py	Thu Jul 14 22:37:46 2005 -0800
+++ b/mercurial/util.py	Thu Jul 14 22:56:55 2005 -0800
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import os
+import os, errno
 
 def unique(g):
     seen = {}
@@ -46,6 +46,29 @@
         os.unlink(dst)
         os.rename(src, dst)
 
+def copytree(src, dst, copyfile):
+    """Copy a directory tree, files are copied using 'copyfile'."""
+    names = os.listdir(src)
+    os.mkdir(dst)
+
+    for name in names:
+        srcname = os.path.join(src, name)
+        dstname = os.path.join(dst, name)
+        if os.path.isdir(srcname):
+            copytree(srcname, dstname, copyfile)
+        elif os.path.isfile(srcname):
+            copyfile(srcname, dstname)
+        else:
+            raise IOError("Not a regular file: %r" % srcname)
+
+def _makelock_file(info, pathname):
+    ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
+    os.write(ld, info)
+    os.close(ld)
+
+def _readlock_file(pathname):
+    return file(pathname).read()
+
 # Platfor specific varients
 if os.name == 'nt':
     nulldev = 'NUL:'
@@ -59,13 +82,8 @@
     def pconvert(path):
         return path.replace("\\", "/")
 
-    def makelock(info, pathname):
-        ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
-        os.write(ld, info)
-        os.close(ld)
-
-    def readlock(pathname):
-        return file(pathname).read()
+    makelock = _makelock_file
+    readlock = _readlock_file
 
 else:
     nulldev = '/dev/null'
@@ -90,7 +108,19 @@
         return path
 
     def makelock(info, pathname):
-        os.symlink(info, pathname)
+        try:
+            os.symlink(info, pathname)
+        except OSError, why:
+            if why.errno == errno.EEXIST:
+                raise
+            else:
+                _makelock_file(info, pathname)
 
     def readlock(pathname):
-        return os.readlink(pathname)
+        try:
+            return os.readlink(pathname)
+        except OSError, why:
+            if why.errno == errno.EINVAL:
+                return _readlock_file(pathname)
+            else:
+                raise
--- a/tests/test-bad-pull	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-bad-pull	Thu Jul 14 22:56:55 2005 -0800
@@ -17,8 +17,9 @@
 run()
 EOF
 
+set +x # backgrounding sometimes disturbs the order of command tracing
 python dumb.py 2>/dev/null &
-sleep 2
+set -x
 
 hg clone http://localhost:20059/foo copy2
 echo $?
--- a/tests/test-bad-pull.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-bad-pull.out	Thu Jul 14 22:56:55 2005 -0800
@@ -6,8 +6,7 @@
 + ls copy
 ls: copy: No such file or directory
 + cat
-+ python dumb.py
-+ sleep 2
++ set +x
 + hg clone http://localhost:20059/foo copy2
 requesting all changes
 abort: HTTP Error 404: File not found
--- a/tests/test-basic.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-basic.out	Thu Jul 14 22:56:55 2005 -0800
@@ -11,7 +11,6 @@
 date:        Thu Jan  1 00:00:00 1970
 summary:     test
 
-
 + hg manifest
 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 a
 + hg cat a
--- a/tests/test-copy.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-copy.out	Thu Jul 14 22:56:55 2005 -0800
@@ -19,21 +19,17 @@
 date:        Thu Jan  1 00:00:00 1970
 summary:     2
 
-
 changeset:   0:c19d34741b0a4ced8e4ba74bb834597d5193851e
 user:        test
 date:        Thu Jan  1 00:00:00 1970
 summary:     1
 
-
 + hg log a
-revision:    0:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
 changeset:   0:c19d34741b0a4ced8e4ba74bb834597d5193851e
 user:        test
 date:        Thu Jan  1 00:00:00 1970
 summary:     1
 
-
 + hexdump -C .hg/data/b.d
 00000000  75 01 0a 63 6f 70 79 72  65 76 3a 20 62 37 38 39  |u..copyrev: b789|
 00000010  66 64 64 39 36 64 63 32  66 33 62 64 32 32 39 63  |fdd96dc2f3bd229c|
--- a/tests/test-flags.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-flags.out	Thu Jul 14 22:56:55 2005 -0800
@@ -52,19 +52,16 @@
 date:        Thu Jan  1 00:00:00 1970
 summary:     chmod +x a
 
-
 changeset:   1:c6ecefc45368ed556d965f1c1086c6561a8b2ac5
 user:        test
 date:        Thu Jan  1 00:00:00 1970
 summary:     a updated
 
-
 changeset:   0:22a449e20da501ca558394c083ca470e9c81b9f7
 user:        test
 date:        Thu Jan  1 00:00:00 1970
 summary:     added a b
 
-
 + hg -v co -m
 resolving manifests
 merging a
--- a/tests/test-tag.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-tag.out	Thu Jul 14 22:56:55 2005 -0800
@@ -9,7 +9,6 @@
 date:        Thu Jan  1 00:00:00 1970
 summary:     test
 
-
 + hg tag -u test -d '0 0' bleah
 + hg history
 changeset:   1:863197ef03781c4fc00276d83eb66c4cb9cd91df
@@ -18,14 +17,12 @@
 date:        Thu Jan  1 00:00:00 1970
 summary:     Added tag bleah for changeset acb14030fe0a21b60322c440ad2d20cf7685a376
 
-
 changeset:   0:acb14030fe0a21b60322c440ad2d20cf7685a376
 tag:         bleah
 user:        test
 date:        Thu Jan  1 00:00:00 1970
 summary:     test
 
-
 + echo foo
 + hg tag -u test -d '0 0' bleah2
 abort: working copy of .hgtags is changed!
--- a/tests/test-up-local-change.out	Thu Jul 14 22:37:46 2005 -0800
+++ b/tests/test-up-local-change.out	Thu Jul 14 22:56:55 2005 -0800
@@ -57,7 +57,6 @@
 2
 
 
-
 changeset:   0:c19d34741b0a4ced8e4ba74bb834597d5193851e
 manifest:    0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
 user:        test
@@ -67,7 +66,6 @@
 1
 
 
-
 + hg diff
 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
 diff -r 1e71731e6fbb a