# HG changeset patch # User mpm@selenic.com # Date 1123991022 28800 # Node ID 01215ad0428341bf0b5806b199c54fe158d9affd # Parent 0cd2ee61b10a03992daabee417949866318c308b# Parent 77b52b864249768a0ec615af331be778fdbb65e9 Merge with BOS diff -r 0cd2ee61b10a -r 01215ad04283 .hgignore diff -r 0cd2ee61b10a -r 01215ad04283 CONTRIBUTORS diff -r 0cd2ee61b10a -r 01215ad04283 TODO diff -r 0cd2ee61b10a -r 01215ad04283 contrib/patchbomb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/patchbomb Sat Aug 13 19:43:42 2005 -0800 @@ -0,0 +1,247 @@ +#!/usr/bin/python +# +# Interactive script for sending a collection of Mercurial changesets +# as a series of patch emails. +# +# The series is started off with a "[PATCH 0 of N]" introduction, +# which describes the series as a whole. +# +# Each patch email has a Subject line of "[PATCH M of N] ...", using +# the first line of the changeset description as the subject text. +# The message contains two or three body parts: +# +# The remainder of the changeset description. +# +# [Optional] If the diffstat program is installed, the result of +# running diffstat on the patch. +# +# The patch itself, as generated by "hg export". +# +# Each message refers to all of its predecessors using the In-Reply-To +# and References headers, so they will show up as a sequence in +# threaded mail and news readers, and in mail archives. +# +# For each changeset, you will be prompted with a diffstat summary and +# the changeset summary, so you can be sure you are sending the right +# changes. +# +# It is best to run this script with the "-n" (test only) flag before +# firing it up "for real", in which case it will use your pager to +# display each of the messages that it would send. +# +# To configure a default mail host, add a section like this to your +# hgrc file: +# +# [smtp] +# host = my_mail_host +# port = 1025 +# +# To configure other defaults, add a section like this to your hgrc +# file: +# +# [patchbomb] +# from = My Name +# to = recipient1, recipient2, ... +# cc = cc1, cc2, ... + +from email.MIMEMultipart import MIMEMultipart +from email.MIMEText import MIMEText +from mercurial import commands +from mercurial import fancyopts +from mercurial import hg +from mercurial import ui +import os +import popen2 +import readline +import smtplib +import socket +import sys +import tempfile +import time + +def diffstat(patch): + fd, name = tempfile.mkstemp() + try: + p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name) + try: + for line in patch: print >> p.tochild, line + p.tochild.close() + if p.wait(): return + fp = os.fdopen(fd, 'r') + stat = [] + for line in fp: stat.append(line.lstrip()) + last = stat.pop() + stat.insert(0, last) + stat = ''.join(stat) + if stat.startswith('0 files'): raise ValueError + return stat + except: raise + finally: + try: os.unlink(name) + except: pass + +def patchbomb(ui, repo, *revs, **opts): + def prompt(prompt, default = None, rest = ': ', empty_ok = False): + if default: prompt += ' [%s]' % default + prompt += rest + while True: + r = raw_input(prompt) + if r: return r + if default is not None: return default + if empty_ok: return r + ui.warn('Please enter a valid value.\n') + + def confirm(s): + if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'): + raise ValueError + + def cdiffstat(summary, patch): + s = diffstat(patch) + if s: + if summary: + ui.write(summary, '\n') + ui.write(s, '\n') + confirm('Does the diffstat above look okay') + return s + + def makepatch(patch, idx, total): + desc = [] + node = None + for line in patch: + if line.startswith('#'): + if line.startswith('# Node ID'): node = line.split()[-1] + continue + if line.startswith('diff -r'): break + desc.append(line) + if not node: raise ValueError + body = ('\n'.join(desc[1:]).strip() or + 'Patch subject is complete summary.') + body += '\n\n\n' + if opts['diffstat']: + body += cdiffstat('\n'.join(desc), patch) + '\n\n' + body += '\n'.join(patch) + msg = MIMEText(body) + subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip()) + if subj.endswith('.'): subj = subj[:-1] + msg['Subject'] = subj + msg['X-Mercurial-Node'] = node + return msg + + start_time = int(time.time()) + + def genmsgid(id): + return '<%s.%s@%s>' % (id[:20], start_time, socket.getfqdn()) + + patches = [] + + class exportee: + def __init__(self, container): + self.lines = [] + self.container = container + self.name = 'email' + + def write(self, data): + self.lines.append(data) + + def close(self): + self.container.append(''.join(self.lines).split('\n')) + self.lines = [] + + commands.export(ui, repo, *args, **{'output': exportee(patches)}) + + jumbo = [] + msgs = [] + + ui.write('This patch series consists of %d patches.\n\n' % len(patches)) + + for p, i in zip(patches, range(len(patches))): + jumbo.extend(p) + msgs.append(makepatch(p, i + 1, len(patches))) + + ui.write('\nWrite the introductory message for the patch series.\n\n') + + sender = (opts['from'] or ui.config('patchbomb', 'from') or + prompt('From', ui.username())) + + msg = MIMEMultipart() + msg['Subject'] = '[PATCH 0 of %d] %s' % ( + len(patches), + opts['subject'] or + prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches))) + to = (opts['to'] or ui.config('patchbomb', 'to') or + [s.strip() for s in prompt('To').split(',')]) + cc = (opts['cc'] or ui.config('patchbomb', 'cc') or + [s.strip() for s in prompt('Cc', default = '').split(',')]) + + ui.write('Finish with ^D or a dot on a line by itself.\n\n') + + body = [] + + while True: + try: l = raw_input() + except EOFError: break + if l == '.': break + body.append(l) + + msg.attach(MIMEText('\n'.join(body) + '\n')) + + ui.write('\n') + + d = cdiffstat('Final summary:\n', jumbo) + if d: msg.attach(MIMEText(d)) + + msgs.insert(0, msg) + + if not opts['test']: + s = smtplib.SMTP() + s.connect(host = ui.config('smtp', 'host', 'mail'), + port = int(ui.config('smtp', 'port', 25))) + + parent = None + tz = time.strftime('%z') + for m in msgs: + try: + m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) + except TypeError: + m['Message-Id'] = genmsgid('patchbomb') + if parent: + m['In-Reply-To'] = parent + else: + parent = m['Message-Id'] + m['Date'] = time.strftime('%a, %e %b %Y %T ', time.localtime(start_time)) + tz + start_time += 1 + m['From'] = sender + m['To'] = ', '.join(to) + if cc: m['Cc'] = ', '.join(cc) + ui.status('Sending ', m['Subject'], ' ...\n') + if opts['test']: + fp = os.popen(os.getenv('PAGER', 'more'), 'w') + fp.write(m.as_string(0)) + fp.write('\n') + fp.close() + else: + s.sendmail(sender, to + cc, m.as_string(0)) + if not opts['test']: + s.close() + +if __name__ == '__main__': + optspec = [('c', 'cc', [], 'email addresses of copy recipients'), + ('d', 'diffstat', None, 'add diffstat output to messages'), + ('f', 'from', '', 'email address of sender'), + ('n', 'test', None, 'print messages that would be sent'), + ('s', 'subject', '', 'subject of introductory message'), + ('t', 'to', [], 'email addresses of recipients')] + options = {} + try: + args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts + optspec, + options) + except fancyopts.getopt.GetoptError, inst: + u = ui.ui() + u.warn('error: %s' % inst) + sys.exit(1) + + u = ui.ui(options["verbose"], options["debug"], options["quiet"], + not options["noninteractive"]) + repo = hg.repository(ui = u) + + patchbomb(u, repo, *args, **options) diff -r 0cd2ee61b10a -r 01215ad04283 doc/hg.1.txt diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/bdiff.c diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/commands.py --- a/mercurial/commands.py Sat Aug 13 12:41:00 2005 -0800 +++ b/mercurial/commands.py Sat Aug 13 19:43:42 2005 -0800 @@ -14,9 +14,6 @@ class UnknownCommand(Exception): """Exception raised if command is not in the command table.""" -class Abort(Exception): - """Raised if a command needs to print an error and exit.""" - def filterfiles(filters, files): l = [x for x in files if x in filters] @@ -35,30 +32,19 @@ def relpath(repo, args): cwd = repo.getcwd() if cwd: - return [util.pconvert(os.path.normpath(os.path.join(cwd, x))) - for x in args] + return [util.normpath(os.path.join(cwd, x)) for x in args] return args -def matchpats(cwd, pats = [], opts = {}, head = ''): - return util.matcher(cwd, pats or ['.'], opts.get('include'), +def matchpats(repo, cwd, pats = [], opts = {}, head = ''): + return util.matcher(repo, cwd, pats or ['.'], opts.get('include'), opts.get('exclude'), head) -def pathto(n1, n2): - '''return the relative path from one place to another''' - if not n1: return n2 - a, b = n1.split(os.sep), n2.split(os.sep) - a.reverse(), b.reverse() - while a and b and a[-1] == b[-1]: - a.pop(), b.pop() - b.reverse() - return os.sep.join((['..'] * len(a)) + b) - def makewalk(repo, pats, opts, head = ''): cwd = repo.getcwd() - files, matchfn = matchpats(cwd, pats, opts, head) + files, matchfn = matchpats(repo, cwd, pats, opts, head) def walk(): for src, fn in repo.walk(files = files, match = matchfn): - yield src, fn, pathto(cwd, fn) + yield src, fn, util.pathto(cwd, fn) return files, matchfn, walk() def walk(repo, pats, opts, head = ''): @@ -89,7 +75,7 @@ try: num = revlog.rev(revlog.lookup(val)) except KeyError: - raise Abort('invalid revision identifier %s', val) + raise util.Abort('invalid revision identifier %s', val) return num for spec in revs: if spec.find(revrangesep) >= 0: @@ -144,7 +130,7 @@ i += 1 return ''.join(newname) except KeyError, inst: - raise Abort("invalid format spec '%%%s' in output file name", + raise util.Abort("invalid format spec '%%%s' in output file name", inst.args[0]) def make_file(repo, r, pat, node=None, @@ -396,11 +382,10 @@ q = dict(zip(pats, pats)) add, remove = [], [] for src, abs, rel in walk(repo, pats, opts): - if src == 'f': - if repo.dirstate.state(abs) == '?': - add.append(abs) - if rel not in q: ui.status('adding ', rel, '\n') - elif repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): + if src == 'f' and repo.dirstate.state(abs) == '?': + add.append(abs) + if rel not in q: ui.status('adding ', rel, '\n') + if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): remove.append(abs) if rel not in q: ui.status('removing ', rel, '\n') repo.add(add) @@ -427,7 +412,7 @@ return name if not pats: - raise Abort('at least one file name or pattern required') + raise util.Abort('at least one file name or pattern required') bcache = {} opmap = [['user', getname], ['number', str], ['changeset', getnode]] @@ -478,6 +463,8 @@ ui.warn("abort: destination '%s' already exists\n" % dest) return 1 + dest = os.path.realpath(dest) + class Dircleanup: def __init__(self, dir_): self.rmtree = shutil.rmtree @@ -541,7 +528,7 @@ if not pats and cwd: opts['include'] = [os.path.join(cwd, i) for i in opts['include']] opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] - fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts) + fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts) if pats: c, a, d, u = repo.changes(files = fns, match = match) files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r'] @@ -583,7 +570,7 @@ ui.warn("%s in manifest1, but listed as state %s" % (f, state)) errors += 1 if errors: - raise Abort(".hg/dirstate inconsistent with current parent's manifest") + raise util.Abort(".hg/dirstate inconsistent with current parent's manifest") def debugstate(ui, repo): """show the contents of the current dirstate""" @@ -621,6 +608,7 @@ def debugwalk(ui, repo, *pats, **opts): items = list(walk(repo, pats, opts)) + if not items: return fmt = '%%s %%-%ds %%s' % max([len(abs) for (src, abs, rel) in items]) for i in items: print fmt % i @@ -631,12 +619,14 @@ revs = map(lambda x: repo.lookup(x), opts['rev']) if len(revs) > 2: - raise Abort("too many revisions to diff") + raise util.Abort("too many revisions to diff") files = [] - roots, match, results = makewalk(repo, pats, opts) - for src, abs, rel in results: - files.append(abs) + match = util.always + if pats: + roots, match, results = makewalk(repo, pats, opts) + for src, abs, rel in results: + files.append(abs) dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match}) def doexport(ui, repo, changeset, seqno, total, revwidth, opts): @@ -665,7 +655,7 @@ def export(ui, repo, *changesets, **opts): """dump the header and diffs for one or more changesets""" if not changesets: - raise Abort("export requires at least one changeset") + raise util.Abort("export requires at least one changeset") seqno = 0 revs = list(revrange(ui, repo, changesets)) total = len(revs) @@ -762,7 +752,7 @@ files.append(pf) patcherr = f.close() if patcherr: - raise Abort("patch failed") + raise util.Abort("patch failed") if len(files) > 0: addremove(ui, repo, *files) @@ -772,7 +762,7 @@ """create a new repository in the current directory""" if source: - raise Abort("no longer supported: use \"hg clone\" instead") + raise util.Abort("no longer supported: use \"hg clone\" instead") hg.repository(ui, ".", create=1) def locate(ui, repo, *pats, **opts): @@ -1078,8 +1068,8 @@ ''' cwd = repo.getcwd() - files, matchfn = matchpats(cwd, pats, opts) - (c, a, d, u) = [[pathto(cwd, x) for x in n] + files, matchfn = matchpats(repo, cwd, pats, opts) + (c, a, d, u) = [[util.pathto(cwd, x) for x in n] for n in repo.changes(files=files, match=matchfn)] changetypes = [('modified', 'M', c), @@ -1471,8 +1461,6 @@ if options['traceback']: traceback.print_exc() raise - except util.CommandError, inst: - u.warn("abort: %s\n" % inst.args) except hg.RepoError, inst: u.warn("abort: ", inst, "!\n") except SignalInterrupt: @@ -1500,7 +1488,7 @@ u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename)) else: u.warn("abort: %s\n" % inst.strerror) - except Abort, inst: + except util.Abort, inst: u.warn('abort: ', inst.args[0] % inst.args[1:], '\n') sys.exit(1) except TypeError, inst: diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/hg.py --- a/mercurial/hg.py Sat Aug 13 12:41:00 2005 -0800 +++ b/mercurial/hg.py Sat Aug 13 19:43:42 2005 -0800 @@ -10,8 +10,8 @@ from revlog import * from demandload import * demandload(globals(), "re lock urllib urllib2 transaction time socket") -demandload(globals(), "tempfile httprangereader bdiff urlparse stat") -demandload(globals(), "bisect select") +demandload(globals(), "tempfile httprangereader bdiff urlparse") +demandload(globals(), "bisect errno select stat") class filelog(revlog): def __init__(self, opener, path): @@ -300,6 +300,11 @@ def wjoin(self, f): return os.path.join(self.root, f) + def getcwd(self): + cwd = os.getcwd() + if cwd == self.root: return '' + return cwd[len(self.root) + 1:] + def ignore(self, f): if not self.ignorefunc: bigpat = [] @@ -307,14 +312,14 @@ l = file(self.wjoin(".hgignore")) for pat in l: if pat != "\n": - p = util.pconvert(pat[:-1]) + p = pat[:-1] try: - r = re.compile(p) + re.compile(p) except: self.ui.warn("ignoring invalid ignore" + " regular expression '%s'\n" % p) else: - bigpat.append(util.pconvert(pat[:-1])) + bigpat.append(p) except IOError: pass if bigpat: @@ -437,22 +442,69 @@ st.write(e + f) self.dirty = 0 - def walk(self, files = None, match = util.always): + def filterfiles(self, files): + ret = {} + unknown = [] + + for x in files: + if x is '.': + return self.map.copy() + if x not in self.map: + unknown.append(x) + else: + ret[x] = self.map[x] + + if not unknown: + return ret + + b = self.map.keys() + b.sort() + blen = len(b) + + for x in unknown: + bs = bisect.bisect(b, x) + if bs != 0 and b[bs-1] == x: + ret[x] = self.map[x] + continue + while bs < blen: + s = b[bs] + if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/': + ret[s] = self.map[s] + else: + break + bs += 1 + return ret + + def walk(self, files = None, match = util.always, dc=None): self.read() - dc = self.map.copy() + # walk all files by default - if not files: files = [self.root] + if not files: + files = [self.root] + if not dc: + dc = self.map.copy() + elif not dc: + dc = self.filterfiles(files) + known = {'.hg': 1} def seen(fn): if fn in known: return True known[fn] = 1 def traverse(): - for f in util.unique(files): - f = os.path.join(self.root, f) - if os.path.isdir(f): + for ff in util.unique(files): + f = os.path.join(self.root, ff) + try: + st = os.stat(f) + except OSError, inst: + if ff not in dc: self.ui.warn('%s: %s\n' % ( + util.pathto(self.getcwd(), ff), + inst.strerror)) + continue + if stat.S_ISDIR(st.st_mode): for dir, subdirs, fl in os.walk(f): d = dir[len(self.root) + 1:] - nd = os.path.normpath(d) + nd = util.normpath(d) + if nd == '.': nd = '' if seen(nd): subdirs[:] = [] continue @@ -465,8 +517,18 @@ for fn in fl: fn = util.pconvert(os.path.join(d, fn)) yield 'f', fn + elif stat.S_ISREG(st.st_mode): + yield 'f', ff else: - yield 'f', f[len(self.root) + 1:] + kind = 'unknown' + if stat.S_ISCHR(st.st_mode): kind = 'character device' + elif stat.S_ISBLK(st.st_mode): kind = 'block device' + elif stat.S_ISFIFO(st.st_mode): kind = 'fifo' + elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link' + elif stat.S_ISSOCK(st.st_mode): kind = 'socket' + self.ui.warn('%s: unsupported file type (type is %s)\n' % ( + util.pathto(self.getcwd(), ff), + kind)) ks = dc.keys() ks.sort() @@ -477,22 +539,23 @@ # not in .hgignore for src, fn in util.unique(traverse()): - fn = os.path.normpath(fn) + fn = util.normpath(fn) if seen(fn): continue - if fn in dc: - del dc[fn] - elif self.ignore(fn): + if fn not in dc and self.ignore(fn): continue if match(fn): yield src, fn def changes(self, files=None, match=util.always): self.read() - dc = self.map.copy() + if not files: + dc = self.map.copy() + else: + dc = self.filterfiles(files) lookup, modified, added, unknown = [], [], [], [] removed, deleted = [], [] - for src, fn in self.walk(files, match): + for src, fn in self.walk(files, match, dc=dc): try: s = os.stat(os.path.join(self.root, fn)) except OSError: @@ -697,9 +760,7 @@ return filelog(self.opener, f) def getcwd(self): - cwd = os.getcwd() - if cwd == self.root: return '' - return cwd[len(self.root) + 1:] + return self.dirstate.getcwd() def wfile(self, f, mode='r'): return self.wopener(f, mode) diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/hgweb.py --- a/mercurial/hgweb.py Sat Aug 13 12:41:00 2005 -0800 +++ b/mercurial/hgweb.py Sat Aug 13 19:43:42 2005 -0800 @@ -708,7 +708,12 @@ import BaseHTTPServer class IPv6HTTPServer(BaseHTTPServer.HTTPServer): - address_family = socket.AF_INET6 + address_family = getattr(socket, 'AF_INET6', None) + + def __init__(self, *args, **kwargs): + if self.address_family is None: + raise RepoError('IPv6 not available on this system') + BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs) class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler): def log_error(self, format, *args): diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/revlog.py diff -r 0cd2ee61b10a -r 01215ad04283 mercurial/util.py --- a/mercurial/util.py Sat Aug 13 12:41:00 2005 -0800 +++ b/mercurial/util.py Sat Aug 13 19:43:42 2005 -0800 @@ -16,7 +16,8 @@ seen[f] = 1 yield f -class CommandError(Exception): pass +class Abort(Exception): + """Raised if a command needs to print an error and exit.""" def always(fn): return True def never(fn): return False @@ -68,36 +69,52 @@ _globchars = {'[': 1, '{': 1, '*': 1, '?': 1} -def matcher(cwd, names, inc, exc, head = ''): +def pathto(n1, n2): + '''return the relative path from one place to another. + this returns a path in the form used by the local filesystem, not hg.''' + if not n1: return localpath(n2) + a, b = n1.split('/'), n2.split('/') + a.reverse(), b.reverse() + while a and b and a[-1] == b[-1]: + a.pop(), b.pop() + b.reverse() + return os.sep.join((['..'] * len(a)) + b) + +def canonpath(repo, cwd, myname): + rootsep = repo.root + os.sep + name = myname + if not name.startswith(os.sep): + name = os.path.join(repo.root, cwd, name) + name = os.path.normpath(name) + if name.startswith(rootsep): + return pconvert(name[len(rootsep):]) + elif name == repo.root: + return '' + else: + raise Abort('%s not under repository root' % myname) + +def matcher(repo, cwd, names, inc, exc, head = ''): def patkind(name): - for prefix in 're:', 'glob:', 'path:': + for prefix in 're:', 'glob:', 'path:', 'relpath:': if name.startswith(prefix): return name.split(':', 1) for c in name: if c in _globchars: return 'glob', name return 'relpath', name - cwdsep = cwd + os.sep - - def regex(name, tail): + def regex(kind, name, tail): '''convert a pattern into a regular expression''' - kind, name = patkind(name) if kind == 're': return name elif kind == 'path': - return '^' + re.escape(name) + '$' - if cwd: name = os.path.join(cwdsep, name) - name = os.path.normpath(name) - if name == '.': name = '**' + return '^' + re.escape(name) + '(?:/|$)' + elif kind == 'relpath': + return head + re.escape(name) + tail return head + globre(name, '', tail) - def under(fn): - """check if fn is under our cwd""" - return not cwd or fn.startswith(cwdsep) - def matchfn(pats, tail): """build a matching function from a set of patterns""" if pats: - pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats]) + pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats]) return re.compile(pat).match def globprefix(pat): @@ -106,18 +123,29 @@ for p in pat.split(os.sep): if patkind(p)[0] == 'glob': break root.append(p) - return os.sep.join(root) + return '/'.join(root) - patkinds = map(patkind, names) - pats = [name for (kind, name) in patkinds if kind != 'relpath'] - files = [name for (kind, name) in patkinds if kind == 'relpath'] - roots = filter(None, map(globprefix, pats)) + files - if cwd: roots = [cwdsep + r for r in roots] + pats = [] + files = [] + roots = [] + for kind, name in map(patkind, names): + if kind in ('glob', 'relpath'): + name = canonpath(repo, cwd, name) + if name == '': + kind, name = 'glob', '**' + if kind in ('glob', 'path', 're'): + pats.append((kind, name)) + if kind == 'glob': + root = globprefix(name) + if root: roots.append(root) + elif kind == 'relpath': + files.append((kind, name)) + roots.append(name) patmatch = matchfn(pats, '$') or always filematch = matchfn(files, '(?:/|$)') or always - incmatch = matchfn(inc, '(?:/|$)') or always - excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False) + incmatch = matchfn(map(patkind, inc), '(?:/|$)') or always + excmatch = matchfn(map(patkind, exc), '(?:/|$)') or (lambda fn: False) return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and (fn.endswith('/') or @@ -133,7 +161,7 @@ explain_exit(rc)[0]) if errprefix: errmsg = "%s: %s" % (errprefix, errmsg) - raise CommandError(errmsg) + raise Abort(errmsg) def rename(src, dst): try: @@ -178,6 +206,12 @@ def pconvert(path): return path.replace("\\", "/") + def localpath(path): + return path.replace('/', '\\') + + def normpath(path): + return pconvert(os.path.normpath(path)) + makelock = _makelock_file readlock = _readlock_file @@ -206,6 +240,11 @@ def pconvert(path): return path + def localpath(path): + return path + + normpath = os.path.normpath + def makelock(info, pathname): try: os.symlink(info, pathname) diff -r 0cd2ee61b10a -r 01215ad04283 templates/map diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-help diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-help.out diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-merge-revert.out diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-merge-revert2 diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-merge-revert2.out diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-walk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-walk Sat Aug 13 19:43:42 2005 -0800 @@ -0,0 +1,55 @@ +#!/bin/sh + +mkdir t +cd t +hg init +mkdir -p beans +for b in kidney navy turtle borlotti black pinto; do + echo $b > beans/$b +done +mkdir -p mammals/Procyonidae +for m in cacomistle coatimundi raccoon; do + echo $m > mammals/Procyonidae/$m +done +echo skunk > mammals/skunk +echo fennel > fennel +echo fenugreek > fenugreek +echo fiddlehead > fiddlehead +echo glob:glob > glob:glob +hg addremove +hg commit -m "commit #0" -d "0 0" +hg debugwalk +cd mammals +hg debugwalk +hg debugwalk Procyonidae +cd Procyonidae +hg debugwalk +hg debugwalk .. +cd .. +hg debugwalk ../beans +hg debugwalk +cd .. +hg debugwalk -Ibeans +hg debugwalk 'mammals/../beans/b*' +hg debugwalk '-X*/Procyonidae' mammals +hg debugwalk path:mammals +hg debugwalk .. +hg debugwalk beans/../.. +# Don't know how to test absolute paths without always getting a false +# error. +#hg debugwalk `pwd`/beans +#hg debugwalk `pwd`/.. +hg debugwalk glob:\* +hg debugwalk 're:.*[kb]$' +hg debugwalk path:beans/black +hg debugwalk beans 'beans/*' +hg debugwalk 'j*' +hg debugwalk NOEXIST +mkfifo fifo +hg debugwalk fifo +rm fenugreek +hg debugwalk fenugreek +hg rm fenugreek +hg debugwalk fenugreek +touch new +hg debugwalk new diff -r 0cd2ee61b10a -r 01215ad04283 tests/test-walk.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-walk.out Sat Aug 13 19:43:42 2005 -0800 @@ -0,0 +1,114 @@ ++ hg init ++ hg addremove +adding fennel +adding fenugreek +adding fiddlehead +adding glob:glob +adding beans/black +adding beans/borlotti +adding beans/kidney +adding beans/navy +adding beans/pinto +adding beans/turtle +adding mammals/skunk +adding mammals/Procyonidae/cacomistle +adding mammals/Procyonidae/coatimundi +adding mammals/Procyonidae/raccoon ++ hg commit -m commit #0 -d 0 0 ++ hg debugwalk +f fennel fennel +f fenugreek fenugreek +f fiddlehead fiddlehead +f glob:glob glob:glob +f beans/black beans/black +f beans/borlotti beans/borlotti +f beans/kidney beans/kidney +f beans/navy beans/navy +f beans/pinto beans/pinto +f beans/turtle beans/turtle +f mammals/skunk mammals/skunk +f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle +f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi +f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon ++ hg debugwalk +f mammals/skunk skunk +f mammals/Procyonidae/cacomistle Procyonidae/cacomistle +f mammals/Procyonidae/coatimundi Procyonidae/coatimundi +f mammals/Procyonidae/raccoon Procyonidae/raccoon ++ hg debugwalk Procyonidae +f mammals/Procyonidae/cacomistle Procyonidae/cacomistle +f mammals/Procyonidae/coatimundi Procyonidae/coatimundi +f mammals/Procyonidae/raccoon Procyonidae/raccoon ++ hg debugwalk +f mammals/Procyonidae/cacomistle cacomistle +f mammals/Procyonidae/coatimundi coatimundi +f mammals/Procyonidae/raccoon raccoon ++ hg debugwalk .. +f mammals/skunk ../skunk +f mammals/Procyonidae/cacomistle cacomistle +f mammals/Procyonidae/coatimundi coatimundi +f mammals/Procyonidae/raccoon raccoon ++ hg debugwalk ../beans +f beans/black ../beans/black +f beans/borlotti ../beans/borlotti +f beans/kidney ../beans/kidney +f beans/navy ../beans/navy +f beans/pinto ../beans/pinto +f beans/turtle ../beans/turtle ++ hg debugwalk +f mammals/skunk skunk +f mammals/Procyonidae/cacomistle Procyonidae/cacomistle +f mammals/Procyonidae/coatimundi Procyonidae/coatimundi +f mammals/Procyonidae/raccoon Procyonidae/raccoon ++ hg debugwalk -Ibeans +f beans/black beans/black +f beans/borlotti beans/borlotti +f beans/kidney beans/kidney +f beans/navy beans/navy +f beans/pinto beans/pinto +f beans/turtle beans/turtle ++ hg debugwalk mammals/../beans/b* +f beans/black beans/black +f beans/borlotti beans/borlotti ++ hg debugwalk -X*/Procyonidae mammals +f mammals/skunk mammals/skunk ++ hg debugwalk path:mammals +f mammals/skunk mammals/skunk +f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle +f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi +f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon ++ hg debugwalk .. +abort: .. not under repository root ++ hg debugwalk beans/../.. +abort: beans/../.. not under repository root ++ hg debugwalk glob:* +f fennel fennel +f fenugreek fenugreek +f fiddlehead fiddlehead +f glob:glob glob:glob ++ hg debugwalk re:.*[kb]$ +f fenugreek fenugreek +f glob:glob glob:glob +f beans/black beans/black +f mammals/skunk mammals/skunk ++ hg debugwalk path:beans/black +f beans/black beans/black ++ hg debugwalk beans beans/* +f beans/black beans/black +f beans/borlotti beans/borlotti +f beans/kidney beans/kidney +f beans/navy beans/navy +f beans/pinto beans/pinto +f beans/turtle beans/turtle ++ hg debugwalk j* ++ hg debugwalk NOEXIST +NOEXIST: No such file or directory ++ hg debugwalk fifo +fifo: unsupported file type (type is fifo) ++ hg debugwalk fenugreek +m fenugreek fenugreek ++ hg rm fenugreek ++ hg debugwalk fenugreek +m fenugreek fenugreek ++ hg debugwalk new +f new new