mercurial/commands.py
changeset 2874 4ec58b157265
parent 2871 ffa2be02c4e5
child 2875 3d6efcbbd1c9
equal deleted inserted replaced
2873:5dd6631c8238 2874:4ec58b157265
     8 from demandload import demandload
     8 from demandload import demandload
     9 from node import *
     9 from node import *
    10 from i18n import gettext as _
    10 from i18n import gettext as _
    11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
    11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
    12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
    12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
    13 demandload(globals(), "fnmatch mdiff difflib patch random signal tempfile time")
    13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
    14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
    14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
    15 demandload(globals(), "archival cStringIO changegroup")
    15 demandload(globals(), "archival cStringIO changegroup")
    16 demandload(globals(), "hgweb.server sshserver")
    16 demandload(globals(), "cmdutil hgweb.server sshserver")
    17 
    17 
    18 class UnknownCommand(Exception):
    18 class UnknownCommand(Exception):
    19     """Exception raised if command is not in the command table."""
    19     """Exception raised if command is not in the command table."""
    20 class AmbiguousCommand(Exception):
    20 class AmbiguousCommand(Exception):
    21     """Exception raised if command shortcut matches more than one command."""
    21     """Exception raised if command shortcut matches more than one command."""
    22 
    22 
    23 def bail_if_changed(repo):
    23 def bail_if_changed(repo):
    24     modified, added, removed, deleted, unknown = repo.changes()
    24     modified, added, removed, deleted, unknown = repo.changes()
    25     if modified or added or removed or deleted:
    25     if modified or added or removed or deleted:
    26         raise util.Abort(_("outstanding uncommitted changes"))
    26         raise util.Abort(_("outstanding uncommitted changes"))
    27 
       
    28 def filterfiles(filters, files):
       
    29     l = [x for x in files if x in filters]
       
    30 
       
    31     for t in filters:
       
    32         if t and t[-1] != "/":
       
    33             t += "/"
       
    34         l += [x for x in files if x.startswith(t)]
       
    35     return l
       
    36 
    27 
    37 def relpath(repo, args):
    28 def relpath(repo, args):
    38     cwd = repo.getcwd()
    29     cwd = repo.getcwd()
    39     if cwd:
    30     if cwd:
    40         return [util.normpath(os.path.join(cwd, x)) for x in args]
    31         return [util.normpath(os.path.join(cwd, x)) for x in args]
   342             if rev in seen:
   333             if rev in seen:
   343                 continue
   334                 continue
   344             seen[rev] = 1
   335             seen[rev] = 1
   345             yield str(rev)
   336             yield str(rev)
   346 
   337 
   347 def make_filename(repo, pat, node,
       
   348                   total=None, seqno=None, revwidth=None, pathname=None):
       
   349     node_expander = {
       
   350         'H': lambda: hex(node),
       
   351         'R': lambda: str(repo.changelog.rev(node)),
       
   352         'h': lambda: short(node),
       
   353         }
       
   354     expander = {
       
   355         '%': lambda: '%',
       
   356         'b': lambda: os.path.basename(repo.root),
       
   357         }
       
   358 
       
   359     try:
       
   360         if node:
       
   361             expander.update(node_expander)
       
   362         if node and revwidth is not None:
       
   363             expander['r'] = (lambda:
       
   364                     str(repo.changelog.rev(node)).zfill(revwidth))
       
   365         if total is not None:
       
   366             expander['N'] = lambda: str(total)
       
   367         if seqno is not None:
       
   368             expander['n'] = lambda: str(seqno)
       
   369         if total is not None and seqno is not None:
       
   370             expander['n'] = lambda:str(seqno).zfill(len(str(total)))
       
   371         if pathname is not None:
       
   372             expander['s'] = lambda: os.path.basename(pathname)
       
   373             expander['d'] = lambda: os.path.dirname(pathname) or '.'
       
   374             expander['p'] = lambda: pathname
       
   375 
       
   376         newname = []
       
   377         patlen = len(pat)
       
   378         i = 0
       
   379         while i < patlen:
       
   380             c = pat[i]
       
   381             if c == '%':
       
   382                 i += 1
       
   383                 c = pat[i]
       
   384                 c = expander[c]()
       
   385             newname.append(c)
       
   386             i += 1
       
   387         return ''.join(newname)
       
   388     except KeyError, inst:
       
   389         raise util.Abort(_("invalid format spec '%%%s' in output file name"),
       
   390                     inst.args[0])
       
   391 
       
   392 def make_file(repo, pat, node=None,
       
   393               total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
       
   394     if not pat or pat == '-':
       
   395         return 'w' in mode and sys.stdout or sys.stdin
       
   396     if hasattr(pat, 'write') and 'w' in mode:
       
   397         return pat
       
   398     if hasattr(pat, 'read') and 'r' in mode:
       
   399         return pat
       
   400     return open(make_filename(repo, pat, node, total, seqno, revwidth,
       
   401                               pathname),
       
   402                 mode)
       
   403 
       
   404 def write_bundle(cg, filename=None, compress=True):
   338 def write_bundle(cg, filename=None, compress=True):
   405     """Write a bundle file and return its filename.
   339     """Write a bundle file and return its filename.
   406 
   340 
   407     Existing files will not be overwritten.
   341     Existing files will not be overwritten.
   408     If no filename is specified, a temporary file is created.
   342     If no filename is specified, a temporary file is created.
   450     finally:
   384     finally:
   451         if fh is not None:
   385         if fh is not None:
   452             fh.close()
   386             fh.close()
   453         if cleanup is not None:
   387         if cleanup is not None:
   454             os.unlink(cleanup)
   388             os.unlink(cleanup)
   455 
       
   456 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
       
   457            changes=None, text=False, opts={}):
       
   458     if not node1:
       
   459         node1 = repo.dirstate.parents()[0]
       
   460     # reading the data for node1 early allows it to play nicely
       
   461     # with repo.changes and the revlog cache.
       
   462     change = repo.changelog.read(node1)
       
   463     mmap = repo.manifest.read(change[0])
       
   464     date1 = util.datestr(change[2])
       
   465 
       
   466     if not changes:
       
   467         changes = repo.changes(node1, node2, files, match=match)
       
   468     modified, added, removed, deleted, unknown = changes
       
   469     if files:
       
   470         modified, added, removed = map(lambda x: filterfiles(files, x),
       
   471                                        (modified, added, removed))
       
   472 
       
   473     if not modified and not added and not removed:
       
   474         return
       
   475 
       
   476     if node2:
       
   477         change = repo.changelog.read(node2)
       
   478         mmap2 = repo.manifest.read(change[0])
       
   479         _date2 = util.datestr(change[2])
       
   480         def date2(f):
       
   481             return _date2
       
   482         def read(f):
       
   483             return repo.file(f).read(mmap2[f])
       
   484     else:
       
   485         tz = util.makedate()[1]
       
   486         _date2 = util.datestr()
       
   487         def date2(f):
       
   488             try:
       
   489                 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
       
   490             except OSError, err:
       
   491                 if err.errno != errno.ENOENT: raise
       
   492                 return _date2
       
   493         def read(f):
       
   494             return repo.wread(f)
       
   495 
       
   496     if ui.quiet:
       
   497         r = None
       
   498     else:
       
   499         hexfunc = ui.verbose and hex or short
       
   500         r = [hexfunc(node) for node in [node1, node2] if node]
       
   501 
       
   502     diffopts = ui.diffopts()
       
   503     showfunc = opts.get('show_function') or diffopts['showfunc']
       
   504     ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
       
   505     ignorewsamount = opts.get('ignore_space_change') or \
       
   506                      diffopts['ignorewsamount']
       
   507     ignoreblanklines = opts.get('ignore_blank_lines') or \
       
   508                      diffopts['ignoreblanklines']
       
   509 
       
   510     all = modified + added + removed
       
   511     all.sort()
       
   512     for f in all:
       
   513         to = None
       
   514         tn = None
       
   515         if f in mmap:
       
   516             to = repo.file(f).read(mmap[f])
       
   517         if f not in removed:
       
   518             tn = read(f)
       
   519         fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
       
   520                                showfunc=showfunc, ignorews=ignorews,
       
   521                                ignorewsamount=ignorewsamount,
       
   522                                ignoreblanklines=ignoreblanklines))
       
   523 
   389 
   524 def trimuser(ui, name, rev, revcache):
   390 def trimuser(ui, name, rev, revcache):
   525     """trim the name of the user who committed a change"""
   391     """trim the name of the user who committed a change"""
   526     user = revcache.get(rev)
   392     user = revcache.get(rev)
   527     if user is None:
   393     if user is None:
   920         node, p2 = repo.dirstate.parents()
   786         node, p2 = repo.dirstate.parents()
   921         if p2 != nullid:
   787         if p2 != nullid:
   922             raise util.Abort(_('uncommitted merge - please provide a '
   788             raise util.Abort(_('uncommitted merge - please provide a '
   923                                'specific revision'))
   789                                'specific revision'))
   924 
   790 
   925     dest = make_filename(repo, dest, node)
   791     dest = cmdutil.make_filename(repo, dest, node)
   926     if os.path.realpath(dest) == repo.root:
   792     if os.path.realpath(dest) == repo.root:
   927         raise util.Abort(_('repository root cannot be destination'))
   793         raise util.Abort(_('repository root cannot be destination'))
   928     dummy, matchfn, dummy = matchpats(repo, [], opts)
   794     dummy, matchfn, dummy = matchpats(repo, [], opts)
   929     kind = opts.get('type') or 'files'
   795     kind = opts.get('type') or 'files'
   930     prefix = opts['prefix']
   796     prefix = opts['prefix']
   931     if dest == '-':
   797     if dest == '-':
   932         if kind == 'files':
   798         if kind == 'files':
   933             raise util.Abort(_('cannot archive plain files to stdout'))
   799             raise util.Abort(_('cannot archive plain files to stdout'))
   934         dest = sys.stdout
   800         dest = sys.stdout
   935         if not prefix: prefix = os.path.basename(repo.root) + '-%h'
   801         if not prefix: prefix = os.path.basename(repo.root) + '-%h'
   936     prefix = make_filename(repo, prefix, node)
   802     prefix = cmdutil.make_filename(repo, prefix, node)
   937     archival.archive(repo, dest, node, kind, not opts['no_decode'],
   803     archival.archive(repo, dest, node, kind, not opts['no_decode'],
   938                      matchfn, prefix)
   804                      matchfn, prefix)
   939 
   805 
   940 def backout(ui, repo, rev, **opts):
   806 def backout(ui, repo, rev, **opts):
   941     '''reverse effect of earlier changeset
   807     '''reverse effect of earlier changeset
  1036     %d   dirname of file being printed, or '.' if in repo root
   902     %d   dirname of file being printed, or '.' if in repo root
  1037     %p   root-relative path name of file being printed
   903     %p   root-relative path name of file being printed
  1038     """
   904     """
  1039     ctx = repo.changectx(opts['rev'] or "-1")
   905     ctx = repo.changectx(opts['rev'] or "-1")
  1040     for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
   906     for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
  1041         fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
   907         fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
  1042         fp.write(ctx.filectx(abs).data())
   908         fp.write(ctx.filectx(abs).data())
  1043 
   909 
  1044 def clone(ui, source, dest=None, **opts):
   910 def clone(ui, source, dest=None, **opts):
  1045     """make a copy of an existing repository
   911     """make a copy of an existing repository
  1046 
   912 
  1505     """
  1371     """
  1506     node1, node2 = revpair(ui, repo, opts['rev'])
  1372     node1, node2 = revpair(ui, repo, opts['rev'])
  1507 
  1373 
  1508     fns, matchfn, anypats = matchpats(repo, pats, opts)
  1374     fns, matchfn, anypats = matchpats(repo, pats, opts)
  1509 
  1375 
  1510     dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
  1376     patch.diff(repo, node1, node2, fns, match=matchfn,
  1511            text=opts['text'], opts=opts)
  1377                opts=ui.diffopts(opts))
  1512 
       
  1513 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
       
  1514     node = repo.lookup(changeset)
       
  1515     parents = [p for p in repo.changelog.parents(node) if p != nullid]
       
  1516     if opts['switch_parent']:
       
  1517         parents.reverse()
       
  1518     prev = (parents and parents[0]) or nullid
       
  1519     change = repo.changelog.read(node)
       
  1520 
       
  1521     fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
       
  1522                    revwidth=revwidth)
       
  1523     if fp != sys.stdout:
       
  1524         ui.note("%s\n" % fp.name)
       
  1525 
       
  1526     fp.write("# HG changeset patch\n")
       
  1527     fp.write("# User %s\n" % change[1])
       
  1528     fp.write("# Date %d %d\n" % change[2])
       
  1529     fp.write("# Node ID %s\n" % hex(node))
       
  1530     fp.write("# Parent  %s\n" % hex(prev))
       
  1531     if len(parents) > 1:
       
  1532         fp.write("# Parent  %s\n" % hex(parents[1]))
       
  1533     fp.write(change[4].rstrip())
       
  1534     fp.write("\n\n")
       
  1535 
       
  1536     dodiff(fp, ui, repo, prev, node, text=opts['text'])
       
  1537     if fp != sys.stdout:
       
  1538         fp.close()
       
  1539 
  1378 
  1540 def export(ui, repo, *changesets, **opts):
  1379 def export(ui, repo, *changesets, **opts):
  1541     """dump the header and diffs for one or more changesets
  1380     """dump the header and diffs for one or more changesets
  1542 
  1381 
  1543     Print the changeset header and diffs for one or more revisions.
  1382     Print the changeset header and diffs for one or more revisions.
  1564     With the --switch-parent option, the diff will be against the second
  1403     With the --switch-parent option, the diff will be against the second
  1565     parent. It can be useful to review a merge.
  1404     parent. It can be useful to review a merge.
  1566     """
  1405     """
  1567     if not changesets:
  1406     if not changesets:
  1568         raise util.Abort(_("export requires at least one changeset"))
  1407         raise util.Abort(_("export requires at least one changeset"))
  1569     seqno = 0
       
  1570     revs = list(revrange(ui, repo, changesets))
  1408     revs = list(revrange(ui, repo, changesets))
  1571     total = len(revs)
  1409     if len(revs) > 1:
  1572     revwidth = max(map(len, revs))
  1410         ui.note(_('exporting patches:\n'))
  1573     msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
  1411     else:
  1574     ui.note(msg)
  1412         ui.note(_('exporting patch:\n'))
  1575     for cset in revs:
  1413     patch.export(repo, map(repo.lookup, revs), template=opts['output'],
  1576         seqno += 1
  1414                  switch_parent=opts['switch_parent'], opts=ui.diffopts(opts))
  1577         doexport(ui, repo, cset, seqno, total, revwidth, opts)
       
  1578 
  1415 
  1579 def forget(ui, repo, *pats, **opts):
  1416 def forget(ui, repo, *pats, **opts):
  1580     """don't add the specified files on the next commit (DEPRECATED)
  1417     """don't add the specified files on the next commit (DEPRECATED)
  1581 
  1418 
  1582     (DEPRECATED)
  1419     (DEPRECATED)
  1961             if opts['no_merges'] and len(parents) == 2:
  1798             if opts['no_merges'] and len(parents) == 2:
  1962                 continue
  1799                 continue
  1963             displayer.show(changenode=n)
  1800             displayer.show(changenode=n)
  1964             if opts['patch']:
  1801             if opts['patch']:
  1965                 prev = (parents and parents[0]) or nullid
  1802                 prev = (parents and parents[0]) or nullid
  1966                 dodiff(ui, ui, other, prev, n)
  1803                 patch.diff(repo, other, prev, n)
  1967                 ui.write("\n")
  1804                 ui.write("\n")
  1968     finally:
  1805     finally:
  1969         if hasattr(other, 'close'):
  1806         if hasattr(other, 'close'):
  1970             other.close()
  1807             other.close()
  1971         if cleanup:
  1808         if cleanup:
  2112                 br = repo.branchlookup([repo.changelog.node(rev)])
  1949                 br = repo.branchlookup([repo.changelog.node(rev)])
  2113 
  1950 
  2114             displayer.show(rev, brinfo=br)
  1951             displayer.show(rev, brinfo=br)
  2115             if opts['patch']:
  1952             if opts['patch']:
  2116                 prev = (parents and parents[0]) or nullid
  1953                 prev = (parents and parents[0]) or nullid
  2117                 dodiff(du, du, repo, prev, changenode, match=matchfn)
  1954                 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
  2118                 du.write("\n\n")
  1955                 du.write("\n\n")
  2119         elif st == 'iter':
  1956         elif st == 'iter':
  2120             if count == limit: break
  1957             if count == limit: break
  2121             if du.header[rev]:
  1958             if du.header[rev]:
  2122                 for args in du.header[rev]:
  1959                 for args in du.header[rev]:
  2193         if opts['no_merges'] and len(parents) == 2:
  2030         if opts['no_merges'] and len(parents) == 2:
  2194             continue
  2031             continue
  2195         displayer.show(changenode=n)
  2032         displayer.show(changenode=n)
  2196         if opts['patch']:
  2033         if opts['patch']:
  2197             prev = (parents and parents[0]) or nullid
  2034             prev = (parents and parents[0]) or nullid
  2198             dodiff(ui, ui, repo, prev, n)
  2035             patch.diff(repo, prev, n)
  2199             ui.write("\n")
  2036             ui.write("\n")
  2200 
  2037 
  2201 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
  2038 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
  2202     """show the parents of the working dir or revision
  2039     """show the parents of the working dir or revision
  2203 
  2040 
  2841     br = None
  2678     br = None
  2842     if opts['branches']:
  2679     if opts['branches']:
  2843         br = repo.branchlookup([n])
  2680         br = repo.branchlookup([n])
  2844     show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
  2681     show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
  2845     if opts['patch']:
  2682     if opts['patch']:
  2846         dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
  2683         patch.diff(repo, repo.changelog.parents(n)[0], n)
  2847 
  2684 
  2848 def unbundle(ui, repo, fname, **opts):
  2685 def unbundle(ui, repo, fname, **opts):
  2849     """apply a changegroup file
  2686     """apply a changegroup file
  2850 
  2687 
  2851     Apply a compressed changegroup file generated by the bundle
  2688     Apply a compressed changegroup file generated by the bundle