mercurial/commands.py
changeset 896 01215ad04283
parent 852 1df0983eb589
parent 895 77b52b864249
child 898 3616c0d7ab88
equal deleted inserted replaced
867:0cd2ee61b10a 896:01215ad04283
    12 demandload(globals(), "errno socket version struct atexit")
    12 demandload(globals(), "errno socket version struct atexit")
    13 
    13 
    14 class UnknownCommand(Exception):
    14 class UnknownCommand(Exception):
    15     """Exception raised if command is not in the command table."""
    15     """Exception raised if command is not in the command table."""
    16 
    16 
    17 class Abort(Exception):
       
    18     """Raised if a command needs to print an error and exit."""
       
    19 
       
    20 def filterfiles(filters, files):
    17 def filterfiles(filters, files):
    21     l = [x for x in files if x in filters]
    18     l = [x for x in files if x in filters]
    22 
    19 
    23     for t in filters:
    20     for t in filters:
    24         if t and t[-1] != "/":
    21         if t and t[-1] != "/":
    33     return files
    30     return files
    34 
    31 
    35 def relpath(repo, args):
    32 def relpath(repo, args):
    36     cwd = repo.getcwd()
    33     cwd = repo.getcwd()
    37     if cwd:
    34     if cwd:
    38         return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
    35         return [util.normpath(os.path.join(cwd, x)) for x in args]
    39                 for x in args]
       
    40     return args
    36     return args
    41 
    37 
    42 def matchpats(cwd, pats = [], opts = {}, head = ''):
    38 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
    43     return util.matcher(cwd, pats or ['.'], opts.get('include'),
    39     return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
    44                         opts.get('exclude'), head)
    40                         opts.get('exclude'), head)
    45 
       
    46 def pathto(n1, n2):
       
    47     '''return the relative path from one place to another'''
       
    48     if not n1: return n2
       
    49     a, b = n1.split(os.sep), n2.split(os.sep)
       
    50     a.reverse(), b.reverse()
       
    51     while a and b and a[-1] == b[-1]:
       
    52         a.pop(), b.pop()
       
    53     b.reverse()
       
    54     return os.sep.join((['..'] * len(a)) + b)
       
    55 
    41 
    56 def makewalk(repo, pats, opts, head = ''):
    42 def makewalk(repo, pats, opts, head = ''):
    57     cwd = repo.getcwd()
    43     cwd = repo.getcwd()
    58     files, matchfn = matchpats(cwd, pats, opts, head)
    44     files, matchfn = matchpats(repo, cwd, pats, opts, head)
    59     def walk():
    45     def walk():
    60         for src, fn in repo.walk(files = files, match = matchfn):
    46         for src, fn in repo.walk(files = files, match = matchfn):
    61             yield src, fn, pathto(cwd, fn)
    47             yield src, fn, util.pathto(cwd, fn)
    62     return files, matchfn, walk()
    48     return files, matchfn, walk()
    63 
    49 
    64 def walk(repo, pats, opts, head = ''):
    50 def walk(repo, pats, opts, head = ''):
    65     files, matchfn, results = makewalk(repo, pats, opts, head)
    51     files, matchfn, results = makewalk(repo, pats, opts, head)
    66     for r in results: yield r
    52     for r in results: yield r
    87                 num = repo.changelog.rev(repo.lookup(val))
    73                 num = repo.changelog.rev(repo.lookup(val))
    88             except KeyError:
    74             except KeyError:
    89                 try:
    75                 try:
    90                     num = revlog.rev(revlog.lookup(val))
    76                     num = revlog.rev(revlog.lookup(val))
    91                 except KeyError:
    77                 except KeyError:
    92                     raise Abort('invalid revision identifier %s', val)
    78                     raise util.Abort('invalid revision identifier %s', val)
    93         return num
    79         return num
    94     for spec in revs:
    80     for spec in revs:
    95         if spec.find(revrangesep) >= 0:
    81         if spec.find(revrangesep) >= 0:
    96             start, end = spec.split(revrangesep, 1)
    82             start, end = spec.split(revrangesep, 1)
    97             start = fix(start, 0)
    83             start = fix(start, 0)
   142                 c = expander[c]()
   128                 c = expander[c]()
   143             newname.append(c)
   129             newname.append(c)
   144             i += 1
   130             i += 1
   145         return ''.join(newname)
   131         return ''.join(newname)
   146     except KeyError, inst:
   132     except KeyError, inst:
   147         raise Abort("invalid format spec '%%%s' in output file name",
   133         raise util.Abort("invalid format spec '%%%s' in output file name",
   148                     inst.args[0])
   134                     inst.args[0])
   149 
   135 
   150 def make_file(repo, r, pat, node=None,
   136 def make_file(repo, r, pat, node=None,
   151               total=None, seqno=None, revwidth=None, mode='wb'):
   137               total=None, seqno=None, revwidth=None, mode='wb'):
   152     if not pat or pat == '-':
   138     if not pat or pat == '-':
   394 def addremove(ui, repo, *pats, **opts):
   380 def addremove(ui, repo, *pats, **opts):
   395     """add all new files, delete all missing files"""
   381     """add all new files, delete all missing files"""
   396     q = dict(zip(pats, pats))
   382     q = dict(zip(pats, pats))
   397     add, remove = [], []
   383     add, remove = [], []
   398     for src, abs, rel in walk(repo, pats, opts):
   384     for src, abs, rel in walk(repo, pats, opts):
   399         if src == 'f':
   385         if src == 'f' and repo.dirstate.state(abs) == '?':
   400             if repo.dirstate.state(abs) == '?':
   386             add.append(abs)
   401                 add.append(abs)
   387             if rel not in q: ui.status('adding ', rel, '\n')
   402                 if rel not in q: ui.status('adding ', rel, '\n')
   388         if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
   403         elif repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
       
   404             remove.append(abs)
   389             remove.append(abs)
   405             if rel not in q: ui.status('removing ', rel, '\n')
   390             if rel not in q: ui.status('removing ', rel, '\n')
   406     repo.add(add)
   391     repo.add(add)
   407     repo.remove(remove)
   392     repo.remove(remove)
   408 
   393 
   425                 name = name[f+1:]
   410                 name = name[f+1:]
   426             bcache[rev] = name
   411             bcache[rev] = name
   427             return name
   412             return name
   428 
   413 
   429     if not pats:
   414     if not pats:
   430         raise Abort('at least one file name or pattern required')
   415         raise util.Abort('at least one file name or pattern required')
   431 
   416 
   432     bcache = {}
   417     bcache = {}
   433     opmap = [['user', getname], ['number', str], ['changeset', getnode]]
   418     opmap = [['user', getname], ['number', str], ['changeset', getnode]]
   434     if not opts['user'] and not opts['changeset']:
   419     if not opts['user'] and not opts['changeset']:
   435         opts['number'] = 1
   420         opts['number'] = 1
   475         dest = os.path.basename(os.path.normpath(source))
   460         dest = os.path.basename(os.path.normpath(source))
   476 
   461 
   477     if os.path.exists(dest):
   462     if os.path.exists(dest):
   478         ui.warn("abort: destination '%s' already exists\n" % dest)
   463         ui.warn("abort: destination '%s' already exists\n" % dest)
   479         return 1
   464         return 1
       
   465 
       
   466     dest = os.path.realpath(dest)
   480 
   467 
   481     class Dircleanup:
   468     class Dircleanup:
   482         def __init__(self, dir_):
   469         def __init__(self, dir_):
   483             self.rmtree = shutil.rmtree
   470             self.rmtree = shutil.rmtree
   484             self.dir_ = dir_
   471             self.dir_ = dir_
   539         addremove(ui, repo, *pats, **opts)
   526         addremove(ui, repo, *pats, **opts)
   540     cwd = repo.getcwd()
   527     cwd = repo.getcwd()
   541     if not pats and cwd:
   528     if not pats and cwd:
   542         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
   529         opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
   543         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
   530         opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
   544     fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts)
   531     fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts)
   545     if pats:
   532     if pats:
   546         c, a, d, u = repo.changes(files = fns, match = match)
   533         c, a, d, u = repo.changes(files = fns, match = match)
   547         files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
   534         files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
   548     else:
   535     else:
   549         files = []
   536         files = []
   581         state = repo.dirstate.state(f)
   568         state = repo.dirstate.state(f)
   582         if state not in "nrm":
   569         if state not in "nrm":
   583             ui.warn("%s in manifest1, but listed as state %s" % (f, state))
   570             ui.warn("%s in manifest1, but listed as state %s" % (f, state))
   584             errors += 1
   571             errors += 1
   585     if errors:
   572     if errors:
   586         raise Abort(".hg/dirstate inconsistent with current parent's manifest")
   573         raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
   587 
   574 
   588 def debugstate(ui, repo):
   575 def debugstate(ui, repo):
   589     """show the contents of the current dirstate"""
   576     """show the contents of the current dirstate"""
   590     repo.dirstate.read()
   577     repo.dirstate.read()
   591     dc = repo.dirstate.map
   578     dc = repo.dirstate.map
   619             ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
   606             ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
   620     ui.write("}\n")
   607     ui.write("}\n")
   621 
   608 
   622 def debugwalk(ui, repo, *pats, **opts):
   609 def debugwalk(ui, repo, *pats, **opts):
   623     items = list(walk(repo, pats, opts))
   610     items = list(walk(repo, pats, opts))
       
   611     if not items: return
   624     fmt = '%%s  %%-%ds  %%s' % max([len(abs) for (src, abs, rel) in items])
   612     fmt = '%%s  %%-%ds  %%s' % max([len(abs) for (src, abs, rel) in items])
   625     for i in items: print fmt % i
   613     for i in items: print fmt % i
   626 
   614 
   627 def diff(ui, repo, *pats, **opts):
   615 def diff(ui, repo, *pats, **opts):
   628     """diff working directory (or selected files)"""
   616     """diff working directory (or selected files)"""
   629     revs = []
   617     revs = []
   630     if opts['rev']:
   618     if opts['rev']:
   631         revs = map(lambda x: repo.lookup(x), opts['rev'])
   619         revs = map(lambda x: repo.lookup(x), opts['rev'])
   632 
   620 
   633     if len(revs) > 2:
   621     if len(revs) > 2:
   634         raise Abort("too many revisions to diff")
   622         raise util.Abort("too many revisions to diff")
   635 
   623 
   636     files = []
   624     files = []
   637     roots, match, results = makewalk(repo, pats, opts)
   625     match = util.always
   638     for src, abs, rel in results:
   626     if pats:
   639         files.append(abs)
   627         roots, match, results = makewalk(repo, pats, opts)
       
   628         for src, abs, rel in results:
       
   629             files.append(abs)
   640     dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
   630     dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
   641 
   631 
   642 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
   632 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
   643     node = repo.lookup(changeset)
   633     node = repo.lookup(changeset)
   644     prev, other = repo.changelog.parents(node)
   634     prev, other = repo.changelog.parents(node)
   663     if fp != sys.stdout: fp.close()
   653     if fp != sys.stdout: fp.close()
   664 
   654 
   665 def export(ui, repo, *changesets, **opts):
   655 def export(ui, repo, *changesets, **opts):
   666     """dump the header and diffs for one or more changesets"""
   656     """dump the header and diffs for one or more changesets"""
   667     if not changesets:
   657     if not changesets:
   668         raise Abort("export requires at least one changeset")
   658         raise util.Abort("export requires at least one changeset")
   669     seqno = 0
   659     seqno = 0
   670     revs = list(revrange(ui, repo, changesets))
   660     revs = list(revrange(ui, repo, changesets))
   671     total = len(revs)
   661     total = len(revs)
   672     revwidth = max(len(revs[0]), len(revs[-1]))
   662     revwidth = max(len(revs[0]), len(revs[-1]))
   673     ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
   663     ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
   760                 pf = l[14:]
   750                 pf = l[14:]
   761                 if pf not in files:
   751                 if pf not in files:
   762                     files.append(pf)
   752                     files.append(pf)
   763         patcherr = f.close()
   753         patcherr = f.close()
   764         if patcherr:
   754         if patcherr:
   765             raise Abort("patch failed")
   755             raise util.Abort("patch failed")
   766 
   756 
   767         if len(files) > 0:
   757         if len(files) > 0:
   768             addremove(ui, repo, *files)
   758             addremove(ui, repo, *files)
   769         repo.commit(files, message, user)
   759         repo.commit(files, message, user)
   770 
   760 
   771 def init(ui, source=None):
   761 def init(ui, source=None):
   772     """create a new repository in the current directory"""
   762     """create a new repository in the current directory"""
   773 
   763 
   774     if source:
   764     if source:
   775         raise Abort("no longer supported: use \"hg clone\" instead")
   765         raise util.Abort("no longer supported: use \"hg clone\" instead")
   776     hg.repository(ui, ".", create=1)
   766     hg.repository(ui, ".", create=1)
   777 
   767 
   778 def locate(ui, repo, *pats, **opts):
   768 def locate(ui, repo, *pats, **opts):
   779     """locate files matching specific patterns"""
   769     """locate files matching specific patterns"""
   780     end = '\n'
   770     end = '\n'
  1076     R = removed
  1066     R = removed
  1077     ? = not tracked
  1067     ? = not tracked
  1078     '''
  1068     '''
  1079 
  1069 
  1080     cwd = repo.getcwd()
  1070     cwd = repo.getcwd()
  1081     files, matchfn = matchpats(cwd, pats, opts)
  1071     files, matchfn = matchpats(repo, cwd, pats, opts)
  1082     (c, a, d, u) = [[pathto(cwd, x) for x in n]
  1072     (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
  1083                     for n in repo.changes(files=files, match=matchfn)]
  1073                     for n in repo.changes(files=files, match=matchfn)]
  1084 
  1074 
  1085     changetypes = [('modified', 'M', c),
  1075     changetypes = [('modified', 'M', c),
  1086                    ('added', 'A', a),
  1076                    ('added', 'A', a),
  1087                    ('removed', 'R', d),
  1077                    ('removed', 'R', d),
  1469                 return d()
  1459                 return d()
  1470         except:
  1460         except:
  1471             if options['traceback']:
  1461             if options['traceback']:
  1472                 traceback.print_exc()
  1462                 traceback.print_exc()
  1473             raise
  1463             raise
  1474     except util.CommandError, inst:
       
  1475         u.warn("abort: %s\n" % inst.args)
       
  1476     except hg.RepoError, inst:
  1464     except hg.RepoError, inst:
  1477         u.warn("abort: ", inst, "!\n")
  1465         u.warn("abort: ", inst, "!\n")
  1478     except SignalInterrupt:
  1466     except SignalInterrupt:
  1479         u.warn("killed!\n")
  1467         u.warn("killed!\n")
  1480     except KeyboardInterrupt:
  1468     except KeyboardInterrupt:
  1498     except OSError, inst:
  1486     except OSError, inst:
  1499         if hasattr(inst, "filename"):
  1487         if hasattr(inst, "filename"):
  1500             u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
  1488             u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
  1501         else:
  1489         else:
  1502             u.warn("abort: %s\n" % inst.strerror)
  1490             u.warn("abort: %s\n" % inst.strerror)
  1503     except Abort, inst:
  1491     except util.Abort, inst:
  1504         u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
  1492         u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
  1505         sys.exit(1)
  1493         sys.exit(1)
  1506     except TypeError, inst:
  1494     except TypeError, inst:
  1507         # was this an argument error?
  1495         # was this an argument error?
  1508         tb = traceback.extract_tb(sys.exc_info()[2])
  1496         tb = traceback.extract_tb(sys.exc_info()[2])