mercurial/commands.py
changeset 1584 b3e94785ab69
parent 1582 63799b01985c
child 1586 5c5aaaa9ab6f
equal deleted inserted replaced
1583:32a4e6802864 1584:b3e94785ab69
    13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
    13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
    14 demandload(globals(), "errno socket version struct atexit sets bz2")
    14 demandload(globals(), "errno socket version struct atexit sets bz2")
    15 
    15 
    16 class UnknownCommand(Exception):
    16 class UnknownCommand(Exception):
    17     """Exception raised if command is not in the command table."""
    17     """Exception raised if command is not in the command table."""
       
    18 class AmbiguousCommand(Exception):
       
    19     """Exception raised if command shortcut matches more than one command."""
    18 
    20 
    19 def filterfiles(filters, files):
    21 def filterfiles(filters, files):
    20     l = [x for x in files if x in filters]
    22     l = [x for x in files if x in filters]
    21 
    23 
    22     for t in filters:
    24     for t in filters:
    29     cwd = repo.getcwd()
    31     cwd = repo.getcwd()
    30     if cwd:
    32     if cwd:
    31         return [util.normpath(os.path.join(cwd, x)) for x in args]
    33         return [util.normpath(os.path.join(cwd, x)) for x in args]
    32     return args
    34     return args
    33 
    35 
    34 def matchpats(repo, cwd, pats=[], opts={}, head=''):
    36 def matchpats(repo, pats=[], opts={}, head=''):
       
    37     cwd = repo.getcwd()
       
    38     if not pats and cwd:
       
    39         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
       
    40         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
       
    41         cwd = ''
    35     return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
    42     return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
    36                         opts.get('exclude'), head)
    43                         opts.get('exclude'), head) + (cwd,)
    37 
    44 
    38 def makewalk(repo, pats, opts, head=''):
    45 def makewalk(repo, pats, opts, node=None, head=''):
    39     cwd = repo.getcwd()
    46     files, matchfn, anypats, cwd = matchpats(repo, pats, opts, head)
    40     files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
       
    41     exact = dict(zip(files, files))
    47     exact = dict(zip(files, files))
    42     def walk():
    48     def walk():
    43         for src, fn in repo.walk(files=files, match=matchfn):
    49         for src, fn in repo.walk(node=node, files=files, match=matchfn):
    44             yield src, fn, util.pathto(cwd, fn), fn in exact
    50             yield src, fn, util.pathto(cwd, fn), fn in exact
    45     return files, matchfn, walk()
    51     return files, matchfn, walk()
    46 
    52 
    47 def walk(repo, pats, opts, head=''):
    53 def walk(repo, pats, opts, node=None, head=''):
    48     files, matchfn, results = makewalk(repo, pats, opts, head)
    54     files, matchfn, results = makewalk(repo, pats, opts, node, head)
    49     for r in results:
    55     for r in results:
    50         yield r
    56         yield r
    51 
    57 
    52 def walkchangerevs(ui, repo, cwd, pats, opts):
    58 def walkchangerevs(ui, repo, pats, opts):
    53     '''Iterate over files and the revs they changed in.
    59     '''Iterate over files and the revs they changed in.
    54 
    60 
    55     Callers most commonly need to iterate backwards over the history
    61     Callers most commonly need to iterate backwards over the history
    56     it is interested in.  Doing so has awful (quadratic-looking)
    62     it is interested in.  Doing so has awful (quadratic-looking)
    57     performance, so we use iterators in a "windowed" way.
    63     performance, so we use iterators in a "windowed" way.
    77     over with "add" - use to display data'''
    83     over with "add" - use to display data'''
    78 
    84 
    79     if repo.changelog.count() == 0:
    85     if repo.changelog.count() == 0:
    80         return [], False
    86         return [], False
    81 
    87 
    82     cwd = repo.getcwd()
    88     files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
    83     if not pats and cwd:
       
    84         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
       
    85         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
       
    86     files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
       
    87                                         pats, opts)
       
    88     revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
    89     revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
    89     wanted = {}
    90     wanted = {}
    90     slowpath = anypats
    91     slowpath = anypats
    91     window = 300
    92     window = 300
    92     fncache = {}
    93     fncache = {}
   385     option_lists = []
   386     option_lists = []
   386     if cmd and cmd != 'shortlist':
   387     if cmd and cmd != 'shortlist':
   387         if with_version:
   388         if with_version:
   388             show_version(ui)
   389             show_version(ui)
   389             ui.write('\n')
   390             ui.write('\n')
   390         key, i = find(cmd)
   391         aliases, i = find(cmd)
   391         # synopsis
   392         # synopsis
   392         ui.write("%s\n\n" % i[2])
   393         ui.write("%s\n\n" % i[2])
   393 
   394 
   394         # description
   395         # description
   395         doc = i[0].__doc__
   396         doc = i[0].__doc__
   397             doc = doc.splitlines(0)[0]
   398             doc = doc.splitlines(0)[0]
   398         ui.write("%s\n" % doc.rstrip())
   399         ui.write("%s\n" % doc.rstrip())
   399 
   400 
   400         if not ui.quiet:
   401         if not ui.quiet:
   401             # aliases
   402             # aliases
   402             aliases = ', '.join(key.split('|')[1:])
   403             if len(aliases) > 1:
   403             if aliases:
   404                 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
   404                 ui.write(_("\naliases: %s\n") % aliases)
       
   405 
   405 
   406             # options
   406             # options
   407             if i[1]:
   407             if i[1]:
   408                 option_lists.append(("options", i[1]))
   408                 option_lists.append(("options", i[1]))
   409 
   409 
   480 
   480 
   481     Schedule files to be version controlled and added to the repository.
   481     Schedule files to be version controlled and added to the repository.
   482 
   482 
   483     The files will be added to the repository at the next commit.
   483     The files will be added to the repository at the next commit.
   484 
   484 
   485     If no names are given, add all files in the current directory and
   485     If no names are given, add all files in the repository.
   486     its subdirectories.
       
   487     """
   486     """
   488 
   487 
   489     names = []
   488     names = []
   490     for src, abs, rel, exact in walk(repo, pats, opts):
   489     for src, abs, rel, exact in walk(repo, pats, opts):
   491         if exact:
   490         if exact:
   535     ucache = {}
   534     ucache = {}
   536     def getname(rev):
   535     def getname(rev):
   537         cl = repo.changelog.read(repo.changelog.node(rev))
   536         cl = repo.changelog.read(repo.changelog.node(rev))
   538         return trimuser(ui, cl[1], rev, ucache)
   537         return trimuser(ui, cl[1], rev, ucache)
   539 
   538 
       
   539     dcache = {}
       
   540     def getdate(rev):
       
   541     	datestr = dcache.get(rev)
       
   542         if datestr is None:
       
   543             cl = repo.changelog.read(repo.changelog.node(rev))
       
   544             datestr = dcache[rev] = util.datestr(cl[2])
       
   545 	return datestr
       
   546 
   540     if not pats:
   547     if not pats:
   541         raise util.Abort(_('at least one file name or pattern required'))
   548         raise util.Abort(_('at least one file name or pattern required'))
   542 
   549 
   543     opmap = [['user', getname], ['number', str], ['changeset', getnode]]
   550     opmap = [['user', getname], ['number', str], ['changeset', getnode],
   544     if not opts['user'] and not opts['changeset']:
   551              ['date', getdate]]
       
   552     if not opts['user'] and not opts['changeset'] and not opts['date']:
   545         opts['number'] = 1
   553         opts['number'] = 1
   546 
   554 
   547     if opts['rev']:
   555     if opts['rev']:
   548         node = repo.changelog.lookup(opts['rev'])
   556         node = repo.changelog.lookup(opts['rev'])
   549     else:
   557     else:
   622     %s   basename of file being printed
   630     %s   basename of file being printed
   623     %d   dirname of file being printed, or '.' if in repo root
   631     %d   dirname of file being printed, or '.' if in repo root
   624     %p   root-relative path name of file being printed
   632     %p   root-relative path name of file being printed
   625     """
   633     """
   626     mf = {}
   634     mf = {}
   627     if opts['rev']:
   635     rev = opts['rev']
   628         change = repo.changelog.read(repo.lookup(opts['rev']))
   636     if rev:
   629         mf = repo.manifest.read(change[0])
   637         node = repo.lookup(rev)
   630     for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
   638     else:
       
   639         node = repo.changelog.tip()
       
   640     change = repo.changelog.read(node)
       
   641     mf = repo.manifest.read(change[0])
       
   642     for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
   631         r = repo.file(abs)
   643         r = repo.file(abs)
   632         if opts['rev']:
   644         n = mf[abs]
   633             try:
       
   634                 n = mf[abs]
       
   635             except (hg.RepoError, KeyError):
       
   636                 try:
       
   637                     n = r.lookup(rev)
       
   638                 except KeyError, inst:
       
   639                     raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
       
   640         else:
       
   641             n = r.tip()
       
   642         fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
   645         fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
   643         fp.write(r.read(n))
   646         fp.write(r.read(n))
   644 
   647 
   645 def clone(ui, source, dest=None, **opts):
   648 def clone(ui, source, dest=None, **opts):
   646     """make a copy of an existing repository
   649     """make a copy of an existing repository
   665     if os.path.exists(dest):
   668     if os.path.exists(dest):
   666         raise util.Abort(_("destination '%s' already exists"), dest)
   669         raise util.Abort(_("destination '%s' already exists"), dest)
   667 
   670 
   668     dest = os.path.realpath(dest)
   671     dest = os.path.realpath(dest)
   669 
   672 
   670     class Dircleanup:
   673     class Dircleanup(object):
   671         def __init__(self, dir_):
   674         def __init__(self, dir_):
   672             self.rmtree = shutil.rmtree
   675             self.rmtree = shutil.rmtree
   673             self.dir_ = dir_
   676             self.dir_ = dir_
   674             os.mkdir(dir_)
   677             os.mkdir(dir_)
   675         def close(self):
   678         def close(self):
   733         repo.pull(other, heads = revs)
   736         repo.pull(other, heads = revs)
   734 
   737 
   735     f = repo.opener("hgrc", "w", text=True)
   738     f = repo.opener("hgrc", "w", text=True)
   736     f.write("[paths]\n")
   739     f.write("[paths]\n")
   737     f.write("default = %s\n" % abspath)
   740     f.write("default = %s\n" % abspath)
       
   741     f.close()
   738 
   742 
   739     if not opts['noupdate']:
   743     if not opts['noupdate']:
   740         update(ui, repo)
   744         update(ui, repo)
   741 
   745 
   742     d.close()
   746     d.close()
   745     """commit the specified files or all outstanding changes
   749     """commit the specified files or all outstanding changes
   746 
   750 
   747     Commit changes to the given files into the repository.
   751     Commit changes to the given files into the repository.
   748 
   752 
   749     If a list of files is omitted, all changes reported by "hg status"
   753     If a list of files is omitted, all changes reported by "hg status"
   750     from the root of the repository will be commited.
   754     will be commited.
   751 
   755 
   752     The HGEDITOR or EDITOR environment variables are used to start an
   756     The HGEDITOR or EDITOR environment variables are used to start an
   753     editor to add a commit comment.
   757     editor to add a commit comment.
   754     """
   758     """
   755     message = opts['message']
   759     message = opts['message']
   768             raise util.Abort(_("can't read commit message '%s': %s") %
   772             raise util.Abort(_("can't read commit message '%s': %s") %
   769                              (logfile, inst.strerror))
   773                              (logfile, inst.strerror))
   770 
   774 
   771     if opts['addremove']:
   775     if opts['addremove']:
   772         addremove(ui, repo, *pats, **opts)
   776         addremove(ui, repo, *pats, **opts)
   773     cwd = repo.getcwd()
   777     fns, match, anypats, cwd = matchpats(repo, pats, opts)
   774     if not pats and cwd:
       
   775         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
       
   776         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
       
   777     fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
       
   778                                     pats, opts)
       
   779     if pats:
   778     if pats:
   780         c, a, d, u = repo.changes(files=fns, match=match)
   779         c, a, d, u = repo.changes(files=fns, match=match)
   781         files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
   780         files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
   782     else:
   781     else:
   783         files = []
   782         files = []
   785         repo.commit(files, message, opts['user'], opts['date'], match)
   784         repo.commit(files, message, opts['user'], opts['date'], match)
   786     except ValueError, inst:
   785     except ValueError, inst:
   787         raise util.Abort(str(inst))
   786         raise util.Abort(str(inst))
   788 
   787 
   789 def docopy(ui, repo, pats, opts):
   788 def docopy(ui, repo, pats, opts):
   790     if not pats:
   789     cwd = repo.getcwd()
   791         raise util.Abort(_('no source or destination specified'))
   790     errors = 0
   792     elif len(pats) == 1:
   791     copied = []
   793         raise util.Abort(_('no destination specified'))
   792     targets = {}
   794     pats = list(pats)
       
   795     dest = pats.pop()
       
   796     sources = []
       
   797     dir2dir = len(pats) == 1 and os.path.isdir(pats[0])
       
   798 
   793 
   799     def okaytocopy(abs, rel, exact):
   794     def okaytocopy(abs, rel, exact):
   800         reasons = {'?': _('is not managed'),
   795         reasons = {'?': _('is not managed'),
   801                    'a': _('has been marked for add')}
   796                    'a': _('has been marked for add')}
   802         reason = reasons.get(repo.dirstate.state(abs))
   797         reason = reasons.get(repo.dirstate.state(abs))
   803         if reason:
   798         if reason:
   804             if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
   799             if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
   805         else:
   800         else:
   806             return True
   801             return True
   807 
   802 
   808     for src, abs, rel, exact in walk(repo, pats, opts):
   803     def copy(abssrc, relsrc, target, exact):
   809         if okaytocopy(abs, rel, exact):
   804         abstarget = util.canonpath(repo.root, cwd, target)
   810             sources.append((abs, rel, exact))
   805         reltarget = util.pathto(cwd, abstarget)
   811     if not sources:
   806         prevsrc = targets.get(abstarget)
   812         raise util.Abort(_('no files to copy'))
   807         if prevsrc is not None:
   813 
   808             ui.warn(_('%s: not overwriting - %s collides with %s\n') %
   814     cwd = repo.getcwd()
   809                     (reltarget, abssrc, prevsrc))
   815     absdest = util.canonpath(repo.root, cwd, dest)
   810             return
   816     reldest = util.pathto(cwd, absdest)
   811         if (not opts['after'] and os.path.exists(reltarget) or
   817     if os.path.exists(reldest):
   812             opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
   818         destisfile = not os.path.isdir(reldest)
   813             if not opts['force']:
   819     else:
   814                 ui.warn(_('%s: not overwriting - file exists\n') %
   820         destisfile = not dir2dir and (len(sources) == 1
   815                         reltarget)
   821                                       or repo.dirstate.state(absdest) != '?')
   816                 return
   822 
   817             if not opts['after']:
   823     if destisfile and len(sources) > 1:
   818                 os.unlink(reltarget)
   824         raise util.Abort(_('with multiple sources, destination must be a '
   819         if opts['after']:
   825                            'directory'))
   820             if not os.path.exists(reltarget):
   826 
   821                 return
   827     srcpfxlen = 0
       
   828     if dir2dir:
       
   829         srcpfx = util.pathto(cwd, util.canonpath(repo.root, cwd, pats[0]))
       
   830         if os.path.exists(reldest):
       
   831             srcpfx = os.path.split(srcpfx)[0]
       
   832         if srcpfx:
       
   833             srcpfx += os.sep
       
   834         srcpfxlen = len(srcpfx)
       
   835 
       
   836     errs, copied = 0, []
       
   837     for abs, rel, exact in sources:
       
   838         if destisfile:
       
   839             mydest = reldest
       
   840         elif dir2dir:
       
   841             mydest = os.path.join(dest, rel[srcpfxlen:])
       
   842         else:
   822         else:
   843             mydest = os.path.join(dest, os.path.basename(rel))
   823             targetdir = os.path.dirname(reltarget) or '.'
   844         myabsdest = util.canonpath(repo.root, cwd, mydest)
   824             if not os.path.isdir(targetdir):
   845         myreldest = util.pathto(cwd, myabsdest)
   825                 os.makedirs(targetdir)
   846         if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
       
   847             ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
       
   848             continue
       
   849         mydestdir = os.path.dirname(myreldest) or '.'
       
   850         if not opts['after']:
       
   851             try:
   826             try:
   852                 if dir2dir: os.makedirs(mydestdir)
   827                 shutil.copyfile(relsrc, reltarget)
   853                 elif not destisfile: os.mkdir(mydestdir)
   828                 shutil.copymode(relsrc, reltarget)
   854             except OSError, inst:
       
   855                 if inst.errno != errno.EEXIST: raise
       
   856         if ui.verbose or not exact:
       
   857             ui.status(_('copying %s to %s\n') % (rel, myreldest))
       
   858         if not opts['after']:
       
   859             try:
       
   860                 shutil.copyfile(rel, myreldest)
       
   861                 shutil.copymode(rel, myreldest)
       
   862             except shutil.Error, inst:
   829             except shutil.Error, inst:
   863                 raise util.Abort(str(inst))
   830                 raise util.Abort(str(inst))
   864             except IOError, inst:
   831             except IOError, inst:
   865                 if inst.errno == errno.ENOENT:
   832                 if inst.errno == errno.ENOENT:
   866                     ui.warn(_('%s: deleted in working copy\n') % rel)
   833                     ui.warn(_('%s: deleted in working copy\n') % relsrc)
   867                 else:
   834                 else:
   868                     ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
   835                     ui.warn(_('%s: cannot copy - %s\n') %
   869                 errs += 1
   836                             (relsrc, inst.strerror))
   870                 continue
   837                     errors += 1
   871         repo.copy(abs, myabsdest)
   838                     return
   872         copied.append((abs, rel, exact))
   839         if ui.verbose or not exact:
   873     if errs:
   840             ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
       
   841         targets[abstarget] = abssrc
       
   842         repo.copy(abssrc, abstarget)
       
   843         copied.append((abssrc, relsrc, exact))
       
   844 
       
   845     def targetpathfn(pat, dest, srcs):
       
   846         if os.path.isdir(pat):
       
   847             if pat.endswith(os.sep):
       
   848                 pat = pat[:-len(os.sep)]
       
   849             if destdirexists:
       
   850                 striplen = len(os.path.split(pat)[0])
       
   851             else:
       
   852                 striplen = len(pat)
       
   853             if striplen:
       
   854                 striplen += len(os.sep)
       
   855             res = lambda p: os.path.join(dest, p[striplen:])
       
   856         elif destdirexists:
       
   857             res = lambda p: os.path.join(dest, os.path.basename(p))
       
   858         else:
       
   859             res = lambda p: dest
       
   860         return res
       
   861 
       
   862     def targetpathafterfn(pat, dest, srcs):
       
   863         if util.patkind(pat, None)[0]:
       
   864             # a mercurial pattern
       
   865             res = lambda p: os.path.join(dest, os.path.basename(p))
       
   866         elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
       
   867             # A directory. Either the target path contains the last
       
   868             # component of the source path or it does not.
       
   869             def evalpath(striplen):
       
   870                 score = 0
       
   871                 for s in srcs:
       
   872                     t = os.path.join(dest, s[1][striplen:])
       
   873                     if os.path.exists(t):
       
   874                         score += 1
       
   875                 return score
       
   876 
       
   877             if pat.endswith(os.sep):
       
   878                 pat = pat[:-len(os.sep)]
       
   879             striplen = len(pat) + len(os.sep)
       
   880             if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
       
   881                 score = evalpath(striplen)
       
   882                 striplen1 = len(os.path.split(pat)[0])
       
   883                 if striplen1:
       
   884                     striplen1 += len(os.sep)
       
   885                 if evalpath(striplen1) > score:
       
   886                     striplen = striplen1
       
   887             res = lambda p: os.path.join(dest, p[striplen:])
       
   888         else:
       
   889             # a file
       
   890             if destdirexists:
       
   891                 res = lambda p: os.path.join(dest, os.path.basename(p))
       
   892             else:
       
   893                 res = lambda p: dest
       
   894         return res
       
   895 
       
   896 
       
   897     pats = list(pats)
       
   898     if not pats:
       
   899         raise util.Abort(_('no source or destination specified'))
       
   900     if len(pats) == 1:
       
   901         raise util.Abort(_('no destination specified'))
       
   902     dest = pats.pop()
       
   903     destdirexists = os.path.isdir(dest)
       
   904     if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
       
   905         raise util.Abort(_('with multiple sources, destination must be an '
       
   906                          'existing directory'))
       
   907     if opts['after']:
       
   908         tfn = targetpathafterfn
       
   909     else:
       
   910         tfn = targetpathfn
       
   911     copylist = []
       
   912     for pat in pats:
       
   913         srcs = []
       
   914         for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
       
   915             if okaytocopy(abssrc, relsrc, exact):
       
   916                 srcs.append((abssrc, relsrc, exact))
       
   917         if not srcs:
       
   918             continue
       
   919         copylist.append((tfn(pat, dest, srcs), srcs))
       
   920     if not copylist:
       
   921         raise util.Abort(_('no files to copy'))
       
   922 
       
   923     for targetpath, srcs in copylist:
       
   924         for abssrc, relsrc, exact in srcs:
       
   925             copy(abssrc, relsrc, targetpath(relsrc), exact)
       
   926 
       
   927     if errors:
   874         ui.warn(_('(consider using --after)\n'))
   928         ui.warn(_('(consider using --after)\n'))
   875     return errs, copied
   929     return errors, copied
   876 
   930 
   877 def copy(ui, repo, *pats, **opts):
   931 def copy(ui, repo, *pats, **opts):
   878     """mark files as copied for the next commit
   932     """mark files as copied for the next commit
   879 
   933 
   880     Mark dest as having copies of source files.  If dest is a
   934     Mark dest as having copies of source files.  If dest is a
  1005             # assume all revision numbers are for changesets
  1059             # assume all revision numbers are for changesets
  1006             n = repo.lookup(rev)
  1060             n = repo.lookup(rev)
  1007             change = repo.changelog.read(n)
  1061             change = repo.changelog.read(n)
  1008             m = repo.manifest.read(change[0])
  1062             m = repo.manifest.read(change[0])
  1009             n = m[relpath(repo, [file])[0]]
  1063             n = m[relpath(repo, [file])[0]]
  1010         except hg.RepoError, KeyError:
  1064         except (hg.RepoError, KeyError):
  1011             n = r.lookup(rev)
  1065             n = r.lookup(rev)
  1012     else:
  1066     else:
  1013         n = r.tip()
  1067         n = r.tip()
  1014     m = r.renamed(n)
  1068     m = r.renamed(n)
  1015     if m:
  1069     if m:
  1028     for src, abs, rel, exact in items:
  1082     for src, abs, rel, exact in items:
  1029         line = fmt % (src, abs, rel, exact and 'exact' or '')
  1083         line = fmt % (src, abs, rel, exact and 'exact' or '')
  1030         ui.write("%s\n" % line.rstrip())
  1084         ui.write("%s\n" % line.rstrip())
  1031 
  1085 
  1032 def diff(ui, repo, *pats, **opts):
  1086 def diff(ui, repo, *pats, **opts):
  1033     """diff working directory (or selected files)
  1087     """diff repository (or selected files)
  1034 
  1088 
  1035     Show differences between revisions for the specified files.
  1089     Show differences between revisions for the specified files.
  1036 
  1090 
  1037     Differences between files are shown using the unified diff format.
  1091     Differences between files are shown using the unified diff format.
  1038 
  1092 
  1054     if len(revs) > 1:
  1108     if len(revs) > 1:
  1055         node2 = revs[1]
  1109         node2 = revs[1]
  1056     if len(revs) > 2:
  1110     if len(revs) > 2:
  1057         raise util.Abort(_("too many revisions to diff"))
  1111         raise util.Abort(_("too many revisions to diff"))
  1058 
  1112 
  1059     fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
  1113     fns, matchfn, anypats, cwd = matchpats(repo, pats, opts)
  1060 
  1114 
  1061     dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
  1115     dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
  1062            text=opts['text'])
  1116            text=opts['text'])
  1063 
  1117 
  1064 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
  1118 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
  1175             lstart = body.rfind('\n', begin, mstart) + 1 or begin
  1229             lstart = body.rfind('\n', begin, mstart) + 1 or begin
  1176             lend = body.find('\n', mend)
  1230             lend = body.find('\n', mend)
  1177             yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
  1231             yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
  1178             begin = lend + 1
  1232             begin = lend + 1
  1179 
  1233 
  1180     class linestate:
  1234     class linestate(object):
  1181         def __init__(self, line, linenum, colstart, colend):
  1235         def __init__(self, line, linenum, colstart, colend):
  1182             self.line = line
  1236             self.line = line
  1183             self.linenum = linenum
  1237             self.linenum = linenum
  1184             self.colstart = colstart
  1238             self.colstart = colstart
  1185             self.colend = colend
  1239             self.colend = colend
  1225             counts[change] += 1
  1279             counts[change] += 1
  1226         return counts['+'], counts['-']
  1280         return counts['+'], counts['-']
  1227 
  1281 
  1228     fstate = {}
  1282     fstate = {}
  1229     skip = {}
  1283     skip = {}
  1230     changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
  1284     changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
  1231     count = 0
  1285     count = 0
  1232     incrementing = False
  1286     incrementing = False
  1233     for st, rev, fns in changeiter:
  1287     for st, rev, fns in changeiter:
  1234         if st == 'window':
  1288         if st == 'window':
  1235             incrementing = rev
  1289             incrementing = rev
  1273 
  1327 
  1274     Repository "heads" are changesets that don't have children
  1328     Repository "heads" are changesets that don't have children
  1275     changesets. They are where development generally takes place and
  1329     changesets. They are where development generally takes place and
  1276     are the usual targets for update and merge operations.
  1330     are the usual targets for update and merge operations.
  1277     """
  1331     """
  1278     heads = repo.changelog.heads()
  1332     if opts['rev']:
       
  1333         heads = repo.heads(repo.lookup(opts['rev']))
       
  1334     else:
       
  1335         heads = repo.heads()
  1279     br = None
  1336     br = None
  1280     if opts['branches']:
  1337     if opts['branches']:
  1281         br = repo.branchlookup(heads)
  1338         br = repo.branchlookup(heads)
  1282     for n in repo.changelog.heads():
  1339     for n in heads:
  1283         show_changeset(ui, repo, changenode=n, brinfo=br)
  1340         show_changeset(ui, repo, changenode=n, brinfo=br)
  1284 
  1341 
  1285 def identify(ui, repo):
  1342 def identify(ui, repo):
  1286     """print information about the working copy
  1343     """print information about the working copy
  1287 
  1344 
  1459     """show revision history of entire repository or files
  1516     """show revision history of entire repository or files
  1460 
  1517 
  1461     Print the revision history of the specified files or the entire project.
  1518     Print the revision history of the specified files or the entire project.
  1462 
  1519 
  1463     By default this command outputs: changeset id and hash, tags,
  1520     By default this command outputs: changeset id and hash, tags,
  1464     parents, user, date and time, and a summary for each commit. The
  1521     non-trivial parents, user, date and time, and a summary for each
  1465     -v switch adds some more detail, such as changed files, manifest
  1522     commit. When the -v/--verbose switch is used, the list of changed
  1466     hashes or message signatures.
  1523     files and full commit message is shown.
  1467     """
  1524     """
  1468     class dui:
  1525     class dui(object):
  1469         # Implement and delegate some ui protocol.  Save hunks of
  1526         # Implement and delegate some ui protocol.  Save hunks of
  1470         # output for later display in the desired order.
  1527         # output for later display in the desired order.
  1471         def __init__(self, ui):
  1528         def __init__(self, ui):
  1472             self.ui = ui
  1529             self.ui = ui
  1473             self.hunk = {}
  1530             self.hunk = {}
  1485         def debug(self, *args):
  1542         def debug(self, *args):
  1486             if self.debugflag:
  1543             if self.debugflag:
  1487                 self.write(*args)
  1544                 self.write(*args)
  1488         def __getattr__(self, key):
  1545         def __getattr__(self, key):
  1489             return getattr(self.ui, key)
  1546             return getattr(self.ui, key)
  1490     cwd = repo.getcwd()
  1547     changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
  1491     if not pats and cwd:
       
  1492         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
       
  1493         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
       
  1494     changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
       
  1495                                            pats, opts)
       
  1496     for st, rev, fns in changeiter:
  1548     for st, rev, fns in changeiter:
  1497         if st == 'window':
  1549         if st == 'window':
  1498             du = dui(ui)
  1550             du = dui(ui)
  1499         elif st == 'add':
  1551         elif st == 'add':
  1500             du.bump(rev)
  1552             du.bump(rev)
  1731     Recover from an interrupted commit or pull.
  1783     Recover from an interrupted commit or pull.
  1732 
  1784 
  1733     This command tries to fix the repository status after an interrupted
  1785     This command tries to fix the repository status after an interrupted
  1734     operation. It should only be necessary when Mercurial suggests it.
  1786     operation. It should only be necessary when Mercurial suggests it.
  1735     """
  1787     """
  1736     repo.recover()
  1788     if repo.recover():
       
  1789         return repo.verify()
       
  1790     return False
  1737 
  1791 
  1738 def remove(ui, repo, pat, *pats, **opts):
  1792 def remove(ui, repo, pat, *pats, **opts):
  1739     """remove the specified files on the next commit
  1793     """remove the specified files on the next commit
  1740 
  1794 
  1741     Schedule the indicated files for removal from the repository.
  1795     Schedule the indicated files for removal from the repository.
  1797     If a file has been deleted, it is recreated.  If the executable
  1851     If a file has been deleted, it is recreated.  If the executable
  1798     mode of a file was changed, it is reset.
  1852     mode of a file was changed, it is reset.
  1799 
  1853 
  1800     If names are given, all files matching the names are reverted.
  1854     If names are given, all files matching the names are reverted.
  1801 
  1855 
  1802     If no names are given, all files in the current directory and
  1856     If no arguments are given, all files in the repository are reverted.
  1803     its subdirectories are reverted.
       
  1804     """
  1857     """
  1805     node = opts['rev'] and repo.lookup(opts['rev']) or \
  1858     node = opts['rev'] and repo.lookup(opts['rev']) or \
  1806            repo.dirstate.parents()[0]
  1859            repo.dirstate.parents()[0]
  1807 
  1860 
  1808     files, choose, anypats = matchpats(repo, repo.getcwd(), pats, opts)
  1861     files, choose, anypats, cwd = matchpats(repo, pats, opts)
  1809     (c, a, d, u) = repo.changes(match=choose)
  1862     (c, a, d, u) = repo.changes(match=choose)
  1810     repo.forget(a)
  1863     repo.forget(a)
  1811     repo.undelete(d)
  1864     repo.undelete(d)
  1812 
  1865 
  1813     return repo.update(node, False, True, choose, False)
  1866     return repo.update(node, False, True, choose, False)
  1926     httpd.serve_forever()
  1979     httpd.serve_forever()
  1927 
  1980 
  1928 def status(ui, repo, *pats, **opts):
  1981 def status(ui, repo, *pats, **opts):
  1929     """show changed files in the working directory
  1982     """show changed files in the working directory
  1930 
  1983 
  1931     Show changed files in the working directory.  If no names are
  1984     Show changed files in the repository.  If names are
  1932     given, all files are shown.  Otherwise, only files matching the
  1985     given, only files that match are shown.
  1933     given names are shown.
       
  1934 
  1986 
  1935     The codes used to show the status of files are:
  1987     The codes used to show the status of files are:
  1936     M = modified
  1988     M = modified
  1937     A = added
  1989     A = added
  1938     R = removed
  1990     R = removed
  1939     ? = not tracked
  1991     ? = not tracked
  1940     """
  1992     """
  1941 
  1993 
  1942     cwd = repo.getcwd()
  1994     files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
  1943     files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
       
  1944     (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
  1995     (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
  1945                     for n in repo.changes(files=files, match=matchfn)]
  1996                     for n in repo.changes(files=files, match=matchfn)]
  1946 
  1997 
  1947     changetypes = [(_('modified'), 'M', c),
  1998     changetypes = [(_('modified'), 'M', c),
  1948                    (_('added'), 'A', a),
  1999                    (_('added'), 'A', a),
  1984     if rev:
  2035     if rev:
  1985         r = hex(repo.lookup(rev))
  2036         r = hex(repo.lookup(rev))
  1986     else:
  2037     else:
  1987         r = hex(repo.changelog.tip())
  2038         r = hex(repo.changelog.tip())
  1988 
  2039 
  1989     if name.find(revrangesep) >= 0:
  2040     disallowed = (revrangesep, '\r', '\n')
  1990         raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
  2041     for c in disallowed:
       
  2042         if name.find(c) >= 0:
       
  2043             raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
  1991 
  2044 
  1992     if opts['local']:
  2045     if opts['local']:
  1993         repo.opener("localtags", "a").write("%s %s\n" % (r, name))
  2046         repo.opener("localtags", "a").write("%s %s\n" % (r, name))
  1994         return
  2047         return
  1995 
  2048 
  2136     "^annotate":
  2189     "^annotate":
  2137         (annotate,
  2190         (annotate,
  2138          [('r', 'rev', '', _('annotate the specified revision')),
  2191          [('r', 'rev', '', _('annotate the specified revision')),
  2139           ('a', 'text', None, _('treat all files as text')),
  2192           ('a', 'text', None, _('treat all files as text')),
  2140           ('u', 'user', None, _('list the author')),
  2193           ('u', 'user', None, _('list the author')),
       
  2194           ('d', 'date', None, _('list the date')),
  2141           ('n', 'number', None, _('list the revision number (default)')),
  2195           ('n', 'number', None, _('list the revision number (default)')),
  2142           ('c', 'changeset', None, _('list the changeset')),
  2196           ('c', 'changeset', None, _('list the changeset')),
  2143           ('I', 'include', [], _('include names matching the given patterns')),
  2197           ('I', 'include', [], _('include names matching the given patterns')),
  2144           ('X', 'exclude', [], _('exclude names matching the given patterns'))],
  2198           ('X', 'exclude', [], _('exclude names matching the given patterns'))],
  2145          _('hg annotate [OPTION]... FILE...')),
  2199          _('hg annotate [OPTION]... FILE...')),
  2221           ('r', 'rev', [], _('search in given revision range')),
  2275           ('r', 'rev', [], _('search in given revision range')),
  2222           ('u', 'user', None, _('print user who committed change'))],
  2276           ('u', 'user', None, _('print user who committed change'))],
  2223          "hg grep [OPTION]... PATTERN [FILE]..."),
  2277          "hg grep [OPTION]... PATTERN [FILE]..."),
  2224     "heads":
  2278     "heads":
  2225         (heads,
  2279         (heads,
  2226          [('b', 'branches', None, _('find branch info'))],
  2280          [('b', 'branches', None, _('find branch info')),
  2227          _('hg heads [-b]')),
  2281           ('r', 'rev', "", _('show only heads which are descendants of rev'))],
       
  2282          _('hg heads [-b] [-r <rev>]')),
  2228     "help": (help_, [], _('hg help [COMMAND]')),
  2283     "help": (help_, [], _('hg help [COMMAND]')),
  2229     "identify|id": (identify, [], _('hg identify')),
  2284     "identify|id": (identify, [], _('hg identify')),
  2230     "import|patch":
  2285     "import|patch":
  2231         (import_,
  2286         (import_,
  2232          [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
  2287          [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
  2372 
  2427 
  2373 norepo = ("clone init version help debugancestor debugconfig debugdata"
  2428 norepo = ("clone init version help debugancestor debugconfig debugdata"
  2374           " debugindex debugindexdot paths")
  2429           " debugindex debugindexdot paths")
  2375 
  2430 
  2376 def find(cmd):
  2431 def find(cmd):
  2377     choice = []
  2432     """Return (aliases, command table entry) for command string."""
       
  2433     choice = None
  2378     for e in table.keys():
  2434     for e in table.keys():
  2379         aliases = e.lstrip("^").split("|")
  2435         aliases = e.lstrip("^").split("|")
  2380         if cmd in aliases:
  2436         if cmd in aliases:
  2381             return e, table[e]
  2437             return aliases, table[e]
  2382         for a in aliases:
  2438         for a in aliases:
  2383             if a.startswith(cmd):
  2439             if a.startswith(cmd):
  2384                 choice.append(e)
  2440                 if choice:
  2385     if len(choice) == 1:
  2441                     raise AmbiguousCommand(cmd)
  2386         e = choice[0]
  2442                 else:
  2387         return e, table[e]
  2443                     choice = aliases, table[e]
       
  2444                     break
       
  2445     if choice:
       
  2446         return choice
  2388 
  2447 
  2389     raise UnknownCommand(cmd)
  2448     raise UnknownCommand(cmd)
  2390 
  2449 
  2391 class SignalInterrupt(Exception):
  2450 class SignalInterrupt(Exception):
  2392     """Exception raised on SIGTERM and SIGHUP."""
  2451     """Exception raised on SIGTERM and SIGHUP."""
  2409     except fancyopts.getopt.GetoptError, inst:
  2468     except fancyopts.getopt.GetoptError, inst:
  2410         raise ParseError(None, inst)
  2469         raise ParseError(None, inst)
  2411 
  2470 
  2412     if args:
  2471     if args:
  2413         cmd, args = args[0], args[1:]
  2472         cmd, args = args[0], args[1:]
       
  2473         aliases, i = find(cmd)
       
  2474         cmd = aliases[0]
  2414         defaults = ui.config("defaults", cmd)
  2475         defaults = ui.config("defaults", cmd)
  2415         if defaults:
  2476         if defaults:
  2416             # reparse with command defaults added
  2477             args = defaults.split() + args
  2417             args = [cmd] + defaults.split() + args
       
  2418             try:
       
  2419                 args = fancyopts.fancyopts(args, globalopts, options)
       
  2420             except fancyopts.getopt.GetoptError, inst:
       
  2421                 raise ParseError(None, inst)
       
  2422 
       
  2423             cmd, args = args[0], args[1:]
       
  2424 
       
  2425         i = find(cmd)[1]
       
  2426         c = list(i[1])
  2478         c = list(i[1])
  2427     else:
  2479     else:
  2428         cmd = None
  2480         cmd = None
  2429         c = []
  2481         c = []
  2430 
  2482 
  2458         sys.stderr.write(_("abort: %s\n") % inst)
  2510         sys.stderr.write(_("abort: %s\n") % inst)
  2459         sys.exit(1)
  2511         sys.exit(1)
  2460 
  2512 
  2461     external = []
  2513     external = []
  2462     for x in u.extensions():
  2514     for x in u.extensions():
  2463         def on_exception(Exception, inst):
  2515         def on_exception(exc, inst):
  2464             u.warn(_("*** failed to import extension %s\n") % x[1])
  2516             u.warn(_("*** failed to import extension %s\n") % x[1])
  2465             u.warn("%s\n" % inst)
  2517             u.warn("%s\n" % inst)
  2466             if "--traceback" in sys.argv[1:]:
  2518             if "--traceback" in sys.argv[1:]:
  2467                 traceback.print_exc()
  2519                 traceback.print_exc()
  2468         if x[1]:
  2520         if x[1]:
  2500             help_(u, inst.args[0])
  2552             help_(u, inst.args[0])
  2501         else:
  2553         else:
  2502             u.warn(_("hg: %s\n") % inst.args[1])
  2554             u.warn(_("hg: %s\n") % inst.args[1])
  2503             help_(u, 'shortlist')
  2555             help_(u, 'shortlist')
  2504         sys.exit(-1)
  2556         sys.exit(-1)
       
  2557     except AmbiguousCommand, inst:
       
  2558         u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
       
  2559         sys.exit(1)
  2505     except UnknownCommand, inst:
  2560     except UnknownCommand, inst:
  2506         u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
  2561         u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
  2507         help_(u, 'shortlist')
  2562         help_(u, 'shortlist')
  2508         sys.exit(1)
  2563         sys.exit(1)
  2509 
  2564 
  2618         if len(tb) > 2: # no
  2673         if len(tb) > 2: # no
  2619             raise
  2674             raise
  2620         u.debug(inst, "\n")
  2675         u.debug(inst, "\n")
  2621         u.warn(_("%s: invalid arguments\n") % cmd)
  2676         u.warn(_("%s: invalid arguments\n") % cmd)
  2622         help_(u, cmd)
  2677         help_(u, cmd)
       
  2678     except AmbiguousCommand, inst:
       
  2679         u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
       
  2680         help_(u, 'shortlist')
  2623     except UnknownCommand, inst:
  2681     except UnknownCommand, inst:
  2624         u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
  2682         u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
  2625         help_(u, 'shortlist')
  2683         help_(u, 'shortlist')
  2626     except SystemExit:
  2684     except SystemExit:
  2627         # don't catch this in the catch-all below
  2685         # don't catch this in the catch-all below
  2628         raise
  2686         raise
  2629     except:
  2687     except:
  2630         u.warn(_("** unknown exception encountered, details follow\n"))
  2688         u.warn(_("** unknown exception encountered, details follow\n"))
  2631         u.warn(_("** report bug details to mercurial@selenic.com\n"))
  2689         u.warn(_("** report bug details to mercurial@selenic.com\n"))
       
  2690         u.warn(_("** Mercurial Distributed SCM (version %s)\n")
       
  2691                % version.get_version())
  2632         raise
  2692         raise
  2633 
  2693 
  2634     sys.exit(-1)
  2694     sys.exit(-1)