mercurial/commands.py
changeset 705 574869103985
parent 693 10c0264751da
parent 701 80ed193efff7
child 706 5107a7b6b14a
child 723 9e0f3ba4a9c2
equal deleted inserted replaced
694:51eb248d3348 705:574869103985
     3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
     3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
     4 #
     4 #
     5 # This software may be used and distributed according to the terms
     5 # This software may be used and distributed according to the terms
     6 # of the GNU General Public License, incorporated herein by reference.
     6 # of the GNU General Public License, incorporated herein by reference.
     7 
     7 
     8 from demandload import *
     8 from demandload import demandload
     9 demandload(globals(), "os re sys signal")
     9 demandload(globals(), "os re sys signal shutil")
    10 demandload(globals(), "fancyopts ui hg util")
    10 demandload(globals(), "fancyopts ui hg util")
    11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
    11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
    12 demandload(globals(), "errno socket version struct")
    12 demandload(globals(), "errno socket version struct")
    13 
    13 
    14 class UnknownCommand(Exception): pass
    14 class UnknownCommand(Exception):
       
    15     """Exception raised if command is not in the command table."""
    15 
    16 
    16 def filterfiles(filters, files):
    17 def filterfiles(filters, files):
    17     l = [ x for x in files if x in filters ]
    18     l = [x for x in files if x in filters]
    18 
    19 
    19     for t in filters:
    20     for t in filters:
    20         if t and t[-1] != "/": t += "/"
    21         if t and t[-1] != "/":
    21         l += [ x for x in files if x.startswith(t) ]
    22             t += "/"
       
    23         l += [x for x in files if x.startswith(t)]
    22     return l
    24     return l
    23 
    25 
    24 def relfilter(repo, files):
    26 def relfilter(repo, files):
    25     cwd = repo.getcwd()
    27     cwd = repo.getcwd()
    26     if cwd:
    28     if cwd:
    28     return files
    30     return files
    29 
    31 
    30 def relpath(repo, args):
    32 def relpath(repo, args):
    31     cwd = repo.getcwd()
    33     cwd = repo.getcwd()
    32     if cwd:
    34     if cwd:
    33         return [ util.pconvert(os.path.normpath(os.path.join(cwd, x))) for x in args ]
    35         return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
       
    36                 for x in args]
    34     return args
    37     return args
    35 
    38 
    36 revrangesep = ':'
    39 revrangesep = ':'
    37 
    40 
    38 def revrange(ui, repo, revs = [], revlog = None):
    41 def revrange(ui, repo, revs, revlog=None):
    39     if revlog is None:
    42     if revlog is None:
    40         revlog = repo.changelog
    43         revlog = repo.changelog
    41     revcount = revlog.count()
    44     revcount = revlog.count()
    42     def fix(val, defval):
    45     def fix(val, defval):
    43         if not val: return defval
    46         if not val:
       
    47             return defval
    44         try:
    48         try:
    45             num = int(val)
    49             num = int(val)
    46             if str(num) != val: raise ValueError
    50             if str(num) != val:
    47             if num < 0: num += revcount
    51                 raise ValueError
       
    52             if num < 0:
       
    53                 num += revcount
    48             if not (0 <= num < revcount):
    54             if not (0 <= num < revcount):
    49                 raise ValueError
    55                 raise ValueError
    50         except ValueError:
    56         except ValueError:
    51             try:
    57             try:
    52                 num = repo.changelog.rev(repo.lookup(val))
    58                 num = repo.changelog.rev(repo.lookup(val))
    83     expander = {
    89     expander = {
    84         '%': lambda: '%',
    90         '%': lambda: '%',
    85         'b': lambda: os.path.basename(repo.root),
    91         'b': lambda: os.path.basename(repo.root),
    86         }
    92         }
    87 
    93 
    88     if node: expander.update(node_expander)
    94     if node:
       
    95         expander.update(node_expander)
    89     if node and revwidth is not None:
    96     if node and revwidth is not None:
    90         expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
    97         expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
    91     if total is not None: expander['N'] = lambda: str(total)
    98     if total is not None:
    92     if seqno is not None: expander['n'] = lambda: str(seqno)
    99         expander['N'] = lambda: str(total)
       
   100     if seqno is not None:
       
   101         expander['n'] = lambda: str(seqno)
    93     if total is not None and seqno is not None:
   102     if total is not None and seqno is not None:
    94         expander['n'] = lambda:str(seqno).zfill(len(str(total)))
   103         expander['n'] = lambda:str(seqno).zfill(len(str(total)))
    95 
   104 
    96     newname = []
   105     newname = []
    97     patlen = len(pat)
   106     patlen = len(pat)
   104             c = expander[c]()
   113             c = expander[c]()
   105         newname.append(c)
   114         newname.append(c)
   106         i += 1
   115         i += 1
   107     return ''.join(newname)
   116     return ''.join(newname)
   108 
   117 
   109 def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
   118 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
   110     def date(c):
   119     def date(c):
   111         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
   120         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
   112 
   121 
   113     (c, a, d, u) = repo.changes(node1, node2, files)
   122     (c, a, d, u) = repo.changes(node1, node2, files)
   114     if files:
   123     if files:
   118         return
   127         return
   119 
   128 
   120     if node2:
   129     if node2:
   121         change = repo.changelog.read(node2)
   130         change = repo.changelog.read(node2)
   122         mmap2 = repo.manifest.read(change[0])
   131         mmap2 = repo.manifest.read(change[0])
   123         def read(f): return repo.file(f).read(mmap2[f])
       
   124         date2 = date(change)
   132         date2 = date(change)
       
   133         def read(f):
       
   134             return repo.file(f).read(mmap2[f])
   125     else:
   135     else:
   126         date2 = time.asctime()
   136         date2 = time.asctime()
   127         if not node1:
   137         if not node1:
   128             node1 = repo.dirstate.parents()[0]
   138             node1 = repo.dirstate.parents()[0]
   129         def read(f): return repo.wfile(f).read()
   139         def read(f):
       
   140             return repo.wfile(f).read()
   130 
   141 
   131     if ui.quiet:
   142     if ui.quiet:
   132         r = None
   143         r = None
   133     else:
   144     else:
   134         hexfunc = ui.verbose and hg.hex or hg.short
   145         hexfunc = ui.verbose and hg.hex or hg.short
   220         "This is free software; see the source for copying conditions. "
   231         "This is free software; see the source for copying conditions. "
   221         "There is NO\nwarranty; "
   232         "There is NO\nwarranty; "
   222         "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
   233         "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
   223     )
   234     )
   224 
   235 
   225 def help(ui, cmd=None):
   236 def help_(ui, cmd=None):
   226     '''show help for a given command or all commands'''
   237     """show help for a given command or all commands"""
   227     if cmd:
   238     if cmd:
   228         try:
   239         try:
   229             i = find(cmd)
   240             i = find(cmd)
   230             ui.write("%s\n\n" % i[2])
   241             ui.write("%s\n\n" % i[2])
   231 
   242 
   232             if i[1]:
   243             if i[1]:
   233                 for s, l, d, c in i[1]:
   244                 for s, l, d, c in i[1]:
   234                     opt=' '
   245                     opt = ' '
   235                     if s: opt = opt + '-' + s + ' '
   246                     if s:
   236                     if l: opt = opt + '--' + l + ' '
   247                         opt = opt + '-' + s + ' '
   237                     if d: opt = opt + '(' + str(d) + ')'
   248                     if l:
       
   249                         opt = opt + '--' + l + ' '
       
   250                     if d:
       
   251                         opt = opt + '(' + str(d) + ')'
   238                     ui.write(opt, "\n")
   252                     ui.write(opt, "\n")
   239                     if c: ui.write('   %s\n' % c)
   253                     if c:
       
   254                         ui.write('   %s\n' % c)
   240                 ui.write("\n")
   255                 ui.write("\n")
   241 
   256 
   242             ui.write(i[0].__doc__, "\n")
   257             ui.write(i[0].__doc__, "\n")
   243         except UnknownCommand:
   258         except UnknownCommand:
   244             ui.warn("hg: unknown command %s\n" % cmd)
   259             ui.warn("hg: unknown command %s\n" % cmd)
   271         for f in fns:
   286         for f in fns:
   272             ui.write(' %-*s   %s\n' % (m, f, h[f]))
   287             ui.write(' %-*s   %s\n' % (m, f, h[f]))
   273 
   288 
   274 # Commands start here, listed alphabetically
   289 # Commands start here, listed alphabetically
   275 
   290 
   276 def add(ui, repo, file, *files):
   291 def add(ui, repo, file1, *files):
   277     '''add the specified files on the next commit'''
   292     '''add the specified files on the next commit'''
   278     repo.add(relpath(repo, (file,) + files))
   293     repo.add(relpath(repo, (file1,) + files))
   279 
   294 
   280 def addremove(ui, repo, *files):
   295 def addremove(ui, repo, *files):
   281     """add all new files, delete all missing files"""
   296     """add all new files, delete all missing files"""
   282     if files:
   297     if files:
   283         files = relpath(repo, files)
   298         files = relpath(repo, files)
   294     else:
   309     else:
   295         (c, a, d, u) = repo.changes(None, None)
   310         (c, a, d, u) = repo.changes(None, None)
   296     repo.add(u)
   311     repo.add(u)
   297     repo.remove(d)
   312     repo.remove(d)
   298 
   313 
   299 def annotate(u, repo, file, *files, **ops):
   314 def annotate(u, repo, file1, *files, **ops):
   300     """show changeset information per file line"""
   315     """show changeset information per file line"""
   301     def getnode(rev):
   316     def getnode(rev):
   302         return hg.short(repo.changelog.node(rev))
   317         return hg.short(repo.changelog.node(rev))
   303 
   318 
   304     def getname(rev):
   319     def getname(rev):
   324     node = repo.dirstate.parents()[0]
   339     node = repo.dirstate.parents()[0]
   325     if ops['revision']:
   340     if ops['revision']:
   326         node = repo.changelog.lookup(ops['revision'])
   341         node = repo.changelog.lookup(ops['revision'])
   327     change = repo.changelog.read(node)
   342     change = repo.changelog.read(node)
   328     mmap = repo.manifest.read(change[0])
   343     mmap = repo.manifest.read(change[0])
   329     for f in relpath(repo, (file,) + files):
   344     for f in relpath(repo, (file1,) + files):
   330         lines = repo.file(f).annotate(mmap[f])
   345         lines = repo.file(f).annotate(mmap[f])
   331         pieces = []
   346         pieces = []
   332 
   347 
   333         for o, f in opmap:
   348         for o, f in opmap:
   334             if ops[o]:
   349             if ops[o]:
   335                 l = [ f(n) for n,t in lines ]
   350                 l = [f(n) for n, dummy in lines]
   336                 m = max(map(len, l))
   351                 m = max(map(len, l))
   337                 pieces.append([ "%*s" % (m, x) for x in l])
   352                 pieces.append(["%*s" % (m, x) for x in l])
   338 
   353 
   339         for p,l in zip(zip(*pieces), lines):
   354         for p, l in zip(zip(*pieces), lines):
   340             u.write(" ".join(p) + ": " + l[1])
   355             u.write(" ".join(p) + ": " + l[1])
   341 
   356 
   342 def cat(ui, repo, file, rev = [], **opts):
   357 def cat(ui, repo, file1, rev=None, **opts):
   343     """output the latest or given revision of a file"""
   358     """output the latest or given revision of a file"""
   344     r = repo.file(relpath(repo, [file])[0])
   359     r = repo.file(relpath(repo, [file1])[0])
   345     n = r.tip()
   360     if rev:
   346     if rev: n = r.lookup(rev)
   361         n = r.lookup(rev)
       
   362     else:
       
   363         n = r.tip()
   347     if opts['output'] and opts['output'] != '-':
   364     if opts['output'] and opts['output'] != '-':
   348         try:
   365         try:
   349             outname = make_filename(repo, r, opts['output'], node=n)
   366             outname = make_filename(repo, r, opts['output'], node=n)
   350             fp = open(outname, 'wb')
   367             fp = open(outname, 'wb')
   351         except KeyError, inst:
   368         except KeyError, inst:
   354             sys.exit(1);
   371             sys.exit(1);
   355     else:
   372     else:
   356         fp = sys.stdout
   373         fp = sys.stdout
   357     fp.write(r.read(n))
   374     fp.write(r.read(n))
   358 
   375 
   359 def clone(ui, source, dest = None, **opts):
   376 def clone(ui, source, dest=None, **opts):
   360     """make a copy of an existing repository"""
   377     """make a copy of an existing repository"""
   361     if dest is None:
   378     if dest is None:
   362         dest = os.path.basename(os.path.normpath(source))
   379         dest = os.path.basename(os.path.normpath(source))
   363 
   380 
   364     if os.path.exists(dest):
   381     if os.path.exists(dest):
   365         ui.warn("abort: destination '%s' already exists\n" % dest)
   382         ui.warn("abort: destination '%s' already exists\n" % dest)
   366         return 1
   383         return 1
   367 
   384 
   368     class dircleanup:
   385     class Dircleanup:
   369         def __init__(self, dir):
   386         def __init__(self, dir_):
   370             import shutil
       
   371             self.rmtree = shutil.rmtree
   387             self.rmtree = shutil.rmtree
   372             self.dir = dir
   388             self.dir_ = dir_
   373             os.mkdir(dir)
   389             os.mkdir(dir_)
   374         def close(self):
   390         def close(self):
   375             self.dir = None
   391             self.dir_ = None
   376         def __del__(self):
   392         def __del__(self):
   377             if self.dir:
   393             if self.dir_:
   378                 self.rmtree(self.dir, True)
   394                 self.rmtree(self.dir_, True)
   379 
   395 
   380     d = dircleanup(dest)
   396     d = Dircleanup(dest)
   381     link = 0
       
   382     abspath = source
   397     abspath = source
   383     source = ui.expandpath(source)
   398     source = ui.expandpath(source)
   384     other = hg.repository(ui, source)
   399     other = hg.repository(ui, source)
   385 
   400 
   386     if other.dev() != -1:
   401     if other.dev() != -1:
   387         abspath = os.path.abspath(source)
   402         abspath = os.path.abspath(source)
   388 
   403         copyfile = (os.stat(dest).st_dev == other.dev()
   389     if other.dev() != -1 and os.stat(dest).st_dev == other.dev():
   404                     and getattr(os, 'link', None) or shutil.copy2)
   390         ui.note("cloning by hardlink\n")
   405         if copyfile is not shutil.copy2:
   391         util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
   406             ui.note("cloning by hardlink\n")
       
   407         util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
       
   408                       copyfile)
   392         try:
   409         try:
   393             os.remove(os.path.join(dest, ".hg", "dirstate"))
   410             os.unlink(os.path.join(dest, ".hg", "dirstate"))
   394         except: pass
   411         except IOError:
       
   412             pass
   395 
   413 
   396         repo = hg.repository(ui, dest)
   414         repo = hg.repository(ui, dest)
   397 
   415 
   398     else:
   416     else:
   399         repo = hg.repository(ui, dest, create=1)
   417         repo = hg.repository(ui, dest, create=1)
   409     d.close()
   427     d.close()
   410 
   428 
   411 def commit(ui, repo, *files, **opts):
   429 def commit(ui, repo, *files, **opts):
   412     """commit the specified files or all outstanding changes"""
   430     """commit the specified files or all outstanding changes"""
   413     text = opts['text']
   431     text = opts['text']
   414     if not text and opts['logfile']:
   432     logfile = opts['logfile']
   415         try: text = open(opts['logfile']).read()
   433     if not text and logfile:
   416         except IOError: pass
   434         try:
       
   435             text = open(logfile).read()
       
   436         except IOError, why:
       
   437             ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
   417 
   438 
   418     if opts['addremove']:
   439     if opts['addremove']:
   419         addremove(ui, repo, *files)
   440         addremove(ui, repo, *files)
   420     repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
   441     repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
   421 
   442 
   460     """show the contents of the current dirstate"""
   481     """show the contents of the current dirstate"""
   461     repo.dirstate.read()
   482     repo.dirstate.read()
   462     dc = repo.dirstate.map
   483     dc = repo.dirstate.map
   463     keys = dc.keys()
   484     keys = dc.keys()
   464     keys.sort()
   485     keys.sort()
   465     for file in keys:
   486     for file_ in keys:
   466         ui.write("%c %s\n" % (dc[file][0], file))
   487         ui.write("%c %s\n" % (dc[file_][0], file_))
   467 
   488 
   468 def debugindex(ui, file):
   489 def debugindex(ui, file_):
   469     """dump the contents of an index file"""
   490     """dump the contents of an index file"""
   470     r = hg.revlog(hg.opener(""), file, "")
   491     r = hg.revlog(hg.opener(""), file_, "")
   471     ui.write("   rev    offset  length   base linkrev" +
   492     ui.write("   rev    offset  length   base linkrev" +
   472              " p1           p2           nodeid\n")
   493              " p1           p2           nodeid\n")
   473     for i in range(r.count()):
   494     for i in range(r.count()):
   474         e = r.index[i]
   495         e = r.index[i]
   475         ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
   496         ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
   476                 i, e[0], e[1], e[2], e[3],
   497                 i, e[0], e[1], e[2], e[3],
   477             hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
   498             hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
   478 
   499 
   479 def debugindexdot(ui, file):
   500 def debugindexdot(ui, file_):
   480     """dump an index DAG as a .dot file"""
   501     """dump an index DAG as a .dot file"""
   481     r = hg.revlog(hg.opener(""), file, "")
   502     r = hg.revlog(hg.opener(""), file_, "")
   482     ui.write("digraph G {\n")
   503     ui.write("digraph G {\n")
   483     for i in range(r.count()):
   504     for i in range(r.count()):
   484         e = r.index[i]
   505         e = r.index[i]
   485         ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
   506         ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
   486         if e[5] != hg.nullid:
   507         if e[5] != hg.nullid:
   544     revwidth = max(len(revs[0]), len(revs[-1]))
   565     revwidth = max(len(revs[0]), len(revs[-1]))
   545     for cset in revs:
   566     for cset in revs:
   546         seqno += 1
   567         seqno += 1
   547         doexport(ui, repo, cset, seqno, total, revwidth, opts)
   568         doexport(ui, repo, cset, seqno, total, revwidth, opts)
   548 
   569 
   549 def forget(ui, repo, file, *files):
   570 def forget(ui, repo, file1, *files):
   550     """don't add the specified files on the next commit"""
   571     """don't add the specified files on the next commit"""
   551     repo.forget(relpath(repo, (file,) + files))
   572     repo.forget(relpath(repo, (file1,) + files))
   552 
   573 
   553 def heads(ui, repo):
   574 def heads(ui, repo):
   554     """show current repository heads"""
   575     """show current repository heads"""
   555     for n in repo.changelog.heads():
   576     for n in repo.changelog.heads():
   556         show_changeset(ui, repo, changenode=n)
   577         show_changeset(ui, repo, changenode=n)
   579 def import_(ui, repo, patch1, *patches, **opts):
   600 def import_(ui, repo, patch1, *patches, **opts):
   580     """import an ordered set of patches"""
   601     """import an ordered set of patches"""
   581     try:
   602     try:
   582         import psyco
   603         import psyco
   583         psyco.full()
   604         psyco.full()
   584     except:
   605     except ImportError:
   585         pass
   606         pass
   586 
   607 
   587     patches = (patch1,) + patches
   608     patches = (patch1,) + patches
   588 
   609 
   589     d = opts["base"]
   610     d = opts["base"]
   591 
   612 
   592     for patch in patches:
   613     for patch in patches:
   593         ui.status("applying %s\n" % patch)
   614         ui.status("applying %s\n" % patch)
   594         pf = os.path.join(d, patch)
   615         pf = os.path.join(d, patch)
   595 
   616 
   596         text = ""
   617         text = []
   597         for l in file(pf):
   618         user = None
   598             if l.startswith("--- ") or l.startswith("diff -r"): break
   619         hgpatch = False
   599             text += l
   620         for line in file(pf):
   600 
   621             line = line.rstrip()
   601         # parse values that exist when importing the result of an hg export
   622             if line.startswith("--- ") or line.startswith("diff -r"):
   602         hgpatch = user = snippet = None
   623                 break
   603         ui.debug('text:\n')
   624             elif hgpatch:
   604         for t in text.splitlines():
   625                 # parse values when importing the result of an hg export
   605             ui.debug(t,'\n')
   626                 if line.startswith("# User "):
   606             if t == '# HG changeset patch' or hgpatch == True:
   627                     user = line[7:]
       
   628                     ui.debug('User: %s\n' % user)
       
   629                 elif not line.startswith("# ") and line:
       
   630                     text.append(line)
       
   631                     hgpatch = False
       
   632             elif line == '# HG changeset patch':
   607                 hgpatch = True
   633                 hgpatch = True
   608                 if t.startswith("# User "):
   634             else:
   609                     user = t[7:]
   635                 text.append(line)
   610                     ui.debug('User: %s\n' % user)
   636 
   611                 if not t.startswith("# ") and t.strip() and not snippet: snippet = t
   637         # make sure text isn't empty
   612         if snippet: text = snippet + '\n' + text
   638         if not text:
       
   639             text = "imported patch %s\n" % patch
       
   640         else:
       
   641             text = "%s\n" % '\n'.join(text)
   613         ui.debug('text:\n%s\n' % text)
   642         ui.debug('text:\n%s\n' % text)
   614 
       
   615         # make sure text isn't empty
       
   616         if not text: text = "imported patch %s\n" % patch
       
   617 
   643 
   618         f = os.popen("patch -p%d < %s" % (strip, pf))
   644         f = os.popen("patch -p%d < %s" % (strip, pf))
   619         files = []
   645         files = []
   620         for l in f.read().splitlines():
   646         for l in f.read().splitlines():
   621             l.rstrip('\r\n');
   647             l.rstrip('\r\n');
   637     """create a new repository in the current directory"""
   663     """create a new repository in the current directory"""
   638 
   664 
   639     if source:
   665     if source:
   640         ui.warn("no longer supported: use \"hg clone\" instead\n")
   666         ui.warn("no longer supported: use \"hg clone\" instead\n")
   641         sys.exit(1)
   667         sys.exit(1)
   642     repo = hg.repository(ui, ".", create=1)
   668     hg.repository(ui, ".", create=1)
   643 
   669 
   644 def locate(ui, repo, *pats, **opts):
   670 def locate(ui, repo, *pats, **opts):
   645     """locate files matching specific patterns"""
   671     """locate files matching specific patterns"""
   646     if [p for p in pats if os.sep in p]:
   672     if [p for p in pats if os.sep in p]:
   647         ui.warn("error: patterns may not contain '%s'\n" % os.sep)
   673         ui.warn("error: patterns may not contain '%s'\n" % os.sep)
   648         ui.warn("use '-i <dir>' instead\n")
   674         ui.warn("use '-i <dir>' instead\n")
   649         sys.exit(1)
   675         sys.exit(1)
   650     def compile(pats, head = '^', tail = os.sep, on_empty = True):
   676     def compile(pats, head='^', tail=os.sep, on_empty=True):
   651         if not pats:
   677         if not pats:
   652             class c:
   678             class c:
   653                 def match(self, x): return on_empty
   679                 def match(self, x):
       
   680                     return on_empty
   654             return c()
   681             return c()
   655         regexp = r'%s(?:%s)%s' % (
   682         fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
   656             head,
   683                   for p in pats]
   657             '|'.join([fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
   684         regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
   658                       for p in pats]),
       
   659             tail)
       
   660         return re.compile(regexp)
   685         return re.compile(regexp)
   661     exclude = compile(opts['exclude'], on_empty = False)
   686     exclude = compile(opts['exclude'], on_empty=False)
   662     include = compile(opts['include'])
   687     include = compile(opts['include'])
   663     pat = compile([os.path.normcase(p) for p in pats], head = '', tail = '$')
   688     pat = compile(pats, head='', tail='$')
   664     end = '\n'
   689     end = opts['print0'] and '\0' or '\n'
   665     if opts['print0']: end = '\0'
   690     if opts['rev']:
   666     if opts['rev']: node = repo.manifest.lookup(opts['rev'])
   691         node = repo.manifest.lookup(opts['rev'])
   667     else: node = repo.manifest.tip()
   692     else:
       
   693         node = repo.manifest.tip()
   668     manifest = repo.manifest.read(node)
   694     manifest = repo.manifest.read(node)
   669     cwd = repo.getcwd()
   695     cwd = repo.getcwd()
   670     cwd_plus = cwd and (cwd + os.sep)
   696     cwd_plus = cwd and (cwd + os.sep)
   671     found = []
   697     found = []
   672     for f in manifest:
   698     for f in manifest:
   673         f = os.path.normcase(f)
   699         f = os.path.normcase(f)
   674         if exclude.match(f) or not(include.match(f) and
   700         if exclude.match(f) or not(include.match(f) and
   675                                    f.startswith(cwd_plus) and
   701                                    f.startswith(cwd_plus) and
   676                                    pat.match(os.path.basename(f))): continue
   702                                    pat.match(os.path.basename(f))):
   677         if opts['fullpath']: f = os.path.join(repo.root, f)
   703             continue
   678         elif cwd: f = f[len(cwd_plus):]
   704         if opts['fullpath']:
       
   705             f = os.path.join(repo.root, f)
       
   706         elif cwd:
       
   707             f = f[len(cwd_plus):]
   679         found.append(f)
   708         found.append(f)
   680     found.sort()
   709     found.sort()
   681     for f in found: ui.write(f, end)
   710     for f in found:
       
   711         ui.write(f, end)
   682 
   712 
   683 def log(ui, repo, f=None, **opts):
   713 def log(ui, repo, f=None, **opts):
   684     """show the revision history of the repository or a single file"""
   714     """show the revision history of the repository or a single file"""
   685     if f:
   715     if f:
   686         files = relpath(repo, [f])
   716         files = relpath(repo, [f])
   710                 filenode = filelog.node(i)
   740                 filenode = filelog.node(i)
   711                 i = filelog.linkrev(filenode)
   741                 i = filelog.linkrev(filenode)
   712             changenode = repo.changelog.node(i)
   742             changenode = repo.changelog.node(i)
   713             prev, other = repo.changelog.parents(changenode)
   743             prev, other = repo.changelog.parents(changenode)
   714             dodiff(sys.stdout, ui, repo, files, prev, changenode)
   744             dodiff(sys.stdout, ui, repo, files, prev, changenode)
   715             ui.write("\n")
   745             ui.write("\n\n")
   716         ui.write("\n")
   746 
   717 
   747 def manifest(ui, repo, rev=None):
   718 def manifest(ui, repo, rev = []):
       
   719     """output the latest or given revision of the project manifest"""
   748     """output the latest or given revision of the project manifest"""
   720     n = repo.manifest.tip()
       
   721     if rev:
   749     if rev:
   722         try:
   750         try:
   723             # assume all revision numbers are for changesets
   751             # assume all revision numbers are for changesets
   724             n = repo.lookup(rev)
   752             n = repo.lookup(rev)
   725             change = repo.changelog.read(n)
   753             change = repo.changelog.read(n)
   726             n = change[0]
   754             n = change[0]
   727         except:
   755         except hg.RepoError:
   728             n = repo.manifest.lookup(rev)
   756             n = repo.manifest.lookup(rev)
   729 
   757     else:
       
   758         n = repo.manifest.tip()
   730     m = repo.manifest.read(n)
   759     m = repo.manifest.read(n)
   731     mf = repo.manifest.readflags(n)
   760     mf = repo.manifest.readflags(n)
   732     files = m.keys()
   761     files = m.keys()
   733     files.sort()
   762     files.sort()
   734 
   763 
   735     for f in files:
   764     for f in files:
   736         ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
   765         ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
   737 
   766 
   738 def parents(ui, repo, node = None):
   767 def parents(ui, repo, node=None):
   739     '''show the parents of the current working dir'''
   768     '''show the parents of the current working dir'''
   740     if node:
   769     if node:
   741         p = repo.changelog.parents(repo.lookup(hg.bin(node)))
   770         p = repo.changelog.parents(repo.lookup(hg.bin(node)))
   742     else:
   771     else:
   743         p = repo.dirstate.parents()
   772         p = repo.dirstate.parents()
   773 def rawcommit(ui, repo, *flist, **rc):
   802 def rawcommit(ui, repo, *flist, **rc):
   774     "raw commit interface"
   803     "raw commit interface"
   775 
   804 
   776     text = rc['text']
   805     text = rc['text']
   777     if not text and rc['logfile']:
   806     if not text and rc['logfile']:
   778         try: text = open(rc['logfile']).read()
   807         try:
   779         except IOError: pass
   808             text = open(rc['logfile']).read()
       
   809         except IOError:
       
   810             pass
   780     if not text and not rc['logfile']:
   811     if not text and not rc['logfile']:
   781         ui.warn("abort: missing commit text\n")
   812         ui.warn("abort: missing commit text\n")
   782         return 1
   813         return 1
   783 
   814 
   784     files = relpath(repo, list(flist))
   815     files = relpath(repo, list(flist))
   791 
   822 
   792 def recover(ui, repo):
   823 def recover(ui, repo):
   793     """roll back an interrupted transaction"""
   824     """roll back an interrupted transaction"""
   794     repo.recover()
   825     repo.recover()
   795 
   826 
   796 def remove(ui, repo, file, *files):
   827 def remove(ui, repo, file1, *files):
   797     """remove the specified files on the next commit"""
   828     """remove the specified files on the next commit"""
   798     repo.remove(relpath(repo, (file,) + files))
   829     repo.remove(relpath(repo, (file1,) + files))
   799 
   830 
   800 def revert(ui, repo, *names, **opts):
   831 def revert(ui, repo, *names, **opts):
   801     """revert modified files or dirs back to their unmodified states"""
   832     """revert modified files or dirs back to their unmodified states"""
   802     node = opts['rev'] and repo.lookup(opts['rev']) or \
   833     node = opts['rev'] and repo.lookup(opts['rev']) or \
   803            repo.dirstate.parents()[0]
   834            repo.dirstate.parents()[0]
   817     chosen = {}
   848     chosen = {}
   818 
   849 
   819     def choose(name):
   850     def choose(name):
   820         def body(name):
   851         def body(name):
   821             for r in relnames:
   852             for r in relnames:
   822                 if not name.startswith(r): continue
   853                 if not name.startswith(r):
       
   854                     continue
   823                 rest = name[len(r):]
   855                 rest = name[len(r):]
   824                 if not rest: return r, True
   856                 if not rest:
       
   857                     return r, True
   825                 depth = rest.count(os.sep)
   858                 depth = rest.count(os.sep)
   826                 if not r:
   859                 if not r:
   827                     if depth == 0 or not opts['nonrecursive']: return r, True
   860                     if depth == 0 or not opts['nonrecursive']:
       
   861                         return r, True
   828                 elif rest[0] == os.sep:
   862                 elif rest[0] == os.sep:
   829                     if depth == 1 or not opts['nonrecursive']: return r, True
   863                     if depth == 1 or not opts['nonrecursive']:
       
   864                         return r, True
   830             return None, False
   865             return None, False
   831         relname, ret = body(name)
   866         relname, ret = body(name)
   832         if ret:
   867         if ret:
   833             chosen[relname] = 1
   868             chosen[relname] = 1
   834         return ret
   869         return ret
   873                 respond(" ".join(map(hg.hex, h)) + "\n")
   908                 respond(" ".join(map(hg.hex, h)) + "\n")
   874             if cmd == "lock":
   909             if cmd == "lock":
   875                 lock = repo.lock()
   910                 lock = repo.lock()
   876                 respond("")
   911                 respond("")
   877             if cmd == "unlock":
   912             if cmd == "unlock":
   878                 if lock: lock.release()
   913                 if lock:
       
   914                     lock.release()
   879                 lock = None
   915                 lock = None
   880                 respond("")
   916                 respond("")
   881             elif cmd == "branches":
   917             elif cmd == "branches":
   882                 arg, nodes = getarg()
   918                 arg, nodes = getarg()
   883                 nodes = map(hg.bin, nodes.split(" "))
   919                 nodes = map(hg.bin, nodes.split(" "))
   885                 for b in repo.branches(nodes):
   921                 for b in repo.branches(nodes):
   886                     r.append(" ".join(map(hg.hex, b)) + "\n")
   922                     r.append(" ".join(map(hg.hex, b)) + "\n")
   887                 respond("".join(r))
   923                 respond("".join(r))
   888             elif cmd == "between":
   924             elif cmd == "between":
   889                 arg, pairs = getarg()
   925                 arg, pairs = getarg()
   890                 pairs = [ map(hg.bin, p.split("-")) for p in pairs.split(" ") ]
   926                 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
   891                 r = []
   927                 r = []
   892                 for b in repo.between(pairs):
   928                 for b in repo.between(pairs):
   893                     r.append(" ".join(map(hg.hex, b)) + "\n")
   929                     r.append(" ".join(map(hg.hex, b)) + "\n")
   894                 respond("".join(r))
   930                 respond("".join(r))
   895             elif cmd == "changegroup":
   931             elif cmd == "changegroup":
   898                 nodes = map(hg.bin, roots.split(" "))
   934                 nodes = map(hg.bin, roots.split(" "))
   899 
   935 
   900                 cg = repo.changegroup(nodes)
   936                 cg = repo.changegroup(nodes)
   901                 while 1:
   937                 while 1:
   902                     d = cg.read(4096)
   938                     d = cg.read(4096)
   903                     if not d: break
   939                     if not d:
       
   940                         break
   904                     fout.write(d)
   941                     fout.write(d)
   905 
   942 
   906                 fout.flush()
   943                 fout.flush()
   907 
   944 
   908             elif cmd == "addchangegroup":
   945             elif cmd == "addchangegroup":
   913 
   950 
   914                 r = repo.addchangegroup(fin)
   951                 r = repo.addchangegroup(fin)
   915                 respond("")
   952                 respond("")
   916 
   953 
   917     def openlog(opt, default):
   954     def openlog(opt, default):
   918         if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
   955         if opts[opt] and opts[opt] != '-':
   919         else: return default
   956             return open(opts[opt], 'w')
       
   957         else:
       
   958             return default
   920 
   959 
   921     httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
   960     httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
   922                                 opts["address"], opts["port"],
   961                                 opts["address"], opts["port"],
   923                                 openlog('accesslog', sys.stdout),
   962                                 openlog('accesslog', sys.stdout),
   924                                 openlog('errorlog', sys.stderr))
   963                                 openlog('errorlog', sys.stderr))
   927         if addr == '0.0.0.0':
   966         if addr == '0.0.0.0':
   928             addr = socket.gethostname()
   967             addr = socket.gethostname()
   929         else:
   968         else:
   930             try:
   969             try:
   931                 addr = socket.gethostbyaddr(addr)[0]
   970                 addr = socket.gethostbyaddr(addr)[0]
   932             except: pass
   971             except socket.error:
       
   972                 pass
   933         if port != 80:
   973         if port != 80:
   934             ui.status('listening at http://%s:%d/\n' % (addr, port))
   974             ui.status('listening at http://%s:%d/\n' % (addr, port))
   935         else:
   975         else:
   936             ui.status('listening at http://%s/\n' % addr)
   976             ui.status('listening at http://%s/\n' % addr)
   937     httpd.serve_forever()
   977     httpd.serve_forever()
   945     ? = not tracked'''
   985     ? = not tracked'''
   946 
   986 
   947     (c, a, d, u) = repo.changes(None, None)
   987     (c, a, d, u) = repo.changes(None, None)
   948     (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
   988     (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
   949 
   989 
   950     for f in c: ui.write("C ", f, "\n")
   990     for f in c:
   951     for f in a: ui.write("A ", f, "\n")
   991         ui.write("C ", f, "\n")
   952     for f in d: ui.write("R ", f, "\n")
   992     for f in a:
   953     for f in u: ui.write("? ", f, "\n")
   993         ui.write("A ", f, "\n")
   954 
   994     for f in d:
   955 def tag(ui, repo, name, rev = None, **opts):
   995         ui.write("R ", f, "\n")
       
   996     for f in u:
       
   997         ui.write("? ", f, "\n")
       
   998 
       
   999 def tag(ui, repo, name, rev=None, **opts):
   956     """add a tag for the current tip or a given revision"""
  1000     """add a tag for the current tip or a given revision"""
   957 
  1001 
   958     if name == "tip":
  1002     if name == "tip":
   959         ui.warn("abort: 'tip' is a reserved name!\n")
  1003         ui.warn("abort: 'tip' is a reserved name!\n")
   960         return -1
  1004         return -1
   976         if ".hgtags" in x:
  1020         if ".hgtags" in x:
   977             ui.warn("abort: working copy of .hgtags is changed!\n")
  1021             ui.warn("abort: working copy of .hgtags is changed!\n")
   978             ui.status("(please commit .hgtags manually)\n")
  1022             ui.status("(please commit .hgtags manually)\n")
   979             return -1
  1023             return -1
   980 
  1024 
   981     add = 0
  1025     add = not os.path.exists(repo.wjoin(".hgtags"))
   982     if not os.path.exists(repo.wjoin(".hgtags")): add = 1
       
   983     repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
  1026     repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
   984     if add: repo.add([".hgtags"])
  1027     if add:
       
  1028         repo.add([".hgtags"])
   985 
  1029 
   986     if not opts['text']:
  1030     if not opts['text']:
   987         opts['text'] = "Added tag %s for changeset %s" % (name, r)
  1031         opts['text'] = "Added tag %s for changeset %s" % (name, r)
   988 
  1032 
   989     repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
  1033     repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
  1043 # Command options and aliases are listed here, alphabetically
  1087 # Command options and aliases are listed here, alphabetically
  1044 
  1088 
  1045 table = {
  1089 table = {
  1046     "^add": (add, [], "hg add [files]"),
  1090     "^add": (add, [], "hg add [files]"),
  1047     "addremove": (addremove, [], "hg addremove [files]"),
  1091     "addremove": (addremove, [], "hg addremove [files]"),
  1048     "^annotate": (annotate,
  1092     "^annotate":
  1049                      [('r', 'revision', '', 'revision'),
  1093         (annotate,
  1050                       ('u', 'user', None, 'show user'),
  1094          [('r', 'revision', '', 'revision'),
  1051                       ('n', 'number', None, 'show revision number'),
  1095           ('u', 'user', None, 'show user'),
  1052                       ('c', 'changeset', None, 'show changeset')],
  1096           ('n', 'number', None, 'show revision number'),
  1053                      'hg annotate [-u] [-c] [-n] [-r id] [files]'),
  1097           ('c', 'changeset', None, 'show changeset')],
  1054     "cat": (cat, [('o', 'output', "", 'output to file')], 'hg cat [-o outfile] <file> [rev]'),
  1098          'hg annotate [-u] [-c] [-n] [-r id] [files]'),
  1055     "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
  1099     "cat":
  1056               'hg clone [options] <source> [dest]'),
  1100         (cat,
  1057     "^commit|ci": (commit,
  1101          [('o', 'output', "", 'output to file')],
  1058                   [('t', 'text', "", 'commit text'),
  1102          'hg cat [-o outfile] <file> [rev]'),
  1059                    ('A', 'addremove', None, 'run add/remove during commit'),
  1103     "^clone":
  1060                    ('l', 'logfile', "", 'commit text file'),
  1104         (clone,
  1061                    ('d', 'date', "", 'date code'),
  1105          [('U', 'noupdate', None, 'skip update after cloning')],
  1062                    ('u', 'user', "", 'user')],
  1106          'hg clone [options] <source> [dest]'),
  1063                   'hg commit [files]'),
  1107     "^commit|ci":
       
  1108         (commit,
       
  1109          [('t', 'text', "", 'commit text'),
       
  1110           ('A', 'addremove', None, 'run add/remove during commit'),
       
  1111           ('l', 'logfile', "", 'commit text file'),
       
  1112           ('d', 'date', "", 'date code'),
       
  1113           ('u', 'user', "", 'user')],
       
  1114          'hg commit [files]'),
  1064     "copy": (copy, [], 'hg copy <source> <dest>'),
  1115     "copy": (copy, [], 'hg copy <source> <dest>'),
  1065     "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
  1116     "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
  1066     "debugstate": (debugstate, [], 'debugstate'),
  1117     "debugstate": (debugstate, [], 'debugstate'),
  1067     "debugindex": (debugindex, [], 'debugindex <file>'),
  1118     "debugindex": (debugindex, [], 'debugindex <file>'),
  1068     "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
  1119     "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
  1069     "^diff": (diff, [('r', 'rev', [], 'revision')],
  1120     "^diff":
  1070              'hg diff [-r A] [-r B] [files]'),
  1121         (diff,
  1071     "^export": (export, [('o', 'output', "", 'output to file')],
  1122          [('r', 'rev', [], 'revision')],
  1072                "hg export [-o file] <changeset> ..."),
  1123          'hg diff [-r A] [-r B] [files]'),
       
  1124     "^export":
       
  1125         (export,
       
  1126          [('o', 'output', "", 'output to file')],
       
  1127          "hg export [-o file] <changeset> ..."),
  1073     "forget": (forget, [], "hg forget [files]"),
  1128     "forget": (forget, [], "hg forget [files]"),
  1074     "heads": (heads, [], 'hg heads'),
  1129     "heads": (heads, [], 'hg heads'),
  1075     "help": (help, [], 'hg help [command]'),
  1130     "help": (help_, [], 'hg help [command]'),
  1076     "identify|id": (identify, [], 'hg identify'),
  1131     "identify|id": (identify, [], 'hg identify'),
  1077     "import|patch": (import_,
  1132     "import|patch":
  1078                      [('p', 'strip', 1, 'path strip'),
  1133         (import_,
  1079                       ('b', 'base', "", 'base path')],
  1134          [('p', 'strip', 1, 'path strip'),
  1080                      "hg import [options] <patches>"),
  1135           ('b', 'base', "", 'base path')],
       
  1136          "hg import [options] <patches>"),
  1081     "^init": (init, [], 'hg init'),
  1137     "^init": (init, [], 'hg init'),
  1082     "locate": (locate,
  1138     "locate":
  1083                [('0', 'print0', None, 'end records with NUL'),
  1139         (locate,
  1084                 ('f', 'fullpath', None, 'print complete paths'),
  1140          [('0', 'print0', None, 'end records with NUL'),
  1085                 ('i', 'include', [], 'include path in search'),
  1141           ('f', 'fullpath', None, 'print complete paths'),
  1086                 ('r', 'rev', '', 'revision'),
  1142           ('i', 'include', [], 'include path in search'),
  1087                 ('x', 'exclude', [], 'exclude path from search')],
  1143           ('r', 'rev', '', 'revision'),
  1088                'hg locate [options] [files]'),
  1144           ('x', 'exclude', [], 'exclude path from search')],
  1089     "^log|history": (log,
  1145          'hg locate [options] [files]'),
  1090                     [('r', 'rev', [], 'revision'),
  1146     "^log|history":
  1091                      ('p', 'patch', None, 'show patch')],
  1147         (log,
  1092                     'hg log [-r A] [-r B] [-p] [file]'),
  1148          [('r', 'rev', [], 'revision'),
       
  1149           ('p', 'patch', None, 'show patch')],
       
  1150          'hg log [-r A] [-r B] [-p] [file]'),
  1093     "manifest": (manifest, [], 'hg manifest [rev]'),
  1151     "manifest": (manifest, [], 'hg manifest [rev]'),
  1094     "parents": (parents, [], 'hg parents [node]'),
  1152     "parents": (parents, [], 'hg parents [node]'),
  1095     "^pull": (pull,
  1153     "^pull":
  1096                   [('u', 'update', None, 'update working directory')],
  1154         (pull,
  1097                   'hg pull [options] [source]'),
  1155          [('u', 'update', None, 'update working directory')],
       
  1156          'hg pull [options] [source]'),
  1098     "^push": (push, [], 'hg push <destination>'),
  1157     "^push": (push, [], 'hg push <destination>'),
  1099     "rawcommit": (rawcommit,
  1158     "rawcommit":
  1100                   [('p', 'parent', [], 'parent'),
  1159         (rawcommit,
  1101                    ('d', 'date', "", 'date code'),
  1160          [('p', 'parent', [], 'parent'),
  1102                    ('u', 'user', "", 'user'),
  1161           ('d', 'date', "", 'date code'),
  1103                    ('F', 'files', "", 'file list'),
  1162           ('u', 'user', "", 'user'),
  1104                    ('t', 'text', "", 'commit text'),
  1163           ('F', 'files', "", 'file list'),
  1105                    ('l', 'logfile', "", 'commit text file')],
  1164           ('t', 'text', "", 'commit text'),
  1106                   'hg rawcommit [options] [files]'),
  1165           ('l', 'logfile', "", 'commit text file')],
       
  1166          'hg rawcommit [options] [files]'),
  1107     "recover": (recover, [], "hg recover"),
  1167     "recover": (recover, [], "hg recover"),
  1108     "^remove|rm": (remove, [], "hg remove [files]"),
  1168     "^remove|rm": (remove, [], "hg remove [files]"),
  1109     "^revert": (revert,
  1169     "^revert":
  1110                [("n", "nonrecursive", None, "don't recurse into subdirs"),
  1170         (revert,
  1111                 ("r", "rev", "", "revision")],
  1171          [("n", "nonrecursive", None, "don't recurse into subdirs"),
  1112                "hg revert [files|dirs]"),
  1172           ("r", "rev", "", "revision")],
       
  1173          "hg revert [files|dirs]"),
  1113     "root": (root, [], "hg root"),
  1174     "root": (root, [], "hg root"),
  1114     "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
  1175     "^serve":
  1115                        ('E', 'errorlog', '', 'error log file'),
  1176         (serve,
  1116                        ('p', 'port', 8000, 'listen port'),
  1177          [('A', 'accesslog', '', 'access log file'),
  1117                        ('a', 'address', '', 'interface address'),
  1178           ('E', 'errorlog', '', 'error log file'),
  1118                        ('n', 'name', os.getcwd(), 'repository name'),
  1179           ('p', 'port', 8000, 'listen port'),
  1119                        ('', 'stdio', None, 'for remote clients'),
  1180           ('a', 'address', '', 'interface address'),
  1120                        ('t', 'templates', "", 'template map')],
  1181           ('n', 'name', os.getcwd(), 'repository name'),
  1121               "hg serve [options]"),
  1182           ('', 'stdio', None, 'for remote clients'),
       
  1183           ('t', 'templates', "", 'template map')],
       
  1184          "hg serve [options]"),
  1122     "^status": (status, [], 'hg status'),
  1185     "^status": (status, [], 'hg status'),
  1123     "tag": (tag,  [('l', 'local', None, 'make the tag local'),
  1186     "tag":
  1124                    ('t', 'text', "", 'commit text'),
  1187         (tag,
  1125                    ('d', 'date', "", 'date code'),
  1188          [('l', 'local', None, 'make the tag local'),
  1126                    ('u', 'user', "", 'user')],
  1189           ('t', 'text', "", 'commit text'),
  1127             'hg tag [options] <name> [rev]'),
  1190           ('d', 'date', "", 'date code'),
       
  1191           ('u', 'user', "", 'user')],
       
  1192          'hg tag [options] <name> [rev]'),
  1128     "tags": (tags, [], 'hg tags'),
  1193     "tags": (tags, [], 'hg tags'),
  1129     "tip": (tip, [], 'hg tip'),
  1194     "tip": (tip, [], 'hg tip'),
  1130     "undo": (undo, [], 'hg undo'),
  1195     "undo": (undo, [], 'hg undo'),
  1131     "^update|up|checkout|co":
  1196     "^update|up|checkout|co":
  1132             (update,
  1197         (update,
  1133              [('m', 'merge', None, 'allow merging of conflicts'),
  1198          [('m', 'merge', None, 'allow merging of conflicts'),
  1134               ('C', 'clean', None, 'overwrite locally modified files')],
  1199           ('C', 'clean', None, 'overwrite locally modified files')],
  1135              'hg update [options] [node]'),
  1200          'hg update [options] [node]'),
  1136     "verify": (verify, [], 'hg verify'),
  1201     "verify": (verify, [], 'hg verify'),
  1137     "version": (show_version, [], 'hg version'),
  1202     "version": (show_version, [], 'hg version'),
  1138     }
  1203     }
  1139 
  1204 
  1140 globalopts = [('v', 'verbose', None, 'verbose'),
  1205 globalopts = [('v', 'verbose', None, 'verbose'),
  1143               ('', 'profile', None, 'profile'),
  1208               ('', 'profile', None, 'profile'),
  1144               ('R', 'repository', "", 'repository root directory'),
  1209               ('R', 'repository', "", 'repository root directory'),
  1145               ('', 'traceback', None, 'print traceback on exception'),
  1210               ('', 'traceback', None, 'print traceback on exception'),
  1146               ('y', 'noninteractive', None, 'run non-interactively'),
  1211               ('y', 'noninteractive', None, 'run non-interactively'),
  1147               ('', 'version', None, 'output version information and exit'),
  1212               ('', 'version', None, 'output version information and exit'),
  1148               ]
  1213              ]
  1149 
  1214 
  1150 norepo = "clone init version help debugindex debugindexdot"
  1215 norepo = "clone init version help debugindex debugindexdot"
  1151 
  1216 
  1152 def find(cmd):
  1217 def find(cmd):
  1153     for e in table.keys():
  1218     for e in table.keys():
  1154         if re.match("(%s)$" % e, cmd):
  1219         if re.match("(%s)$" % e, cmd):
  1155             return table[e]
  1220             return table[e]
  1156 
  1221 
  1157     raise UnknownCommand(cmd)
  1222     raise UnknownCommand(cmd)
  1158 
  1223 
  1159 class SignalInterrupt(Exception): pass
  1224 class SignalInterrupt(Exception):
       
  1225     """Exception raised on SIGTERM and SIGHUP."""
  1160 
  1226 
  1161 def catchterm(*args):
  1227 def catchterm(*args):
  1162     raise SignalInterrupt
  1228     raise SignalInterrupt
  1163 
  1229 
  1164 def run():
  1230 def run():
  1165     sys.exit(dispatch(sys.argv[1:]))
  1231     sys.exit(dispatch(sys.argv[1:]))
  1166 
  1232 
  1167 class ParseError(Exception): pass
  1233 class ParseError(Exception):
       
  1234     """Exception raised on errors in parsing the command line."""
  1168 
  1235 
  1169 def parse(args):
  1236 def parse(args):
  1170     options = {}
  1237     options = {}
  1171     cmdoptions = {}
  1238     cmdoptions = {}
  1172 
  1239 
  1176         raise ParseError(None, inst)
  1243         raise ParseError(None, inst)
  1177 
  1244 
  1178     if options["version"]:
  1245     if options["version"]:
  1179         return ("version", show_version, [], options, cmdoptions)
  1246         return ("version", show_version, [], options, cmdoptions)
  1180     elif not args:
  1247     elif not args:
  1181         return ("help", help, [], options, cmdoptions)
  1248         return ("help", help_, [], options, cmdoptions)
  1182     else:
  1249     else:
  1183         cmd, args = args[0], args[1:]
  1250         cmd, args = args[0], args[1:]
  1184 
  1251 
  1185     i = find(cmd)
  1252     i = find(cmd)
  1186 
  1253 
  1187     # combine global options into local
  1254     # combine global options into local
  1188     c = list(i[1])
  1255     c = list(i[1])
  1189     l = len(c)
       
  1190     for o in globalopts:
  1256     for o in globalopts:
  1191         c.append((o[0], o[1], options[o[1]], o[3]))
  1257         c.append((o[0], o[1], options[o[1]], o[3]))
  1192 
  1258 
  1193     try:
  1259     try:
  1194         args = fancyopts.fancyopts(args, c, cmdoptions)
  1260         args = fancyopts.fancyopts(args, c, cmdoptions)
  1203 
  1269 
  1204     return (cmd, i[0], args, options, cmdoptions)
  1270     return (cmd, i[0], args, options, cmdoptions)
  1205 
  1271 
  1206 def dispatch(args):
  1272 def dispatch(args):
  1207     signal.signal(signal.SIGTERM, catchterm)
  1273     signal.signal(signal.SIGTERM, catchterm)
  1208     try: signal.signal(signal.SIGHUP, catchterm)
  1274     try:
  1209     except: pass
  1275         signal.signal(signal.SIGHUP, catchterm)
       
  1276     except AttributeError:
       
  1277         pass
  1210 
  1278 
  1211     try:
  1279     try:
  1212         cmd, func, args, options, cmdoptions = parse(args)
  1280         cmd, func, args, options, cmdoptions = parse(args)
  1213     except ParseError, inst:
  1281     except ParseError, inst:
  1214         u = ui.ui()
  1282         u = ui.ui()
  1215         if inst.args[0]:
  1283         if inst.args[0]:
  1216             u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
  1284             u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
  1217             help(u, inst.args[0])
  1285             help_(u, inst.args[0])
  1218         else:
  1286         else:
  1219             u.warn("hg: %s\n" % inst.args[1])
  1287             u.warn("hg: %s\n" % inst.args[1])
  1220             help(u)
  1288             help_(u)
  1221         sys.exit(-1)
  1289         sys.exit(-1)
  1222     except UnknownCommand, inst:
  1290     except UnknownCommand, inst:
  1223         u = ui.ui()
  1291         u = ui.ui()
  1224         u.warn("hg: unknown command '%s'\n" % inst.args[0])
  1292         u.warn("hg: unknown command '%s'\n" % inst.args[0])
  1225         help(u)
  1293         help_(u)
  1226         sys.exit(1)
  1294         sys.exit(1)
  1227 
  1295 
  1228     u = ui.ui(options["verbose"], options["debug"], options["quiet"],
  1296     u = ui.ui(options["verbose"], options["debug"], options["quiet"],
  1229                      not options["noninteractive"])
  1297               not options["noninteractive"])
  1230 
  1298 
  1231     try:
  1299     try:
  1232         try:
  1300         try:
  1233             if cmd not in norepo.split():
  1301             if cmd not in norepo.split():
  1234                 path = options["repository"] or ""
  1302                 path = options["repository"] or ""
  1280         tb = traceback.extract_tb(sys.exc_info()[2])
  1348         tb = traceback.extract_tb(sys.exc_info()[2])
  1281         if len(tb) > 2: # no
  1349         if len(tb) > 2: # no
  1282             raise
  1350             raise
  1283         u.debug(inst, "\n")
  1351         u.debug(inst, "\n")
  1284         u.warn("%s: invalid arguments\n" % cmd)
  1352         u.warn("%s: invalid arguments\n" % cmd)
  1285         help(u, cmd)
  1353         help_(u, cmd)
  1286 
  1354 
  1287     sys.exit(-1)
  1355     sys.exit(-1)