Mercurial > hg > gitweb
changeset 2177:6886bc0b77af
merge with crew.
author | Vadim Gelfer <vadim.gelfer@gmail.com> |
---|---|
date | Tue, 02 May 2006 14:37:55 -0700 |
parents | 3044a3fdae76 (diff) 9b42304d9896 (current diff) |
children | 00205fe76993 |
files | mercurial/appendfile.py mercurial/revlog.py mercurial/sshrepo.py mercurial/util.py |
diffstat | 37 files changed, 684 insertions(+), 182 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue May 02 14:30:00 2006 -0700 +++ b/.hgignore Tue May 02 14:37:55 2006 -0700 @@ -9,6 +9,7 @@ *.swp *.prof tests/.coverage* +tests/annotated tests/*.err build dist
--- a/CONTRIBUTORS Tue May 02 14:30:00 2006 -0700 +++ b/CONTRIBUTORS Tue May 02 14:37:55 2006 -0700 @@ -14,6 +14,7 @@ Vadim Lebedev <vadim at mbdsys.com> Christopher Li <hg at chrisli.org> Chris Mason <mason at suse.com> +Colin McMillen <mcmillen at cs.cmu.edu> Wojciech Milkowski <wmilkowski at interia.pl> Chad Netzer <chad.netzer at gmail.com> Bryan O'Sullivan <bos at serpentine.com>
--- a/doc/hgrc.5.txt Tue May 02 14:30:00 2006 -0700 +++ b/doc/hgrc.5.txt Tue May 02 14:37:55 2006 -0700 @@ -131,11 +131,11 @@ **.txt = tempfile: unix2dos -n INFILE OUTFILE hooks:: - Commands that get automatically executed by various actions such as - starting or finishing a commit. Multiple commands can be run for - the same action by appending a suffix to the action. Overriding a - site-wide hook can be done by changing its value or setting it to - an empty string. + Commands or Python functions that get automatically executed by + various actions such as starting or finishing a commit. Multiple + hooks can be run for the same action by appending a suffix to the + action. Overriding a site-wide hook can be done by changing its + value or setting it to an empty string. Example .hg/hgrc: @@ -211,6 +211,21 @@ the environment for backwards compatibility, but their use is deprecated, and they will be removed in a future release. + The syntax for Python hooks is as follows: + + hookname = python:modulename.submodule.callable + + Python hooks are run within the Mercurial process. Each hook is + called with at least three keyword arguments: a ui object (keyword + "ui"), a repository object (keyword "repo"), and a "hooktype" + keyword that tells what kind of hook is used. Arguments listed as + environment variables above are passed as keyword arguments, with no + "HG_" prefix, and names in lower case. + + A Python hook must return a "true" value to succeed. Returning a + "false" value or raising an exception is treated as failure of the + hook. + http_proxy:: Used to access web-based Mercurial repositories through a HTTP proxy.
--- a/hgext/gpg.py Tue May 02 14:30:00 2006 -0700 +++ b/hgext/gpg.py Tue May 02 14:37:55 2006 -0700 @@ -23,11 +23,11 @@ """ returns of the good and bad signatures""" try: # create temporary files - fd, sigfile = tempfile.mkstemp(prefix="hggpgsig") + fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig") fp = os.fdopen(fd, 'wb') fp.write(sig) fp.close() - fd, datafile = tempfile.mkstemp(prefix="hggpgdata") + fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt") fp = os.fdopen(fd, 'wb') fp.write(data) fp.close()
--- a/hgext/patchbomb.py Tue May 02 14:30:00 2006 -0700 +++ b/hgext/patchbomb.py Tue May 02 14:37:55 2006 -0700 @@ -62,7 +62,7 @@ except ImportError: pass def diffstat(patch): - fd, name = tempfile.mkstemp() + fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt") try: p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name) try:
--- a/mercurial/appendfile.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/appendfile.py Tue May 02 14:37:55 2006 -0700 @@ -38,7 +38,7 @@ self.tmpname = tmpname self.tmpfp = util.posixfile(self.tmpname, 'ab+') else: - fd, self.tmpname = tempfile.mkstemp() + fd, self.tmpname = tempfile.mkstemp(prefix="hg-appendfile-") os.close(fd) self.tmpfp = util.posixfile(self.tmpname, 'ab+') self.realfp = fp
--- a/mercurial/archival.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/archival.py Tue May 02 14:37:55 2006 -0700 @@ -80,8 +80,11 @@ def __init__(self, dest, prefix, compress=True): self.prefix = tidyprefix(dest, prefix, ('.zip',)) - if not isinstance(dest, str) and not hasattr(dest, 'tell'): - dest = tellable(dest) + if not isinstance(dest, str): + try: + dest.tell() + except (AttributeError, IOError): + dest = tellable(dest) self.z = zipfile.ZipFile(dest, 'w', compress and zipfile.ZIP_DEFLATED or zipfile.ZIP_STORED)
--- a/mercurial/changelog.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/changelog.py Tue May 02 14:37:55 2006 -0700 @@ -11,7 +11,7 @@ demandload(globals(), "os time util") class changelog(revlog): - def __init__(self, opener, defversion=0): + def __init__(self, opener, defversion=REVLOGV0): revlog.__init__(self, opener, "00changelog.i", "00changelog.d", defversion) @@ -41,14 +41,15 @@ if date: # validate explicit (probably user-specified) date and # time zone offset. values must fit in signed 32 bits for - # current 32-bit linux runtimes. + # current 32-bit linux runtimes. timezones go from UTC-12 + # to UTC+14 try: when, offset = map(int, date.split(' ')) except ValueError: raise ValueError(_('invalid date: %r') % date) if abs(when) > 0x7fffffff: raise ValueError(_('date exceeds 32 bits: %d') % when) - if abs(offset) >= 43200: + if offset < -50400 or offset > 43200: raise ValueError(_('impossible time zone offset: %d') % offset) else: date = "%d %d" % util.makedate()
--- a/mercurial/commands.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/commands.py Tue May 02 14:37:55 2006 -0700 @@ -19,6 +19,11 @@ class AmbiguousCommand(Exception): """Exception raised if command shortcut matches more than one command.""" +def bail_if_changed(repo): + modified, added, removed, deleted, unknown = repo.changes() + if modified or added or removed or deleted: + raise util.Abort(_("outstanding uncommitted changes")) + def filterfiles(filters, files): l = [x for x in files if x in filters] @@ -298,7 +303,7 @@ raise util.Abort(_("file '%s' already exists"), filename) fh = open(filename, "wb") else: - fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-") + fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") fh = os.fdopen(fd, "wb") cleanup = filename @@ -926,10 +931,48 @@ prefix = make_filename(repo, repo.changelog, opts['prefix'], node) if os.path.realpath(dest) == repo.root: raise util.Abort(_('repository root cannot be destination')) - _, matchfn, _ = matchpats(repo, [], opts) + dummy, matchfn, dummy = matchpats(repo, [], opts) archival.archive(repo, dest, node, opts.get('type') or 'files', not opts['no_decode'], matchfn, prefix) +def backout(ui, repo, rev, **opts): + '''reverse effect of earlier changeset + + Commit the backed out changes as a new changeset. + + If you back out a changeset other than the tip, a new head is + created. The --merge option remembers the parent of the working + directory before starting the backout, then merges the new head + with it afterwards, to save you from doing this by hand. The + result of this merge is not committed, as for a normal merge.''' + + bail_if_changed(repo) + op1, op2 = repo.dirstate.parents() + if op2 != nullid: + raise util.Abort(_('outstanding uncommitted merge')) + node = repo.lookup(rev) + parent, p2 = repo.changelog.parents(node) + if parent == nullid: + raise util.Abort(_('cannot back out a change with no parents')) + if p2 != nullid: + raise util.Abort(_('cannot back out a merge')) + repo.update(node, force=True) + revert_opts = opts.copy() + revert_opts['rev'] = hex(parent) + revert(ui, repo, **revert_opts) + commit_opts = opts.copy() + commit_opts['addremove'] = False + if not commit_opts['message'] and not commit_opts['logfile']: + commit_opts['message'] = _("Backed out changeset %s") % (hex(node)) + commit(ui, repo, **commit_opts) + def nice(node): + return '%d:%s' % (repo.changelog.rev(node), short(node)) + ui.status(_('changeset %s backs out changeset %s\n') % + (nice(repo.changelog.tip()), nice(node))) + if opts['merge'] and op1 != node: + ui.status(_('merging with changeset %s\n') % nice(op1)) + update(ui, repo, hex(op1), **opts) + def bundle(ui, repo, fname, dest="default-push", **opts): """create a changegroup file @@ -1572,10 +1615,15 @@ doexport(ui, repo, cset, seqno, total, revwidth, opts) def forget(ui, repo, *pats, **opts): - """don't add the specified files on the next commit - + """don't add the specified files on the next commit (DEPRECATED) + + (DEPRECATED) Undo an 'hg add' scheduled for the next commit. + + This command is now deprecated and will be removed in a future + release. Please use revert instead. """ + ui.warn(_("(the forget command is deprecated; use revert instead)\n")) forget = [] for src, abs, rel, exact in walk(repo, pats, opts): if repo.dirstate.state(abs) == 'a': @@ -1792,9 +1840,7 @@ patches = (patch1,) + patches if not opts['force']: - modified, added, removed, deleted, unknown = repo.changes() - if modified or added or removed or deleted: - raise util.Abort(_("outstanding uncommitted changes")) + bail_if_changed(repo) d = opts["base"] strip = opts["strip"] @@ -2885,7 +2931,7 @@ ('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')), - 'archive': + "archive": (archive, [('', 'no-decode', None, _('do not pass files through decoders')), ('p', 'prefix', '', _('directory prefix for files in archive')), @@ -2894,6 +2940,17 @@ ('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], _('hg archive [OPTION]... DEST')), + "backout": + (backout, + [('', 'merge', None, + _('merge with old dirstate parent after backout')), + ('m', 'message', '', _('use <text> as commit message')), + ('l', 'logfile', '', _('read commit message from <file>')), + ('d', 'date', '', _('record datecode as commit date')), + ('u', 'user', '', _('record user as committer')), + ('I', 'include', [], _('include names matching the given patterns')), + ('X', 'exclude', [], _('exclude names matching the given patterns'))], + _('hg backout [OPTION]... REV')), "bundle": (bundle, [('f', 'force', None, @@ -2973,7 +3030,7 @@ ('a', 'text', None, _('treat all files as text')), ('', 'switch-parent', None, _('diff against the second parent'))], _('hg export [-a] [-o OUTFILESPEC] REV...')), - "forget": + "debugforget|forget": (forget, [('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], @@ -3255,11 +3312,8 @@ raise UnknownCommand(cmd) -class SignalInterrupt(Exception): - """Exception raised on SIGTERM and SIGHUP.""" - def catchterm(*args): - raise SignalInterrupt + raise util.SignalInterrupt def run(): sys.exit(dispatch(sys.argv[1:])) @@ -3311,7 +3365,7 @@ if num: signal.signal(num, catchterm) try: - u = ui.ui() + u = ui.ui(traceback='--traceback' in sys.argv[1:]) except util.Abort, inst: sys.stderr.write(_("abort: %s\n") % inst) return -1 @@ -3335,7 +3389,7 @@ external.append(mod) except Exception, inst: u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst)) - if "--traceback" in sys.argv[1:]: + if u.traceback: traceback.print_exc() return 1 continue @@ -3363,7 +3417,7 @@ atexit.register(print_time) u.updateopts(options["verbose"], options["debug"], options["quiet"], - not options["noninteractive"]) + not options["noninteractive"], options["traceback"]) # enter the debugger before command execution if options['debugger']: @@ -3430,7 +3484,7 @@ # enter the debugger when we hit an exception if options['debugger']: pdb.post_mortem(sys.exc_info()[2]) - if options['traceback']: + if u.traceback: traceback.print_exc() raise except ParseError, inst: @@ -3447,7 +3501,7 @@ u.warn(_("hg: unknown command '%s'\n") % inst.args[0]) help_(u, 'shortlist') except hg.RepoError, inst: - u.warn(_("abort: "), inst, "!\n") + u.warn(_("abort: %s!\n") % inst) except lock.LockHeld, inst: if inst.errno == errno.ETIMEDOUT: reason = _('timed out waiting for lock held by %s') % inst.locker @@ -3459,7 +3513,7 @@ (inst.desc or inst.filename, inst.strerror)) except revlog.RevlogError, inst: u.warn(_("abort: "), inst, "!\n") - except SignalInterrupt: + except util.SignalInterrupt: u.warn(_("killed!\n")) except KeyboardInterrupt: try:
--- a/mercurial/hgweb.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/hgweb.py Tue May 02 14:37:55 2006 -0700 @@ -125,7 +125,7 @@ def archivelist(self, nodeid): for i in self.archives: if self.repo.ui.configbool("web", "allow" + i, False): - yield {"type" : i, "node" : nodeid} + yield {"type" : i, "node" : nodeid, "url": ""} def listfiles(self, files, mf): for f in files[:self.maxfiles]: @@ -293,7 +293,8 @@ yield self.t('changelog', changenav=changenav, manifest=hex(mf), - rev=pos, changesets=count, entries=changelist) + rev=pos, changesets=count, entries=changelist, + archives=self.archivelist("tip")) def search(self, query): @@ -727,7 +728,9 @@ yield self.t("header", **map) def footer(**map): - yield self.t("footer", **map) + yield self.t("footer", + motd=self.repo.ui.config("web", "motd", ""), + **map) def expand_form(form): shortcuts = { @@ -1006,8 +1009,11 @@ def cleannames(items): return [(name.strip(os.sep), path) for name, path in items] + self.motd = "" + self.repos_sorted = ('name', False) if isinstance(config, (list, tuple)): self.repos = cleannames(config) + self.repos_sorted = ('', False) elif isinstance(config, dict): self.repos = cleannames(config.items()) self.repos.sort() @@ -1015,6 +1021,8 @@ cp = ConfigParser.SafeConfigParser() cp.read(config) self.repos = [] + if cp.has_section('web') and cp.has_option('web', 'motd'): + self.motd = cp.get('web', 'motd') if cp.has_section('paths'): self.repos.extend(cleannames(cp.items('paths'))) if cp.has_section('collections'): @@ -1032,14 +1040,20 @@ yield tmpl("header", **map) def footer(**map): - yield tmpl("footer", **map) + yield tmpl("footer", motd=self.motd, **map) m = os.path.join(templater.templatepath(), "map") tmpl = templater.templater(m, templater.common_filters, defaults={"header": header, "footer": footer}) - def entries(**map): + def archivelist(ui, nodeid, url): + for i in ['zip', 'gz', 'bz2']: + if ui.configbool("web", "allow" + i, False): + yield {"type" : i, "node": nodeid, "url": url} + + def entries(sortcolumn="", descending=False, **map): + rows = [] parity = 0 for name, path in self.repos: u = ui.ui() @@ -1058,16 +1072,37 @@ except OSError: continue - yield dict(contact=(get("ui", "username") or # preferred - get("web", "contact") or # deprecated - get("web", "author", "unknown")), # also - name=get("web", "name", name), + contact = (get("ui", "username") or # preferred + get("web", "contact") or # deprecated + get("web", "author", "")) # also + description = get("web", "description", "") + name = get("web", "name", name) + row = dict(contact=contact or "unknown", + contact_sort=contact.upper() or "unknown", + name=name, + name_sort=name, url=url, - parity=parity, - shortdesc=get("web", "description", "unknown"), - lastupdate=d) - - parity = 1 - parity + description=description or "unknown", + description_sort=description.upper() or "unknown", + lastchange=d, + lastchange_sort=d[1]-d[0], + archives=archivelist(u, "tip", url)) + if (not sortcolumn + or (sortcolumn, descending) == self.repos_sorted): + # fast path for unsorted output + row['parity'] = parity + parity = 1 - parity + yield row + else: + rows.append((row["%s_sort" % sortcolumn], row)) + if rows: + rows.sort() + if descending: + rows.reverse() + for key, row in rows: + row['parity'] = parity + parity = 1 - parity + yield row virtual = req.env.get("PATH_INFO", "").strip('/') if virtual: @@ -1088,4 +1123,20 @@ req.write(staticfile(static, fname) or tmpl("error", error="%r not found" % fname)) else: - req.write(tmpl("index", entries=entries)) + sortable = ["name", "description", "contact", "lastchange"] + sortcolumn, descending = self.repos_sorted + if req.form.has_key('sort'): + sortcolumn = req.form['sort'][0] + descending = sortcolumn.startswith('-') + if descending: + sortcolumn = sortcolumn[1:] + if sortcolumn not in sortable: + sortcolumn = "" + + sort = [("sort_%s" % column, + "%s%s" % ((not descending and column == sortcolumn) + and "-" or "", column)) + for column in sortable] + req.write(tmpl("index", entries=entries, + sortcolumn=sortcolumn, descending=descending, + **dict(sort)))
--- a/mercurial/httprangereader.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/httprangereader.py Tue May 02 14:37:55 2006 -0700 @@ -18,7 +18,11 @@ urllib2.install_opener(opener) req = urllib2.Request(self.url) end = '' - if bytes: end = self.pos + bytes + if bytes: + end = self.pos + bytes - 1 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end)) f = urllib2.urlopen(req) - return f.read() + data = f.read() + if bytes: + data = data[:bytes] + return data
--- a/mercurial/localrepo.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/localrepo.py Tue May 02 14:37:55 2006 -0700 @@ -11,7 +11,8 @@ from i18n import gettext as _ from demandload import * demandload(globals(), "appendfile changegroup") -demandload(globals(), "re lock transaction tempfile stat mdiff errno ui revlog") +demandload(globals(), "re lock transaction tempfile stat mdiff errno ui") +demandload(globals(), "revlog traceback") class localrepository(object): def __del__(self): @@ -42,7 +43,8 @@ pass v = self.ui.revlogopts - self.revlogversion = int(v.get('format', 0)) + self.revlogversion = int(v.get('format', revlog.REVLOGV0)) + self.revlogv1 = self.revlogversion != revlog.REVLOGV0 flags = 0 for x in v.get('flags', "").split(): flags |= revlog.flagstr(x) @@ -71,7 +73,59 @@ os.mkdir(self.join("data")) self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) + def hook(self, name, throw=False, **args): + def callhook(hname, funcname): + '''call python hook. hook is callable object, looked up as + name in python module. if callable returns "true", hook + passes, else fails. if hook raises exception, treated as + hook failure. exception propagates if throw is "true".''' + + self.ui.note(_("calling hook %s: %s\n") % (hname, funcname)) + d = funcname.rfind('.') + if d == -1: + raise util.Abort(_('%s hook is invalid ("%s" not in a module)') + % (hname, funcname)) + modname = funcname[:d] + try: + obj = __import__(modname) + except ImportError: + raise util.Abort(_('%s hook is invalid ' + '(import of "%s" failed)') % + (hname, modname)) + try: + for p in funcname.split('.')[1:]: + obj = getattr(obj, p) + except AttributeError, err: + raise util.Abort(_('%s hook is invalid ' + '("%s" is not defined)') % + (hname, funcname)) + if not callable(obj): + raise util.Abort(_('%s hook is invalid ' + '("%s" is not callable)') % + (hname, funcname)) + try: + r = obj(ui=ui, repo=repo, hooktype=name, **args) + except (KeyboardInterrupt, util.SignalInterrupt): + raise + except Exception, exc: + if isinstance(exc, util.Abort): + self.ui.warn(_('error: %s hook failed: %s\n') % + (hname, exc.args[0] % exc.args[1:])) + else: + self.ui.warn(_('error: %s hook raised an exception: ' + '%s\n') % (hname, exc)) + if throw: + raise + if self.ui.traceback: + traceback.print_exc() + return False + if not r: + if throw: + raise util.Abort(_('%s hook failed') % hname) + self.ui.warn(_('error: %s hook failed\n') % hname) + return r + def runhook(name, cmd): self.ui.note(_("running hook %s: %s\n") % (name, cmd)) env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] + @@ -90,7 +144,10 @@ if hname.split(".", 1)[0] == name and cmd] hooks.sort() for hname, cmd in hooks: - r = runhook(hname, cmd) and r + if cmd.startswith('python:'): + r = callhook(hname, cmd[7:].strip()) and r + else: + r = runhook(hname, cmd) and r return r def tags(self): @@ -1320,7 +1377,8 @@ # Signal that no more groups are left. yield changegroup.closechunk() - self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source) + if msng_cl_lst: + self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source) return util.chunkbuffer(gengroup()) @@ -1766,7 +1824,7 @@ def temp(prefix, node): pre = "%s~%s." % (os.path.basename(fn), prefix) - (fd, name) = tempfile.mkstemp("", pre) + (fd, name) = tempfile.mkstemp(prefix=pre) f = os.fdopen(fd, "wb") self.wwrite(fn, fl.read(node), f) f.close() @@ -1803,12 +1861,17 @@ filenodes = {} changesets = revisions = files = 0 errors = [0] + warnings = [0] neededmanifests = {} def err(msg): self.ui.warn(msg + "\n") errors[0] += 1 + def warn(msg): + self.ui.warn(msg + "\n") + warnings[0] += 1 + def checksize(obj, name): d = obj.checksize() if d[0]: @@ -1816,6 +1879,18 @@ if d[1]: err(_("%s index contains %d extra bytes") % (name, d[1])) + def checkversion(obj, name): + if obj.version != revlog.REVLOGV0: + if not revlogv1: + warn(_("warning: `%s' uses revlog format 1") % name) + elif revlogv1: + warn(_("warning: `%s' uses revlog format 0") % name) + + revlogv1 = self.revlogversion != revlog.REVLOGV0 + if self.ui.verbose or revlogv1 != self.revlogv1: + self.ui.status(_("repository uses revlog format %d\n") % + (revlogv1 and 1 or 0)) + seen = {} self.ui.status(_("checking changesets\n")) checksize(self.changelog, "changelog") @@ -1850,6 +1925,7 @@ seen = {} self.ui.status(_("checking manifests\n")) + checkversion(self.manifest, "manifest") checksize(self.manifest, "manifest") for i in range(self.manifest.count()): @@ -1914,6 +1990,7 @@ err(_("file without name in manifest %s") % short(n)) continue fl = self.file(f) + checkversion(fl, f) checksize(fl, f) nodes = {nullid: 1} @@ -1962,6 +2039,8 @@ self.ui.status(_("%d files, %d changesets, %d total revisions\n") % (files, changesets, revisions)) + if warnings[0]: + self.ui.warn(_("%d warnings encountered!\n") % warnings[0]) if errors[0]: self.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) return 1
--- a/mercurial/manifest.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/manifest.py Tue May 02 14:37:55 2006 -0700 @@ -12,7 +12,7 @@ demandload(globals(), "bisect array") class manifest(revlog): - def __init__(self, opener, defversion=0): + def __init__(self, opener, defversion=REVLOGV0): self.mapcache = None self.listcache = None revlog.__init__(self, opener, "00manifest.i", "00manifest.d",
--- a/mercurial/revlog.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/revlog.py Tue May 02 14:37:55 2006 -0700 @@ -293,7 +293,7 @@ remove data, and can use some simple techniques to avoid the need for locking while reading. """ - def __init__(self, opener, indexfile, datafile, defversion=0): + def __init__(self, opener, indexfile, datafile, defversion=REVLOGV0): """ create a revlog object @@ -333,11 +333,11 @@ and st.st_ctime == oldst.st_ctime): return self.indexstat = st - if len(i) > 0: - v = struct.unpack(versionformat, i)[0] + if len(i) > 0: + v = struct.unpack(versionformat, i)[0] flags = v & ~0xFFFF fmt = v & 0xFFFF - if fmt == 0: + if fmt == REVLOGV0: if flags: raise RevlogError(_("index %s invalid flags %x for format v0" % (self.indexfile, flags))) @@ -349,7 +349,7 @@ raise RevlogError(_("index %s invalid format %d" % (self.indexfile, fmt))) self.version = v - if v == 0: + if v == REVLOGV0: self.indexformat = indexformatv0 shaoffset = v0shaoffset else: @@ -369,7 +369,7 @@ # we've already got the entire data file read in, save it # in the chunk data self.chunkcache = (0, i) - if self.version != 0: + if self.version != REVLOGV0: e = list(self.index[0]) type = self.ngtype(e[0]) e[0] = self.offset_type(0, type) @@ -399,7 +399,7 @@ def ngoffset(self, q): if q & 0xFFFF: raise RevlogError(_('%s: incompatible revision flag %x') % - (self.indexfile, type)) + (self.indexfile, q)) return long(q >> 16) def ngtype(self, q): @@ -441,13 +441,13 @@ if node == nullid: return (nullid, nullid) r = self.rev(node) d = self.index[r][-3:-1] - if self.version == 0: + if self.version == REVLOGV0: return d return [ self.node(x) for x in d ] def start(self, rev): if rev < 0: return -1 - if self.version != 0: + if self.version != REVLOGV0: return self.ngoffset(self.index[rev][0]) return self.index[rev][0] @@ -456,7 +456,7 @@ def size(self, rev): """return the length of the uncompressed text for a given revision""" l = -1 - if self.version != 0: + if self.version != REVLOGV0: l = self.index[rev][2] if l >= 0: return l @@ -911,7 +911,7 @@ if t >= 0: offset = self.end(t) - if self.version == 0: + if self.version == REVLOGV0: e = (offset, l, base, link, p1, p2, node) else: e = (self.offset_type(offset, 0), l, len(text), @@ -935,7 +935,7 @@ f.seek(0, 2) transaction.add(self.indexfile, f.tell(), self.count() - 1) - if len(self.index) == 1 and self.version != 0: + if len(self.index) == 1 and self.version != REVLOGV0: l = struct.pack(versionformat, self.version) f.write(l) entry = entry[4:] @@ -1135,7 +1135,7 @@ raise RevlogError(_("consistency error adding group")) textlen = len(text) else: - if self.version == 0: + if self.version == REVLOGV0: e = (end, len(cdelta), base, link, p1, p2, node) else: e = (self.offset_type(end, 0), len(cdelta), textlen, base,
--- a/mercurial/sshrepo.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/sshrepo.py Tue May 02 14:37:55 2006 -0700 @@ -9,7 +9,7 @@ from remoterepo import * from i18n import gettext as _ from demandload import * -demandload(globals(), "hg os re stat") +demandload(globals(), "hg os re stat util") class sshrepository(remoterepository): def __init__(self, ui, path):
--- a/mercurial/ui.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/ui.py Tue May 02 14:37:55 2006 -0700 @@ -12,7 +12,7 @@ class ui(object): def __init__(self, verbose=False, debug=False, quiet=False, - interactive=True, parentui=None): + interactive=True, traceback=False, parentui=None): self.overlay = {} if parentui is None: # this is the parent of all ui children @@ -24,6 +24,7 @@ self.verbose = self.configbool("ui", "verbose") self.debugflag = self.configbool("ui", "debug") self.interactive = self.configbool("ui", "interactive", True) + self.traceback = traceback self.updateopts(verbose, debug, quiet, interactive) self.diffcache = None @@ -45,11 +46,12 @@ return getattr(self.parentui, key) def updateopts(self, verbose=False, debug=False, quiet=False, - interactive=True): + interactive=True, traceback=False): self.quiet = (self.quiet or quiet) and not verbose and not debug self.verbose = (self.verbose or verbose) or debug self.debugflag = (self.debugflag or debug) self.interactive = (self.interactive and interactive) + self.traceback = self.traceback or traceback def readconfig(self, fn, root=None): if isinstance(fn, basestring):
--- a/mercurial/util.py Tue May 02 14:30:00 2006 -0700 +++ b/mercurial/util.py Tue May 02 14:37:55 2006 -0700 @@ -16,6 +16,9 @@ demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile") demandload(globals(), "threading time") +class SignalInterrupt(Exception): + """Exception raised on SIGTERM and SIGHUP.""" + def pipefilter(s, cmd): '''filter string S through command CMD, returning its output''' (pout, pin) = popen2.popen2(cmd, -1, 'b') @@ -43,11 +46,11 @@ the temporary files generated.''' inname, outname = None, None try: - infd, inname = tempfile.mkstemp(prefix='hgfin') + infd, inname = tempfile.mkstemp(prefix='hg-filter-in-') fp = os.fdopen(infd, 'wb') fp.write(s) fp.close() - outfd, outname = tempfile.mkstemp(prefix='hgfout') + outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-') os.close(outfd) cmd = cmd.replace('INFILE', inname) cmd = cmd.replace('OUTFILE', outname) @@ -676,7 +679,7 @@ def mktempcopy(name): d, fn = os.path.split(name) - fd, temp = tempfile.mkstemp(prefix=fn, dir=d) + fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d) os.close(fd) fp = posixfile(temp, "wb") try:
--- a/templates/changelog.tmpl Tue May 02 14:30:00 2006 -0700 +++ b/templates/changelog.tmpl Tue May 02 14:37:55 2006 -0700 @@ -8,6 +8,7 @@ <div class="buttons"> <a href="?cmd=tags">tags</a> <a href="?mf=#manifest|short#;path=/">manifest</a> +#archives%archiveentry# <a type="application/rss+xml" href="?style=rss">rss</a> </div>
--- a/templates/changelogentry.tmpl Tue May 02 14:30:00 2006 -0700 +++ b/templates/changelogentry.tmpl Tue May 02 14:37:55 2006 -0700 @@ -1,11 +1,11 @@ -<table class="changelogEntry parity#parity#"> +<table class="logEntry parity#parity#"> <tr> - <th class="age" width="15%">#date|age# ago:</th> + <th class="age">#date|age# ago:</th> <th class="firstline">#desc|strip|firstline|escape#</th> </tr> <tr> - <th class="changesetRev">changeset #rev#:</th> - <td class="changesetNode"><a href="?cs=#node|short#">#node|short#</a></td> + <th class="revision">changeset #rev#:</th> + <td class="node"><a href="?cs=#node|short#">#node|short#</a></td> </tr> #parent%changelogparent# #child%changelogchild#
--- a/templates/filelogentry.tmpl Tue May 02 14:30:00 2006 -0700 +++ b/templates/filelogentry.tmpl Tue May 02 14:37:55 2006 -0700 @@ -1,20 +1,25 @@ -<table class="parity#parity#" width="100%" cellspacing="0" cellpadding="0"> -<tr> - <td align="right" width="15%"><b>#date|age# ago: </b></td> - <td><b><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></b></td></tr> -<tr> - <td align="right">revision #filerev#: </td> - <td><a href="?f=#filenode|short#;file=#file|urlescape#">#filenode|short#</a> -<a href="?fd=#node|short#;file=#file|urlescape#">(diff)</a> -<a href="?fa=#filenode|short#;file=#file|urlescape#">(annotate)</a> -</td></tr> -#rename%filelogrename# -<tr> - <td align="right">author: </td> - <td>#author|obfuscate#</td></tr> -<tr> - <td align="right">date: </td> - <td>#date|date# (#date|age# ago)</td></tr> +<table class="logEntry parity#parity#"> + <tr> + <th class="age">#date|age# ago:</th> + <th class="firstline"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></th> + </tr> + <tr> + <th class="revision">revision #filerev#:</td> + <td class="node"> + <a href="?f=#filenode|short#;file=#file|urlescape#">#filenode|short#</a> + <a href="?fd=#node|short#;file=#file|urlescape#">(diff)</a> + <a href="?fa=#filenode|short#;file=#file|urlescape#">(annotate)</a> + </td> + </tr> + #rename%filelogrename# + <tr> + <th class="author">author:</th> + <td class="author">#author|obfuscate#</td> + </tr> + <tr> + <th class="date">date:</th> + <td class="date">#date|date#</td> + </tr> </table>
--- a/templates/footer.tmpl Tue May 02 14:30:00 2006 -0700 +++ b/templates/footer.tmpl Tue May 02 14:37:55 2006 -0700 @@ -1,3 +1,4 @@ +#motd# <div class="logo"> powered by<br/> <a href="http://www.selenic.com/mercurial/">mercurial</a>
--- a/templates/index.tmpl Tue May 02 14:30:00 2006 -0700 +++ b/templates/index.tmpl Tue May 02 14:37:55 2006 -0700 @@ -7,10 +7,10 @@ <table> <tr> - <td>Name</td> - <td>Description</td> - <td>Contact</td> - <td>Last change</td> + <td><a href="?sort=#sort_name#">Name</a></td> + <td><a href="?sort=#sort_description#">Description</a></td> + <td><a href="?sort=#sort_contact#">Contact</a></td> + <td><a href="?sort=#sort_lastchange#">Last change</a></td> <td> </td> <tr> #entries%indexentry#
--- a/templates/map Tue May 02 14:30:00 2006 -0700 +++ b/templates/map Tue May 02 14:37:55 2006 -0700 @@ -28,23 +28,23 @@ changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>' filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>' -filelogrename = '<tr><td align="right">base: </td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>' +filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>' fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#filenode|short#;file=#file|urlescape#">#node|short#</a></td></tr>' changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#filenode|short#;file=#file|urlescape#">#node|short#</a></td></tr>' tags = tags.tmpl -tagentry = '<li class="tagEntry parity#parity#"><span class="node">#node#</span> <a href="?cs=#node|short#">#tag|escape#</a></li>' +tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>' diffblock = '<pre class="parity#parity#">#lines#</pre>' changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>' changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>' filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filelogparent = '<tr><td align="right">parent #rev#: </td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' +filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filelogchild = '<tr><td align="right">child #rev#: </td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#shortdesc#</td><td>#contact|obfuscate#</td><td>#lastupdate|age# ago</td><td><a href="#url#?cl=tip;style=rss">RSS</a></td></tr>' +filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' +indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> #archives%archiveentry#</td></tr>' index = index.tmpl -archiveentry = '<a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> ' +archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> ' notfound = notfound.tmpl error = error.tmpl
--- a/templates/map-gitweb Tue May 02 14:30:00 2006 -0700 +++ b/templates/map-gitweb Tue May 02 14:37:55 2006 -0700 @@ -36,7 +36,7 @@ filerevchild = '<tr><td class="metatag">child:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>' fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>' tags = tags-gitweb.tmpl -tagentry = '<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>' +tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>' diffblock = '#lines#' changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>' changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>' @@ -45,6 +45,6 @@ filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>' filelogchild = '<tr><td align="right">child #rev#: </td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>' shortlog = shortlog-gitweb.tmpl -shortlogentry = '<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>' -filelogentry = '<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>' +shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>' +filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>' archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
--- a/templates/static/style-gitweb.css Tue May 02 14:30:00 2006 -0700 +++ b/templates/static/style-gitweb.css Tue May 02 14:37:55 2006 -0700 @@ -17,6 +17,7 @@ a.title:hover { background-color: #d9d8d1; } div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; } div.log_body { padding:8px 8px 8px 150px; } +.age { white-space:nowrap; } span.age { position:relative; float:left; width:142px; font-style:italic; } div.log_link { padding:0px 8px;
--- a/templates/static/style.css Tue May 02 14:30:00 2006 -0700 +++ b/templates/static/style.css Tue May 02 14:37:55 2006 -0700 @@ -1,4 +1,6 @@ a { text-decoration:none; } +.age { white-space:nowrap; } +.indexlinks { white-space:nowrap; } .parity0 { background-color: #dddddd; } .parity1 { background-color: #eeeeee; } .lineno { width: 60px; color: #aaaaaa; font-size: smaller; @@ -48,16 +50,16 @@ color: #999; } -/* Changelog entries */ -.changelogEntry { width: 100%; } -.changelogEntry th { font-weight: normal; text-align: right; vertical-align: top; } -.changelogEntry th.age, .changelogEntry th.firstline { font-weight: bold; } -.changelogEntry th.firstline { text-align: left; width: inherit; } +/* Changelog/Filelog entries */ +.logEntry { width: 100%; } +.logEntry .age { width: 15%; } +.logEntry th { font-weight: normal; text-align: right; vertical-align: top; } +.logEntry th.age, .logEntry th.firstline { font-weight: bold; } +.logEntry th.firstline { text-align: left; width: inherit; } /* Tag entries */ #tagEntries { list-style: none; margin: 0; padding: 0; } #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; } -#tagEntries .tagEntry span.node { font-family: monospace; } /* Changeset entry */ #changesetEntry { }
--- a/tests/coverage.py Tue May 02 14:30:00 2006 -0700 +++ b/tests/coverage.py Tue May 02 14:37:55 2006 -0700 @@ -350,6 +350,7 @@ show_missing = settings.get('show-missing') directory = settings.get('directory') omit = filter(None, settings.get('omit', '').split(',')) + omit += ['/<'] # Always skip /<string> etc. if settings.get('report'): self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
--- a/tests/run-tests Tue May 02 14:30:00 2006 -0700 +++ b/tests/run-tests Tue May 02 14:37:55 2006 -0700 @@ -64,7 +64,7 @@ exit 1 } -TESTDIR="$PWD" +TESTDIR="`pwd`" export TESTDIR INST="$HGTMP/install" PYTHONDIR="$INST/lib/python"
--- a/tests/run-tests.py Tue May 02 14:30:00 2006 -0700 +++ b/tests/run-tests.py Tue May 02 14:37:55 2006 -0700 @@ -15,11 +15,18 @@ required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] -parser = OptionParser() -parser.add_option("-v", "--verbose", action="store_true", dest="verbose", - default=False, help="output verbose messages") +parser = OptionParser("%prog [options] [tests]") +parser.add_option("-v", "--verbose", action="store_true", + help="output verbose messages") +parser.add_option("-c", "--cover", action="store_true", + help="print a test coverage report") +parser.add_option("-s", "--cover_stdlib", action="store_true", + help="print a test coverage report inc. standard libraries") +parser.add_option("-C", "--annotate", action="store_true", + help="output files annotated with coverage") (options, args) = parser.parse_args() verbose = options.verbose +coverage = options.cover or options.cover_stdlib or options.annotate def vlog(*msg): if verbose: @@ -40,66 +47,82 @@ return name return None -# Before we go any further, check for pre-requisite tools -# stuff from coreutils (cat, rm, etc) are not tested -for p in required_tools: - if os.name == 'nt': - p += '.exe' - found = find_program(p) - if found: - vlog("# Found prerequisite", p, "at", found) - else: - print "WARNING: Did not find prerequisite tool: "+p - -# Reset some environment variables to well-known values -os.environ['LANG'] = os.environ['LC_ALL'] = 'C' -os.environ['TZ'] = 'GMT' - -os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' -os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"' -os.environ["HGUSER"] = "test" -os.environ["HGRCPATH"] = "" - -TESTDIR = os.environ["TESTDIR"] = os.getcwd() -HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") +def check_required_tools(): + # Before we go any further, check for pre-requisite tools + # stuff from coreutils (cat, rm, etc) are not tested + for p in required_tools: + if os.name == 'nt': + p += '.exe' + found = find_program(p) + if found: + vlog("# Found prerequisite", p, "at", found) + else: + print "WARNING: Did not find prerequisite tool: "+p def cleanup_exit(): if verbose: print "# Cleaning up HGTMP", HGTMP shutil.rmtree(HGTMP, True) -vlog("# Using TESTDIR", TESTDIR) -vlog("# Using HGTMP", HGTMP) - -os.umask(022) +def install_hg(): + vlog("# Performing temporary installation of HG") + installerrs = os.path.join("tests", "install.err") -vlog("# Performing temporary installation of HG") -INST = os.path.join(HGTMP, "install") -BINDIR = os.path.join(INST, "bin") -PYTHONDIR = os.path.join(INST, "lib", "python") -installerrs = os.path.join("tests", "install.err") + os.chdir("..") # Get back to hg root + cmd = '%s setup.py install --home="%s" --install-lib="%s" >%s 2>&1' % \ + (sys.executable, INST, PYTHONDIR, installerrs) + vlog("# Running", cmd) + if os.system(cmd) == 0: + if not verbose: + os.remove(installerrs) + else: + f = open(installerrs) + for line in f: + print line, + f.close() + sys.exit(1) + os.chdir(TESTDIR) + + os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"]) + os.environ["PYTHONPATH"] = PYTHONDIR -os.chdir("..") # Get back to hg root -cmd = '%s setup.py install --home="%s" --install-lib="%s" >%s 2>&1' % \ - (sys.executable, INST, PYTHONDIR, installerrs) -vlog("# Running", cmd) -if os.system(cmd) == 0: - if not verbose: - os.remove(installerrs) -else: - f = open(installerrs) - for line in f: - print line, - f.close() - cleanup_exit() - sys.exit(1) -os.chdir(TESTDIR) + if coverage: + vlog("# Installing coverage wrapper") + os.environ['COVERAGE_FILE'] = COVERAGE_FILE + if os.path.exists(COVERAGE_FILE): + os.unlink(COVERAGE_FILE) + # Create a wrapper script to invoke hg via coverage.py + os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py")) + f = open(os.path.join(BINDIR, 'hg'), 'w') + f.write('#!' + sys.executable + '\n') + f.write('import sys, os; os.execv(sys.executable, [sys.executable, '+ \ + '"%s", "-x", "%s"] + sys.argv[1:])\n' % ( + os.path.join(TESTDIR, 'coverage.py'), + os.path.join(BINDIR, '_hg.py'))) + f.close() + os.chmod(os.path.join(BINDIR, 'hg'), 0700) -os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"]) -os.environ["PYTHONPATH"] = PYTHONDIR - -tests = 0 -failed = 0 +def output_coverage(): + vlog("# Producing coverage report") + omit = [BINDIR, TESTDIR, PYTHONDIR] + if not options.cover_stdlib: + # Exclude as system paths (ignoring empty strings seen on win) + omit += [x for x in sys.path if x != ''] + omit = ','.join(omit) + os.chdir(PYTHONDIR) + cmd = '"%s" "%s" -r "--omit=%s"' % ( + sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit) + vlog("# Running: "+cmd) + os.system(cmd) + if options.annotate: + adir = os.path.join(TESTDIR, 'annotated') + if not os.path.isdir(adir): + os.mkdir(adir) + cmd = '"%s" "%s" -a "--directory=%s" "--omit=%s"' % ( + sys.executable, os.path.join(TESTDIR, 'coverage.py'), + adir, omit) + vlog("# Running: "+cmd) + os.system(cmd) def run(cmd, split_lines=True): """Run command in a sub-process, capturing the output (stdout and stderr). @@ -176,17 +199,52 @@ shutil.rmtree(tmpd, True) return ret == 0 -for test in os.listdir("."): - if test.startswith("test-"): - if '~' in test or re.search(r'\.(out|err)$', test): - continue - if not run_one(test): - failed += 1 - tests += 1 + +os.umask(022) + +check_required_tools() + +# Reset some environment variables to well-known values so that +# the tests produce repeatable output. +os.environ['LANG'] = os.environ['LC_ALL'] = 'C' +os.environ['TZ'] = 'GMT' + +os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' +os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"' +os.environ["HGUSER"] = "test" +os.environ["HGRCPATH"] = "" + +TESTDIR = os.environ["TESTDIR"] = os.getcwd() +HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") +vlog("# Using TESTDIR", TESTDIR) +vlog("# Using HGTMP", HGTMP) -print "# Ran %d tests, %d failed." % (tests, failed) +INST = os.path.join(HGTMP, "install") +BINDIR = os.path.join(INST, "bin") +PYTHONDIR = os.path.join(INST, "lib", "python") +COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") + +try: + install_hg() + + tests = 0 + failed = 0 -cleanup_exit() + if len(args) == 0: + args = os.listdir(".") + for test in args: + if test.startswith("test-"): + if '~' in test or re.search(r'\.(out|err)$', test): + continue + if not run_one(test): + failed += 1 + tests += 1 + + print "\n# Ran %d tests, %d failed." % (tests, failed) + if coverage: + output_coverage() +finally: + cleanup_exit() if failed: sys.exit(1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-backout Tue May 02 14:37:55 2006 -0700 @@ -0,0 +1,51 @@ +#!/bin/sh + +echo '# basic operation' +hg init basic +cd basic +echo a > a +hg commit -d '0 0' -A -m a +echo b >> a +hg commit -d '1 0' -m b + +hg backout -d '2 0' tip +cat a + +echo '# file that was removed is recreated' +cd .. +hg init remove +cd remove + +echo content > a +hg commit -d '0 0' -A -m a + +hg rm a +hg commit -d '1 0' -m b + +hg backout -d '2 0' --merge tip +cat a + +echo '# backout of backout is as if nothing happened' + +hg backout -d '3 0' --merge tip +cat a + +echo '# backout with merge' +cd .. +hg init merge +cd merge + +echo line 1 > a +hg commit -d '0 0' -A -m a + +echo line 2 >> a +hg commit -d '1 0' -m b + +echo line 3 >> a +hg commit -d '2 0' -m c + +hg backout --merge -d '3 0' 1 +hg commit -d '4 0' -m d +cat a + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-backout.out Tue May 02 14:37:55 2006 -0700 @@ -0,0 +1,19 @@ +# basic operation +adding a +changeset 2:b38a34ddfd9f backs out changeset 1:a820f4f40a57 +a +# file that was removed is recreated +adding a +adding a +changeset 2:44cd84c7349a backs out changeset 1:76862dcce372 +content +# backout of backout is as if nothing happened +removing a +changeset 3:0dd8a0ed5e99 backs out changeset 2:44cd84c7349a +cat: a: No such file or directory +# backout with merge +adding a +changeset 3:6c77ecc28460 backs out changeset 1:314f55b1bf23 +merging with changeset 2:b66ea5b77abb +merging a +line 1
--- a/tests/test-help.out Tue May 02 14:30:00 2006 -0700 +++ b/tests/test-help.out Tue May 02 14:37:55 2006 -0700 @@ -42,6 +42,7 @@ addremove add all new files, delete all missing files annotate show changeset information per file line archive create unversioned archive of a repository revision + backout reverse effect of earlier changeset bundle create a changegroup file cat output the latest or given revisions of files clone make a copy of an existing repository @@ -49,7 +50,6 @@ copy mark files as copied for the next commit diff diff repository (or selected files) export dump the header and diffs for one or more changesets - forget don't add the specified files on the next commit grep search for a pattern in specified files and revisions heads show current repository heads help show help for a given command or all commands @@ -85,6 +85,7 @@ addremove add all new files, delete all missing files annotate show changeset information per file line archive create unversioned archive of a repository revision + backout reverse effect of earlier changeset bundle create a changegroup file cat output the latest or given revisions of files clone make a copy of an existing repository @@ -92,7 +93,6 @@ copy mark files as copied for the next commit diff diff repository (or selected files) export dump the header and diffs for one or more changesets - forget don't add the specified files on the next commit grep search for a pattern in specified files and revisions heads show current repository heads help show help for a given command or all commands
--- a/tests/test-hook Tue May 02 14:30:00 2006 -0700 +++ b/tests/test-hook Tue May 02 14:37:55 2006 -0700 @@ -87,4 +87,93 @@ echo 'preoutgoing.forbid = echo preoutgoing.forbid hook; exit 1' >> ../a/.hg/hgrc hg pull ../a +cat > hooktests.py <<EOF +from mercurial import util + +uncallable = 0 + +def printargs(args): + args.pop('ui', None) + args.pop('repo', None) + a = list(args.items()) + a.sort() + print 'hook args:' + for k, v in a: + print ' ', k, v + return True + +def passhook(**args): + printargs(args) + return True + +def failhook(**args): + printargs(args) + +class LocalException(Exception): + pass + +def raisehook(**args): + raise LocalException('exception from hook') + +def aborthook(**args): + raise util.Abort('raise abort from hook') + +def brokenhook(**args): + return 1 + {} + +class container: + unreachable = 1 +EOF + +echo '# test python hooks' +PYTHONPATH="`pwd`:$PYTHONPATH" +export PYTHONPATH + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc +hg pull ../a 2>&1 | grep 'raised an exception' + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc +hg pull ../a 2>&1 | grep 'raised an exception' + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc +hg pull ../a + +echo '[hooks]' > ../a/.hg/hgrc +echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc +hg pull ../a + +echo '# make sure --traceback works' +echo '[hooks]' > .hg/hgrc +echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc + +echo a >> a +hg --traceback commit -A -m a 2>&1 | grep '^Traceback' + exit 0
--- a/tests/test-hook.out Tue May 02 14:30:00 2006 -0700 +++ b/tests/test-hook.out Tue May 02 14:37:55 2006 -0700 @@ -89,3 +89,43 @@ pulling from ../a searching for changes abort: preoutgoing.forbid hook exited with status 1 +# test python hooks +error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict' +error: preoutgoing.raise hook raised an exception: exception from hook +pulling from ../a +searching for changes +error: preoutgoing.abort hook failed: raise abort from hook +abort: raise abort from hook +pulling from ../a +searching for changes +hook args: + hooktype preoutgoing + source pull +abort: preoutgoing.fail hook failed +pulling from ../a +searching for changes +abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable) +pulling from ../a +searching for changes +abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined) +pulling from ../a +searching for changes +abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module) +pulling from ../a +searching for changes +abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed) +pulling from ../a +searching for changes +abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed) +pulling from ../a +searching for changes +hook args: + hooktype preoutgoing + source pull +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy) +# make sure --traceback works +Traceback (most recent call last):
--- a/tests/test-nested-repo Tue May 02 14:30:00 2006 -0700 +++ b/tests/test-nested-repo Tue May 02 14:37:55 2006 -0700 @@ -14,6 +14,6 @@ echo '# should print A b/x' hg st echo '# should forget b/x' -hg forget +hg revert echo '# should print nothing' hg st b
--- a/tests/test-revert Tue May 02 14:30:00 2006 -0700 +++ b/tests/test-revert Tue May 02 14:37:55 2006 -0700 @@ -54,4 +54,18 @@ hg revert -r0 a hg st a +hg update -C +chmod +x c +hg revert +echo %% should print non-executable +test -x c || echo non-executable + +chmod +x c +hg commit -d '1000001 0' -m exe + +chmod -x c +hg revert +echo %% should print executable +test -x c && echo executable + true