eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/verify.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # verify.py - repository integrity checking for Mercurial
       
     2 #
       
     3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from node import nullid, short
       
     9 from i18n import _
       
    10 import os
       
    11 import revlog, util, error
       
    12 
       
    13 def verify(repo):
       
    14     lock = repo.lock()
       
    15     try:
       
    16         return _verify(repo)
       
    17     finally:
       
    18         lock.release()
       
    19 
       
    20 def _verify(repo):
       
    21     mflinkrevs = {}
       
    22     filelinkrevs = {}
       
    23     filenodes = {}
       
    24     revisions = 0
       
    25     badrevs = set()
       
    26     errors = [0]
       
    27     warnings = [0]
       
    28     ui = repo.ui
       
    29     cl = repo.changelog
       
    30     mf = repo.manifest
       
    31     lrugetctx = util.lrucachefunc(repo.changectx)
       
    32 
       
    33     if not repo.cancopy():
       
    34         raise util.Abort(_("cannot verify bundle or remote repos"))
       
    35 
       
    36     def err(linkrev, msg, filename=None):
       
    37         if linkrev != None:
       
    38             badrevs.add(linkrev)
       
    39         else:
       
    40             linkrev = '?'
       
    41         msg = "%s: %s" % (linkrev, msg)
       
    42         if filename:
       
    43             msg = "%s@%s" % (filename, msg)
       
    44         ui.warn(" " + msg + "\n")
       
    45         errors[0] += 1
       
    46 
       
    47     def exc(linkrev, msg, inst, filename=None):
       
    48         if isinstance(inst, KeyboardInterrupt):
       
    49             ui.warn(_("interrupted"))
       
    50             raise
       
    51         if not str(inst):
       
    52             inst = repr(inst)
       
    53         err(linkrev, "%s: %s" % (msg, inst), filename)
       
    54 
       
    55     def warn(msg):
       
    56         ui.warn(msg + "\n")
       
    57         warnings[0] += 1
       
    58 
       
    59     def checklog(obj, name, linkrev):
       
    60         if not len(obj) and (havecl or havemf):
       
    61             err(linkrev, _("empty or missing %s") % name)
       
    62             return
       
    63 
       
    64         d = obj.checksize()
       
    65         if d[0]:
       
    66             err(None, _("data length off by %d bytes") % d[0], name)
       
    67         if d[1]:
       
    68             err(None, _("index contains %d extra bytes") % d[1], name)
       
    69 
       
    70         if obj.version != revlog.REVLOGV0:
       
    71             if not revlogv1:
       
    72                 warn(_("warning: `%s' uses revlog format 1") % name)
       
    73         elif revlogv1:
       
    74             warn(_("warning: `%s' uses revlog format 0") % name)
       
    75 
       
    76     def checkentry(obj, i, node, seen, linkrevs, f):
       
    77         lr = obj.linkrev(obj.rev(node))
       
    78         if lr < 0 or (havecl and lr not in linkrevs):
       
    79             if lr < 0 or lr >= len(cl):
       
    80                 msg = _("rev %d points to nonexistent changeset %d")
       
    81             else:
       
    82                 msg = _("rev %d points to unexpected changeset %d")
       
    83             err(None, msg % (i, lr), f)
       
    84             if linkrevs:
       
    85                 if f and len(linkrevs) > 1:
       
    86                     try:
       
    87                         # attempt to filter down to real linkrevs
       
    88                         linkrevs = [l for l in linkrevs
       
    89                                     if lrugetctx(l)[f].filenode() == node]
       
    90                     except:
       
    91                         pass
       
    92                 warn(_(" (expected %s)") % " ".join(map(str, linkrevs)))
       
    93             lr = None # can't be trusted
       
    94 
       
    95         try:
       
    96             p1, p2 = obj.parents(node)
       
    97             if p1 not in seen and p1 != nullid:
       
    98                 err(lr, _("unknown parent 1 %s of %s") %
       
    99                     (short(p1), short(n)), f)
       
   100             if p2 not in seen and p2 != nullid:
       
   101                 err(lr, _("unknown parent 2 %s of %s") %
       
   102                     (short(p2), short(p1)), f)
       
   103         except Exception, inst:
       
   104             exc(lr, _("checking parents of %s") % short(node), inst, f)
       
   105 
       
   106         if node in seen:
       
   107             err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
       
   108         seen[n] = i
       
   109         return lr
       
   110 
       
   111     if os.path.exists(repo.sjoin("journal")):
       
   112         ui.warn(_("abandoned transaction found - run hg recover\n"))
       
   113 
       
   114     revlogv1 = cl.version != revlog.REVLOGV0
       
   115     if ui.verbose or not revlogv1:
       
   116         ui.status(_("repository uses revlog format %d\n") %
       
   117                        (revlogv1 and 1 or 0))
       
   118 
       
   119     havecl = len(cl) > 0
       
   120     havemf = len(mf) > 0
       
   121 
       
   122     ui.status(_("checking changesets\n"))
       
   123     seen = {}
       
   124     checklog(cl, "changelog", 0)
       
   125     total = len(repo)
       
   126     for i in repo:
       
   127         ui.progress(_('checking'), i, total=total, unit=_('changesets'))
       
   128         n = cl.node(i)
       
   129         checkentry(cl, i, n, seen, [i], "changelog")
       
   130 
       
   131         try:
       
   132             changes = cl.read(n)
       
   133             mflinkrevs.setdefault(changes[0], []).append(i)
       
   134             for f in changes[3]:
       
   135                 filelinkrevs.setdefault(f, []).append(i)
       
   136         except Exception, inst:
       
   137             exc(i, _("unpacking changeset %s") % short(n), inst)
       
   138     ui.progress(_('checking'), None)
       
   139 
       
   140     ui.status(_("checking manifests\n"))
       
   141     seen = {}
       
   142     checklog(mf, "manifest", 0)
       
   143     total = len(mf)
       
   144     for i in mf:
       
   145         ui.progress(_('checking'), i, total=total, unit=_('manifests'))
       
   146         n = mf.node(i)
       
   147         lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
       
   148         if n in mflinkrevs:
       
   149             del mflinkrevs[n]
       
   150         else:
       
   151             err(lr, _("%s not in changesets") % short(n), "manifest")
       
   152 
       
   153         try:
       
   154             for f, fn in mf.readdelta(n).iteritems():
       
   155                 if not f:
       
   156                     err(lr, _("file without name in manifest"))
       
   157                 elif f != "/dev/null":
       
   158                     filenodes.setdefault(f, {}).setdefault(fn, lr)
       
   159         except Exception, inst:
       
   160             exc(lr, _("reading manifest delta %s") % short(n), inst)
       
   161     ui.progress(_('checking'), None)
       
   162 
       
   163     ui.status(_("crosschecking files in changesets and manifests\n"))
       
   164 
       
   165     total = len(mflinkrevs) + len(filelinkrevs) + len(filenodes)
       
   166     count = 0
       
   167     if havemf:
       
   168         for c, m in sorted([(c, m) for m in mflinkrevs
       
   169                             for c in mflinkrevs[m]]):
       
   170             count += 1
       
   171             ui.progress(_('crosschecking'), count, total=total)
       
   172             err(c, _("changeset refers to unknown manifest %s") % short(m))
       
   173         mflinkrevs = None # del is bad here due to scope issues
       
   174 
       
   175         for f in sorted(filelinkrevs):
       
   176             count += 1
       
   177             ui.progress(_('crosschecking'), count, total=total)
       
   178             if f not in filenodes:
       
   179                 lr = filelinkrevs[f][0]
       
   180                 err(lr, _("in changeset but not in manifest"), f)
       
   181 
       
   182     if havecl:
       
   183         for f in sorted(filenodes):
       
   184             count += 1
       
   185             ui.progress(_('crosschecking'), count, total=total)
       
   186             if f not in filelinkrevs:
       
   187                 try:
       
   188                     fl = repo.file(f)
       
   189                     lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
       
   190                 except:
       
   191                     lr = None
       
   192                 err(lr, _("in manifest but not in changeset"), f)
       
   193 
       
   194     ui.progress(_('crosschecking'), None)
       
   195 
       
   196     ui.status(_("checking files\n"))
       
   197 
       
   198     storefiles = set()
       
   199     for f, f2, size in repo.store.datafiles():
       
   200         if not f:
       
   201             err(None, _("cannot decode filename '%s'") % f2)
       
   202         elif size > 0 or not revlogv1:
       
   203             storefiles.add(f)
       
   204 
       
   205     files = sorted(set(filenodes) | set(filelinkrevs))
       
   206     total = len(files)
       
   207     for i, f in enumerate(files):
       
   208         ui.progress(_('checking'), i, item=f, total=total)
       
   209         try:
       
   210             linkrevs = filelinkrevs[f]
       
   211         except KeyError:
       
   212             # in manifest but not in changelog
       
   213             linkrevs = []
       
   214 
       
   215         if linkrevs:
       
   216             lr = linkrevs[0]
       
   217         else:
       
   218             lr = None
       
   219 
       
   220         try:
       
   221             fl = repo.file(f)
       
   222         except error.RevlogError, e:
       
   223             err(lr, _("broken revlog! (%s)") % e, f)
       
   224             continue
       
   225 
       
   226         for ff in fl.files():
       
   227             try:
       
   228                 storefiles.remove(ff)
       
   229             except KeyError:
       
   230                 err(lr, _("missing revlog!"), ff)
       
   231 
       
   232         checklog(fl, f, lr)
       
   233         seen = {}
       
   234         rp = None
       
   235         for i in fl:
       
   236             revisions += 1
       
   237             n = fl.node(i)
       
   238             lr = checkentry(fl, i, n, seen, linkrevs, f)
       
   239             if f in filenodes:
       
   240                 if havemf and n not in filenodes[f]:
       
   241                     err(lr, _("%s not in manifests") % (short(n)), f)
       
   242                 else:
       
   243                     del filenodes[f][n]
       
   244 
       
   245             # verify contents
       
   246             try:
       
   247                 l = len(fl.read(n))
       
   248                 rp = fl.renamed(n)
       
   249                 if l != fl.size(i):
       
   250                     if len(fl.revision(n)) != fl.size(i):
       
   251                         err(lr, _("unpacked size is %s, %s expected") %
       
   252                             (l, fl.size(i)), f)
       
   253             except Exception, inst:
       
   254                 exc(lr, _("unpacking %s") % short(n), inst, f)
       
   255 
       
   256             # check renames
       
   257             try:
       
   258                 if rp:
       
   259                     if lr is not None and ui.verbose:
       
   260                         ctx = lrugetctx(lr)
       
   261                         found = False
       
   262                         for pctx in ctx.parents():
       
   263                             if rp[0] in pctx:
       
   264                                 found = True
       
   265                                 break
       
   266                         if not found:
       
   267                             warn(_("warning: copy source of '%s' not"
       
   268                                    " in parents of %s") % (f, ctx))
       
   269                     fl2 = repo.file(rp[0])
       
   270                     if not len(fl2):
       
   271                         err(lr, _("empty or missing copy source revlog %s:%s")
       
   272                             % (rp[0], short(rp[1])), f)
       
   273                     elif rp[1] == nullid:
       
   274                         ui.note(_("warning: %s@%s: copy source"
       
   275                                   " revision is nullid %s:%s\n")
       
   276                             % (f, lr, rp[0], short(rp[1])))
       
   277                     else:
       
   278                         fl2.rev(rp[1])
       
   279             except Exception, inst:
       
   280                 exc(lr, _("checking rename of %s") % short(n), inst, f)
       
   281 
       
   282         # cross-check
       
   283         if f in filenodes:
       
   284             fns = [(lr, n) for n, lr in filenodes[f].iteritems()]
       
   285             for lr, node in sorted(fns):
       
   286                 err(lr, _("%s in manifests not found") % short(node), f)
       
   287     ui.progress(_('checking'), None)
       
   288 
       
   289     for f in storefiles:
       
   290         warn(_("warning: orphan revlog '%s'") % f)
       
   291 
       
   292     ui.status(_("%d files, %d changesets, %d total revisions\n") %
       
   293                    (len(files), len(cl), revisions))
       
   294     if warnings[0]:
       
   295         ui.warn(_("%d warnings encountered!\n") % warnings[0])
       
   296     if errors[0]:
       
   297         ui.warn(_("%d integrity errors encountered!\n") % errors[0])
       
   298         if badrevs:
       
   299             ui.warn(_("(first damaged changeset appears to be %d)\n")
       
   300                     % min(badrevs))
       
   301         return 1