|
|
#!/usr/bin/env python
|
|
|
#
|
|
|
# mercurial - a minimal scalable distributed SCM
|
|
|
# v0.5b "katje"
|
|
|
#
|
|
|
# Copyright 2005 Matt Mackall <mpm@selenic.com>
|
|
|
#
|
|
|
# This software may be used and distributed according to the terms
|
|
|
# of the GNU General Public License, incorporated herein by reference.
|
|
|
|
|
|
# the psyco compiler makes commits a bit faster
|
|
|
# and makes changegroup merge about 20 times slower!
|
|
|
# try:
|
|
|
# import psyco
|
|
|
# psyco.full()
|
|
|
# except:
|
|
|
# pass
|
|
|
|
|
|
import sys
|
|
|
from mercurial import hg, mdiff, fancyopts, ui, commands
|
|
|
|
|
|
try:
|
|
|
sys.exit(commands.dispatch(sys.argv[1:]))
|
|
|
except commands.UnknownCommand:
|
|
|
# fall through
|
|
|
pass
|
|
|
|
|
|
options = {}
|
|
|
opts = [('v', 'verbose', None, 'verbose'),
|
|
|
('d', 'debug', None, 'debug'),
|
|
|
('q', 'quiet', None, 'quiet'),
|
|
|
('y', 'noninteractive', None, 'run non-interactively'),
|
|
|
]
|
|
|
|
|
|
args = fancyopts.fancyopts(sys.argv[1:], opts, options,
|
|
|
'hg [options] <command> [command options] [files]')
|
|
|
|
|
|
try:
|
|
|
cmd = args[0]
|
|
|
args = args[1:]
|
|
|
except:
|
|
|
cmd = "help"
|
|
|
|
|
|
ui = ui.ui(options["verbose"], options["debug"], options["quiet"],
|
|
|
not options["noninteractive"])
|
|
|
|
|
|
try:
|
|
|
repo = hg.repository(ui=ui)
|
|
|
except IOError:
|
|
|
ui.warn("Unable to open repository\n")
|
|
|
sys.exit(0)
|
|
|
|
|
|
if cmd == "debugchangegroup":
|
|
|
newer = repo.newer(map(repo.lookup, args))
|
|
|
for chunk in repo.changegroup(newer):
|
|
|
sys.stdout.write(chunk)
|
|
|
|
|
|
elif cmd == "debugaddchangegroup":
|
|
|
data = sys.stdin.read()
|
|
|
repo.addchangegroup(data)
|
|
|
|
|
|
elif cmd == "dump":
|
|
|
if args:
|
|
|
r = repo.file(args[0])
|
|
|
n = r.tip()
|
|
|
if len(args) > 1: n = r.lookup(args[1])
|
|
|
sys.stdout.write(r.read(n))
|
|
|
else:
|
|
|
print "missing filename"
|
|
|
|
|
|
elif cmd == "dumpmanifest":
|
|
|
n = repo.manifest.tip()
|
|
|
if len(args) > 0:
|
|
|
n = repo.manifest.lookup(args[0])
|
|
|
m = repo.manifest.read(n)
|
|
|
files = m.keys()
|
|
|
files.sort()
|
|
|
|
|
|
for f in files:
|
|
|
print hg.hex(m[f]), f
|
|
|
|
|
|
elif cmd == "debugindex":
|
|
|
if ".hg" not in args[0]:
|
|
|
args[0] = ".hg/data/" + repo.file(args[0]).encodepath(args[0]) + "i"
|
|
|
|
|
|
r = hg.revlog(open, args[0], "")
|
|
|
print " rev offset length base linkrev"+\
|
|
|
" p1 p2 nodeid"
|
|
|
for i in range(r.count()):
|
|
|
e = r.index[i]
|
|
|
print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
|
|
|
i, e[0], e[1], e[2], e[3],
|
|
|
hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
|
|
|
|
|
|
elif cmd == "debugindexdot":
|
|
|
if ".hg" not in args[0]:
|
|
|
args[0] = ".hg/data/" + repo.file(args[0]).encodepath(args[0]) + "i"
|
|
|
|
|
|
r = hg.revlog(open, args[0], "")
|
|
|
print "digraph G {"
|
|
|
for i in range(r.count()):
|
|
|
e = r.index[i]
|
|
|
print "\t%d -> %d" % (r.rev(e[4]), i)
|
|
|
if e[5] != hg.nullid:
|
|
|
print "\t%d -> %d" % (r.rev(e[5]), i)
|
|
|
print "}"
|
|
|
|
|
|
elif cmd == "tags":
|
|
|
repo.lookup(0) # prime the cache
|
|
|
i = repo.tags.items()
|
|
|
i.sort()
|
|
|
for k, n in i:
|
|
|
try:
|
|
|
r = repo.changelog.rev(n)
|
|
|
except KeyError:
|
|
|
r = "?"
|
|
|
print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
|
|
|
|
|
|
elif cmd == "verify":
|
|
|
filelinkrevs = {}
|
|
|
filenodes = {}
|
|
|
manifestchangeset = {}
|
|
|
changesets = revisions = files = 0
|
|
|
errors = 0
|
|
|
|
|
|
ui.status("checking changesets\n")
|
|
|
for i in range(repo.changelog.count()):
|
|
|
changesets += 1
|
|
|
n = repo.changelog.node(i)
|
|
|
for p in repo.changelog.parents(n):
|
|
|
if p not in repo.changelog.nodemap:
|
|
|
ui.warn("changeset %s has unknown parent %s\n" %
|
|
|
(hg.short(n), hg.short(p)))
|
|
|
errors += 1
|
|
|
try:
|
|
|
changes = repo.changelog.read(n)
|
|
|
except Exception, inst:
|
|
|
ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
|
|
|
errors += 1
|
|
|
|
|
|
manifestchangeset[changes[0]] = n
|
|
|
for f in changes[3]:
|
|
|
filelinkrevs.setdefault(f, []).append(i)
|
|
|
|
|
|
ui.status("checking manifests\n")
|
|
|
for i in range(repo.manifest.count()):
|
|
|
n = repo.manifest.node(i)
|
|
|
for p in repo.manifest.parents(n):
|
|
|
if p not in repo.manifest.nodemap:
|
|
|
ui.warn("manifest %s has unknown parent %s\n" %
|
|
|
(hg.short(n), hg.short(p)))
|
|
|
errors += 1
|
|
|
ca = repo.changelog.node(repo.manifest.linkrev(n))
|
|
|
cc = manifestchangeset[n]
|
|
|
if ca != cc:
|
|
|
ui.warn("manifest %s points to %s, not %s\n" %
|
|
|
(hg.hex(n), hg.hex(ca), hg.hex(cc)))
|
|
|
errors += 1
|
|
|
|
|
|
try:
|
|
|
delta = mdiff.patchtext(repo.manifest.delta(n))
|
|
|
except KeyboardInterrupt:
|
|
|
print "aborted"
|
|
|
sys.exit(0)
|
|
|
except Exception, inst:
|
|
|
ui.warn("unpacking manifest %s: %s\n" % (hg.short(n), inst))
|
|
|
errors += 1
|
|
|
|
|
|
ff = [ l.split('\0') for l in delta.splitlines() ]
|
|
|
for f, fn in ff:
|
|
|
filenodes.setdefault(f, {})[hg.bin(fn)] = 1
|
|
|
|
|
|
ui.status("crosschecking files in changesets and manifests\n")
|
|
|
for f in filenodes:
|
|
|
if f not in filelinkrevs:
|
|
|
ui.warn("file %s in manifest but not in changesets\n" % f)
|
|
|
errors += 1
|
|
|
|
|
|
for f in filelinkrevs:
|
|
|
if f not in filenodes:
|
|
|
ui.warn("file %s in changeset but not in manifest\n" % f)
|
|
|
errors += 1
|
|
|
|
|
|
ui.status("checking files\n")
|
|
|
ff = filenodes.keys()
|
|
|
ff.sort()
|
|
|
for f in ff:
|
|
|
if f == "/dev/null": continue
|
|
|
files += 1
|
|
|
fl = repo.file(f)
|
|
|
nodes = { hg.nullid: 1 }
|
|
|
for i in range(fl.count()):
|
|
|
revisions += 1
|
|
|
n = fl.node(i)
|
|
|
|
|
|
if n not in filenodes[f]:
|
|
|
ui.warn("%s: %d:%s not in manifests\n" % (f, i, hg.short(n)))
|
|
|
print len(filenodes[f].keys()), fl.count(), f
|
|
|
errors += 1
|
|
|
else:
|
|
|
del filenodes[f][n]
|
|
|
|
|
|
flr = fl.linkrev(n)
|
|
|
if flr not in filelinkrevs[f]:
|
|
|
ui.warn("%s:%s points to unexpected changeset rev %d\n"
|
|
|
% (f, hg.short(n), fl.linkrev(n)))
|
|
|
errors += 1
|
|
|
else:
|
|
|
filelinkrevs[f].remove(flr)
|
|
|
|
|
|
# verify contents
|
|
|
try:
|
|
|
t = fl.read(n)
|
|
|
except Exception, inst:
|
|
|
ui.warn("unpacking file %s %s: %s\n" % (f, hg.short(n), inst))
|
|
|
errors += 1
|
|
|
|
|
|
# verify parents
|
|
|
(p1, p2) = fl.parents(n)
|
|
|
if p1 not in nodes:
|
|
|
ui.warn("file %s:%s unknown parent 1 %s" %
|
|
|
(f, hg.short(n), hg.short(p1)))
|
|
|
errors += 1
|
|
|
if p2 not in nodes:
|
|
|
ui.warn("file %s:%s unknown parent 2 %s" %
|
|
|
(f, hg.short(n), hg.short(p1)))
|
|
|
errors += 1
|
|
|
nodes[n] = 1
|
|
|
|
|
|
# cross-check
|
|
|
for node in filenodes[f]:
|
|
|
ui.warn("node %s in manifests not in %s\n" % (hg.hex(n), f))
|
|
|
errors += 1
|
|
|
|
|
|
ui.status("%d files, %d changesets, %d total revisions\n" %
|
|
|
(files, changesets, revisions))
|
|
|
|
|
|
if errors:
|
|
|
ui.warn("%d integrity errors encountered!\n" % errors)
|
|
|
sys.exit(1)
|
|
|
|
|
|
else:
|
|
|
if cmd: ui.warn("unknown command\n\n")
|
|
|
sys.exit(1)
|
|
|
|