Mercurial > public > mercurial-scm > hg
comparison mercurial/commands.py @ 870:a82eae840447
Teach walk code about absolute paths.
The first consequence of this is that absolute and relative paths now
all work in the same way. The second is that paths that lie outside
the repository now cause an error to be reported, instead of something
arbitrary and expensive being done.
Internally, all of the serious work is in the util package. The new
canonpath function takes an arbitrary path and either returns a
canonical path or raises an error. Because it needs to know where the
repository root is, it must be fed a repository or dirstate object, which
has given commands.matchpats and friends a new parameter to pass along.
The util.matcher function uses this to canonicalise globs and relative
path names.
Meanwhile, I've moved the Abort exception from commands to util, and
killed off the redundant util.CommandError exception.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sun, 07 Aug 2005 12:43:11 -0800 |
parents | 6a8a50bcc143 |
children | c2e77581bc84 |
comparison
equal
deleted
inserted
replaced
869:1e3a23719662 | 870:a82eae840447 |
---|---|
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] != "/": |
37 if cwd: | 34 if cwd: |
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x))) | 35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x))) |
39 for x in args] | 36 for x in args] |
40 return args | 37 return args |
41 | 38 |
42 def matchpats(cwd, pats = [], opts = {}, head = ''): | 39 def matchpats(repo, cwd, pats = [], opts = {}, head = ''): |
43 return util.matcher(cwd, pats or ['.'], opts.get('include'), | 40 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'), |
44 opts.get('exclude'), head) | 41 opts.get('exclude'), head) |
45 | 42 |
46 def pathto(n1, n2): | 43 def pathto(n1, n2): |
47 '''return the relative path from one place to another''' | 44 '''return the relative path from one place to another''' |
48 if not n1: return n2 | 45 if not n1: return n2 |
53 b.reverse() | 50 b.reverse() |
54 return os.sep.join((['..'] * len(a)) + b) | 51 return os.sep.join((['..'] * len(a)) + b) |
55 | 52 |
56 def makewalk(repo, pats, opts, head = ''): | 53 def makewalk(repo, pats, opts, head = ''): |
57 cwd = repo.getcwd() | 54 cwd = repo.getcwd() |
58 files, matchfn = matchpats(cwd, pats, opts, head) | 55 files, matchfn = matchpats(repo, cwd, pats, opts, head) |
59 def walk(): | 56 def walk(): |
60 for src, fn in repo.walk(files = files, match = matchfn): | 57 for src, fn in repo.walk(files = files, match = matchfn): |
61 yield src, fn, pathto(cwd, fn) | 58 yield src, fn, pathto(cwd, fn) |
62 return files, matchfn, walk() | 59 return files, matchfn, walk() |
63 | 60 |
87 num = repo.changelog.rev(repo.lookup(val)) | 84 num = repo.changelog.rev(repo.lookup(val)) |
88 except KeyError: | 85 except KeyError: |
89 try: | 86 try: |
90 num = revlog.rev(revlog.lookup(val)) | 87 num = revlog.rev(revlog.lookup(val)) |
91 except KeyError: | 88 except KeyError: |
92 raise Abort('invalid revision identifier %s', val) | 89 raise util.Abort('invalid revision identifier %s', val) |
93 return num | 90 return num |
94 for spec in revs: | 91 for spec in revs: |
95 if spec.find(revrangesep) >= 0: | 92 if spec.find(revrangesep) >= 0: |
96 start, end = spec.split(revrangesep, 1) | 93 start, end = spec.split(revrangesep, 1) |
97 start = fix(start, 0) | 94 start = fix(start, 0) |
142 c = expander[c]() | 139 c = expander[c]() |
143 newname.append(c) | 140 newname.append(c) |
144 i += 1 | 141 i += 1 |
145 return ''.join(newname) | 142 return ''.join(newname) |
146 except KeyError, inst: | 143 except KeyError, inst: |
147 raise Abort("invalid format spec '%%%s' in output file name", | 144 raise util.Abort("invalid format spec '%%%s' in output file name", |
148 inst.args[0]) | 145 inst.args[0]) |
149 | 146 |
150 def make_file(repo, r, pat, node=None, | 147 def make_file(repo, r, pat, node=None, |
151 total=None, seqno=None, revwidth=None, mode='wb'): | 148 total=None, seqno=None, revwidth=None, mode='wb'): |
152 if not pat or pat == '-': | 149 if not pat or pat == '-': |
385 name = name[f+1:] | 382 name = name[f+1:] |
386 bcache[rev] = name | 383 bcache[rev] = name |
387 return name | 384 return name |
388 | 385 |
389 if not pats: | 386 if not pats: |
390 raise Abort('at least one file name or pattern required') | 387 raise util.Abort('at least one file name or pattern required') |
391 | 388 |
392 bcache = {} | 389 bcache = {} |
393 opmap = [['user', getname], ['number', str], ['changeset', getnode]] | 390 opmap = [['user', getname], ['number', str], ['changeset', getnode]] |
394 if not opts['user'] and not opts['changeset']: | 391 if not opts['user'] and not opts['changeset']: |
395 opts['number'] = 1 | 392 opts['number'] = 1 |
499 addremove(ui, repo, *pats, **opts) | 496 addremove(ui, repo, *pats, **opts) |
500 cwd = repo.getcwd() | 497 cwd = repo.getcwd() |
501 if not pats and cwd: | 498 if not pats and cwd: |
502 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | 499 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] |
503 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | 500 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] |
504 fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts) | 501 fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts) |
505 if pats: | 502 if pats: |
506 c, a, d, u = repo.changes(files = fns, match = match) | 503 c, a, d, u = repo.changes(files = fns, match = match) |
507 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r'] | 504 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r'] |
508 else: | 505 else: |
509 files = [] | 506 files = [] |
541 state = repo.dirstate.state(f) | 538 state = repo.dirstate.state(f) |
542 if state not in "nrm": | 539 if state not in "nrm": |
543 ui.warn("%s in manifest1, but listed as state %s" % (f, state)) | 540 ui.warn("%s in manifest1, but listed as state %s" % (f, state)) |
544 errors += 1 | 541 errors += 1 |
545 if errors: | 542 if errors: |
546 raise Abort(".hg/dirstate inconsistent with current parent's manifest") | 543 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest") |
547 | 544 |
548 def debugstate(ui, repo): | 545 def debugstate(ui, repo): |
549 """show the contents of the current dirstate""" | 546 """show the contents of the current dirstate""" |
550 repo.dirstate.read() | 547 repo.dirstate.read() |
551 dc = repo.dirstate.map | 548 dc = repo.dirstate.map |
590 revs = [] | 587 revs = [] |
591 if opts['rev']: | 588 if opts['rev']: |
592 revs = map(lambda x: repo.lookup(x), opts['rev']) | 589 revs = map(lambda x: repo.lookup(x), opts['rev']) |
593 | 590 |
594 if len(revs) > 2: | 591 if len(revs) > 2: |
595 raise Abort("too many revisions to diff") | 592 raise util.Abort("too many revisions to diff") |
596 | 593 |
597 files = [] | 594 files = [] |
598 roots, match, results = makewalk(repo, pats, opts) | 595 roots, match, results = makewalk(repo, pats, opts) |
599 for src, abs, rel in results: | 596 for src, abs, rel in results: |
600 files.append(abs) | 597 files.append(abs) |
624 if fp != sys.stdout: fp.close() | 621 if fp != sys.stdout: fp.close() |
625 | 622 |
626 def export(ui, repo, *changesets, **opts): | 623 def export(ui, repo, *changesets, **opts): |
627 """dump the header and diffs for one or more changesets""" | 624 """dump the header and diffs for one or more changesets""" |
628 if not changesets: | 625 if not changesets: |
629 raise Abort("export requires at least one changeset") | 626 raise util.Abort("export requires at least one changeset") |
630 seqno = 0 | 627 seqno = 0 |
631 revs = list(revrange(ui, repo, changesets)) | 628 revs = list(revrange(ui, repo, changesets)) |
632 total = len(revs) | 629 total = len(revs) |
633 revwidth = max(len(revs[0]), len(revs[-1])) | 630 revwidth = max(len(revs[0]), len(revs[-1])) |
634 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n") | 631 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n") |
720 pf = l[14:] | 717 pf = l[14:] |
721 if pf not in files: | 718 if pf not in files: |
722 files.append(pf) | 719 files.append(pf) |
723 patcherr = f.close() | 720 patcherr = f.close() |
724 if patcherr: | 721 if patcherr: |
725 raise Abort("patch failed") | 722 raise util.Abort("patch failed") |
726 | 723 |
727 if len(files) > 0: | 724 if len(files) > 0: |
728 addremove(ui, repo, *files) | 725 addremove(ui, repo, *files) |
729 repo.commit(files, message, user) | 726 repo.commit(files, message, user) |
730 | 727 |
731 def init(ui, source=None): | 728 def init(ui, source=None): |
732 """create a new repository in the current directory""" | 729 """create a new repository in the current directory""" |
733 | 730 |
734 if source: | 731 if source: |
735 raise Abort("no longer supported: use \"hg clone\" instead") | 732 raise util.Abort("no longer supported: use \"hg clone\" instead") |
736 hg.repository(ui, ".", create=1) | 733 hg.repository(ui, ".", create=1) |
737 | 734 |
738 def locate(ui, repo, *pats, **opts): | 735 def locate(ui, repo, *pats, **opts): |
739 """locate files matching specific patterns""" | 736 """locate files matching specific patterns""" |
740 end = '\n' | 737 end = '\n' |
1035 A = added | 1032 A = added |
1036 R = removed | 1033 R = removed |
1037 ? = not tracked''' | 1034 ? = not tracked''' |
1038 | 1035 |
1039 cwd = repo.getcwd() | 1036 cwd = repo.getcwd() |
1040 files, matchfn = matchpats(cwd, pats, opts) | 1037 files, matchfn = matchpats(repo, cwd, pats, opts) |
1041 (c, a, d, u) = [[pathto(cwd, x) for x in n] | 1038 (c, a, d, u) = [[pathto(cwd, x) for x in n] |
1042 for n in repo.changes(files=files, match=matchfn)] | 1039 for n in repo.changes(files=files, match=matchfn)] |
1043 | 1040 |
1044 for f in c: | 1041 for f in c: |
1045 ui.write("M ", f, "\n") | 1042 ui.write("M ", f, "\n") |
1418 return d() | 1415 return d() |
1419 except: | 1416 except: |
1420 if options['traceback']: | 1417 if options['traceback']: |
1421 traceback.print_exc() | 1418 traceback.print_exc() |
1422 raise | 1419 raise |
1423 except util.CommandError, inst: | |
1424 u.warn("abort: %s\n" % inst.args) | |
1425 except hg.RepoError, inst: | 1420 except hg.RepoError, inst: |
1426 u.warn("abort: ", inst, "!\n") | 1421 u.warn("abort: ", inst, "!\n") |
1427 except SignalInterrupt: | 1422 except SignalInterrupt: |
1428 u.warn("killed!\n") | 1423 u.warn("killed!\n") |
1429 except KeyboardInterrupt: | 1424 except KeyboardInterrupt: |
1447 except OSError, inst: | 1442 except OSError, inst: |
1448 if hasattr(inst, "filename"): | 1443 if hasattr(inst, "filename"): |
1449 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename)) | 1444 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename)) |
1450 else: | 1445 else: |
1451 u.warn("abort: %s\n" % inst.strerror) | 1446 u.warn("abort: %s\n" % inst.strerror) |
1452 except Abort, inst: | 1447 except util.Abort, inst: |
1453 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n') | 1448 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n') |
1454 sys.exit(1) | 1449 sys.exit(1) |
1455 except TypeError, inst: | 1450 except TypeError, inst: |
1456 # was this an argument error? | 1451 # was this an argument error? |
1457 tb = traceback.extract_tb(sys.exc_info()[2]) | 1452 tb = traceback.extract_tb(sys.exc_info()[2]) |