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])