Mercurial > hg
view mercurial/verify.py @ 27764:dd0c5f4d1b53
util: adjust 'datapath' to be correct in a frozen OS X package
Apparently unlike py2exe, py2app copies the Mercurial source tree as-is to a
Contents/Resources subdirectory of an app bundle, and places its binary stub in
Contents/MacOS. (The Windows install has the 'hgext' and 'mercurial' modules in
'lib/library.zip', while the help and templates subdirectories have been moved
out of the mercurial directory to the root of the installation. I assume that
the python code living in a zip file is why "py2exe doesn't support __file__".)
Therefore, prior to this change, Mercurial in a frozen app bundle on OS X would
go looking for help *.txt, templates and locale info in Contents/MacOS, where
they don't exist.
There are only a handful of places that test for frozen, and not all of them are
wrong for OS X, so it seems wiser to handle them on a case by case basis, rather
that try to change mainfrozen(). The remaining cases are:
1) util.hgexecutable() wrongly points to the bundled python executable, and
affects $HG in util.system() launched processes (e.g. external hooks)
2) util.hgcmd() wrongly points to the bundled python executable, but it seems
to only affect 'hg serve -d'
3) hook._pythonhook() may be OK, since I didn't see anything outrageous when
printing sys.path from an internal hook. I'm not sure if this special
case is needed on OS X though.
4) sslutil._plainapplepython() is OK, because sys.executable is not
/usr/bin/python, nor is it in /System/Library/Frameworks
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Sun, 10 Jan 2016 17:49:01 -0500 |
parents | fb0cc863d172 |
children | 900d36a3e4dd |
line wrap: on
line source
# verify.py - repository integrity checking for Mercurial # # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import import os from .i18n import _ from .node import ( nullid, short, ) from . import ( error, revlog, util, ) def verify(repo): lock = repo.lock() try: return verifier(repo).verify() finally: lock.release() def _normpath(f): # under hg < 2.4, convert didn't sanitize paths properly, so a # converted repo may contain repeated slashes while '//' in f: f = f.replace('//', '/') return f def _validpath(repo, path): """Returns False if a path should NOT be treated as part of a repo. For all in-core cases, this returns True, as we have no way for a path to be mentioned in the history but not actually be relevant. For narrow clones, this is important because many filelogs will be missing, and changelog entries may mention modified files that are outside the narrow scope. """ return True class verifier(object): def __init__(self, repo): self.repo = repo.unfiltered() self.ui = repo.ui self.badrevs = set() self.errors = 0 self.warnings = 0 self.havecl = len(repo.changelog) > 0 self.havemf = len(repo.manifest) > 0 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0 self.lrugetctx = util.lrucachefunc(repo.changectx) self.refersmf = False self.fncachewarned = False def warn(self, msg): self.ui.warn(msg + "\n") self.warnings += 1 def err(self, linkrev, msg, filename=None): if linkrev is not None: self.badrevs.add(linkrev) else: linkrev = '?' msg = "%s: %s" % (linkrev, msg) if filename: msg = "%s@%s" % (filename, msg) self.ui.warn(" " + msg + "\n") self.errors += 1 def exc(self, linkrev, msg, inst, filename=None): if not str(inst): inst = repr(inst) self.err(linkrev, "%s: %s" % (msg, inst), filename) def checklog(self, obj, name, linkrev): if not len(obj) and (self.havecl or self.havemf): self.err(linkrev, _("empty or missing %s") % name) return d = obj.checksize() if d[0]: self.err(None, _("data length off by %d bytes") % d[0], name) if d[1]: self.err(None, _("index contains %d extra bytes") % d[1], name) if obj.version != revlog.REVLOGV0: if not self.revlogv1: self.warn(_("warning: `%s' uses revlog format 1") % name) elif self.revlogv1: self.warn(_("warning: `%s' uses revlog format 0") % name) def checkentry(self, obj, i, node, seen, linkrevs, f): lr = obj.linkrev(obj.rev(node)) if lr < 0 or (self.havecl and lr not in linkrevs): if lr < 0 or lr >= len(self.repo.changelog): msg = _("rev %d points to nonexistent changeset %d") else: msg = _("rev %d points to unexpected changeset %d") self.err(None, msg % (i, lr), f) if linkrevs: if f and len(linkrevs) > 1: try: # attempt to filter down to real linkrevs linkrevs = [l for l in linkrevs if self.lrugetctx(l)[f].filenode() == node] except Exception: pass self.warn(_(" (expected %s)") % " ".join(map(str, linkrevs))) lr = None # can't be trusted try: p1, p2 = obj.parents(node) if p1 not in seen and p1 != nullid: self.err(lr, _("unknown parent 1 %s of %s") % (short(p1), short(node)), f) if p2 not in seen and p2 != nullid: self.err(lr, _("unknown parent 2 %s of %s") % (short(p2), short(node)), f) except Exception as inst: self.exc(lr, _("checking parents of %s") % short(node), inst, f) if node in seen: self.err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f) seen[node] = i return lr def verify(self): repo = self.repo ui = repo.ui if not repo.url().startswith('file:'): raise error.Abort(_("cannot verify bundle or remote repos")) if os.path.exists(repo.sjoin("journal")): ui.warn(_("abandoned transaction found - run hg recover\n")) if ui.verbose or not self.revlogv1: ui.status(_("repository uses revlog format %d\n") % (self.revlogv1 and 1 or 0)) mflinkrevs, filelinkrevs = self._verifychangelog() filenodes = self._verifymanifest(mflinkrevs) self._crosscheckfiles(mflinkrevs, filelinkrevs, filenodes) totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs) ui.status(_("%d files, %d changesets, %d total revisions\n") % (totalfiles, len(repo.changelog), filerevisions)) if self.warnings: ui.warn(_("%d warnings encountered!\n") % self.warnings) if self.fncachewarned: ui.warn(_('hint: run "hg debugrebuildfncache" to recover from ' 'corrupt fncache\n')) if self.errors: ui.warn(_("%d integrity errors encountered!\n") % self.errors) if self.badrevs: ui.warn(_("(first damaged changeset appears to be %d)\n") % min(self.badrevs)) return 1 def _verifychangelog(self): ui = self.ui repo = self.repo cl = repo.changelog ui.status(_("checking changesets\n")) mflinkrevs = {} filelinkrevs = {} seen = {} self.checklog(cl, "changelog", 0) total = len(repo) for i in repo: ui.progress(_('checking'), i, total=total, unit=_('changesets')) n = cl.node(i) self.checkentry(cl, i, n, seen, [i], "changelog") try: changes = cl.read(n) if changes[0] != nullid: mflinkrevs.setdefault(changes[0], []).append(i) self.refersmf = True for f in changes[3]: if _validpath(repo, f): filelinkrevs.setdefault(_normpath(f), []).append(i) except Exception as inst: self.refersmf = True self.exc(i, _("unpacking changeset %s") % short(n), inst) ui.progress(_('checking'), None) return mflinkrevs, filelinkrevs def _verifymanifest(self, mflinkrevs): repo = self.repo ui = self.ui mf = self.repo.manifest ui.status(_("checking manifests\n")) filenodes = {} seen = {} if self.refersmf: # Do not check manifest if there are only changelog entries with # null manifests. self.checklog(mf, "manifest", 0) total = len(mf) for i in mf: ui.progress(_('checking'), i, total=total, unit=_('manifests')) n = mf.node(i) lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest") if n in mflinkrevs: del mflinkrevs[n] else: self.err(lr, _("%s not in changesets") % short(n), "manifest") try: for f, fn in mf.readdelta(n).iteritems(): if not f: self.err(lr, _("file without name in manifest")) elif f != "/dev/null": # ignore this in very old repos if _validpath(repo, f): filenodes.setdefault( _normpath(f), {}).setdefault(fn, lr) except Exception as inst: self.exc(lr, _("reading manifest delta %s") % short(n), inst) ui.progress(_('checking'), None) return filenodes def _crosscheckfiles(self, mflinkrevs, filelinkrevs, filenodes): repo = self.repo ui = self.ui ui.status(_("crosschecking files in changesets and manifests\n")) total = len(mflinkrevs) + len(filelinkrevs) + len(filenodes) count = 0 if self.havemf: for c, m in sorted([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]): count += 1 if m == nullid: continue ui.progress(_('crosschecking'), count, total=total) self.err(c, _("changeset refers to unknown manifest %s") % short(m)) mflinkrevs = None # del is bad here due to scope issues for f in sorted(filelinkrevs): count += 1 ui.progress(_('crosschecking'), count, total=total) if f not in filenodes: lr = filelinkrevs[f][0] self.err(lr, _("in changeset but not in manifest"), f) if self.havecl: for f in sorted(filenodes): count += 1 ui.progress(_('crosschecking'), count, total=total) if f not in filelinkrevs: try: fl = repo.file(f) lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]]) except Exception: lr = None self.err(lr, _("in manifest but not in changeset"), f) ui.progress(_('crosschecking'), None) def _verifyfiles(self, filenodes, filelinkrevs): repo = self.repo ui = self.ui lrugetctx = self.lrugetctx revlogv1 = self.revlogv1 havemf = self.havemf ui.status(_("checking files\n")) storefiles = set() for f, f2, size in repo.store.datafiles(): if not f: self.err(None, _("cannot decode filename '%s'") % f2) elif size > 0 or not revlogv1: storefiles.add(_normpath(f)) files = sorted(set(filenodes) | set(filelinkrevs)) total = len(files) revisions = 0 for i, f in enumerate(files): ui.progress(_('checking'), i, item=f, total=total) try: linkrevs = filelinkrevs[f] except KeyError: # in manifest but not in changelog linkrevs = [] if linkrevs: lr = linkrevs[0] else: lr = None try: fl = repo.file(f) except error.RevlogError as e: self.err(lr, _("broken revlog! (%s)") % e, f) continue for ff in fl.files(): try: storefiles.remove(ff) except KeyError: self.warn(_(" warning: revlog '%s' not in fncache!") % ff) self.fncachewarned = True self.checklog(fl, f, lr) seen = {} rp = None for i in fl: revisions += 1 n = fl.node(i) lr = self.checkentry(fl, i, n, seen, linkrevs, f) if f in filenodes: if havemf and n not in filenodes[f]: self.err(lr, _("%s not in manifests") % (short(n)), f) else: del filenodes[f][n] # verify contents try: l = len(fl.read(n)) rp = fl.renamed(n) if l != fl.size(i): if len(fl.revision(n)) != fl.size(i): self.err(lr, _("unpacked size is %s, %s expected") % (l, fl.size(i)), f) except error.CensoredNodeError: # experimental config: censor.policy if ui.config("censor", "policy", "abort") == "abort": self.err(lr, _("censored file data"), f) except Exception as inst: self.exc(lr, _("unpacking %s") % short(n), inst, f) # check renames try: if rp: if lr is not None and ui.verbose: ctx = lrugetctx(lr) found = False for pctx in ctx.parents(): if rp[0] in pctx: found = True break if not found: self.warn(_("warning: copy source of '%s' not" " in parents of %s") % (f, ctx)) fl2 = repo.file(rp[0]) if not len(fl2): self.err(lr, _("empty or missing copy source " "revlog %s:%s") % (rp[0], short(rp[1])), f) elif rp[1] == nullid: ui.note(_("warning: %s@%s: copy source" " revision is nullid %s:%s\n") % (f, lr, rp[0], short(rp[1]))) else: fl2.rev(rp[1]) except Exception as inst: self.exc(lr, _("checking rename of %s") % short(n), inst, f) # cross-check if f in filenodes: fns = [(lr, n) for n, lr in filenodes[f].iteritems()] for lr, node in sorted(fns): self.err(lr, _("%s in manifests not found") % short(node), f) ui.progress(_('checking'), None) for f in storefiles: self.warn(_("warning: orphan revlog '%s'") % f) return len(files), revisions