#!/usr/bin/python
#
# This is a pretty-printer for subversion BDB repository databases.
#
import sys, os, re, codecs, textwrap
import skel, svnfs
# Parse arguments
if len(sys.argv) == 2:
dbhome = os.path.join(sys.argv[1], 'db')
if not os.path.exists(dbhome):
sys.stderr.write("%s: '%s' is not a valid svn repository\n" %
(sys.argv[0], dbhome))
sys.exit(1)
else:
sys.stderr.write("Usage: %s <svn-repository>\n" % sys.argv[0])
sys.exit(1)
# Helper Classes
class RepositoryProblem(Exception):
pass
# Helper Functions
def ok(bool, comment):
if not bool:
raise RepositoryProblem(text)
# Helper Data
opmap = {
'add': 'A',
'modify': 'M',
'delete': 'D',
'replace': 'R',
'reset': 'X',
}
# Analysis Modules
def am_uuid(ctx):
"uuids"
db = ctx.uuids_db
ok(db.keys() == [1], 'uuid Table Structure')
ok(re.match(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
db[1]), 'UUID format')
print "Repos UUID: %s" % db[1]
def am_revisions(ctx):
"revisions"
cur = ctx.revs_db.cursor()
try:
rec = cur.first()
ctx.txn2rev = txn2rev = {}
prevrevnum = -1
while rec:
rev = skel.Rev(rec[1])
revnum = rec[0] - 1
print "r%d: txn %s%s" % (revnum, rev.txn,
(not ctx.txns_db.has_key(rev.txn)) and "*** MISSING TXN ***" or "")
ok(not txn2rev.has_key(rev.txn), 'Multiple revs bound to same txn')
txn2rev[rev.txn] = revnum
rec = cur.next()
finally:
cur.close()
def am_changes(ctx):
"changes"
cur = ctx.changes_db.cursor()
try:
current_txnid_len = 0
maximum_txnid_len = 0
while current_txnid_len <= maximum_txnid_len:
current_txnid_len += 1
rec = cur.first()
prevtxn = None
while rec:
if len(rec[0]) != current_txnid_len:
rec = cur.next()
continue
ch = skel.Change(rec[1])
lead = "txn %s:" % rec[0]
if prevtxn == rec[0]:
lead = " " * len(lead)
print "%s %s %s %s %s %s%s" % (lead, opmap[ch.kind], ch.path, ch.node,
ch.textmod and "T" or "-", ch.propmod and "P" or "-",
(not ctx.nodes_db.has_key(ch.node)) \
and "*** MISSING NODE ***" or "")
prevtxn = rec[0]
if len(rec[0]) > maximum_txnid_len:
maximum_txnid_len = len(rec[0])
rec = cur.next()
finally:
cur.close()
def am_copies(ctx):
"copies"
cur = ctx.copies_db.cursor()
try:
print "next-key: %s" % ctx.copies_db['next-key']
rec = cur.first()
while rec:
if rec[0] != 'next-key':
cp = skel.Copy(rec[1])
destnode = ctx.nodes_db.get(cp.destnode)
if not destnode:
destpath = "*** MISSING NODE ***"
else:
destpath = skel.Node(destnode).createpath
print "cpy %s: %s %s @txn %s to %s (%s)" % (rec[0],
{'copy':'C','soft-copy':'S'}[cp.kind], cp.srcpath or "-",
cp.srctxn or "-", cp.destnode, destpath)
rec = cur.next()
finally:
cur.close()
def am_txns(ctx):
"transactions"
cur = ctx.txns_db.cursor()
try:
print "next-key: %s" % ctx.txns_db['next-key']
length = 1
found_some = True
while found_some:
found_some = False
rec = cur.first()
while rec:
if rec[0] != 'next-key' and len(rec[0]) == length:
found_some = True
txn = skel.Txn(rec[1])
if txn.kind == "committed":
label = "r%s" % txn.rev
ok(ctx.txn2rev[rec[0]] == int(txn.rev), 'Txn->rev not <-txn')
else:
label = "%s based-on %s" % (txn.kind, txn.basenode)
print "txn %s: %s root-node %s props %d copies %s" % (rec[0],
label, txn.rootnode, len(txn.proplist) / 2, ",".join(txn.copies))
rec = cur.next()
length += 1
finally:
cur.close()
def am_nodes(ctx):
"nodes"
cur = ctx.nodes_db.cursor()
try:
print "next-key: %s" % ctx.txns_db['next-key']
rec = cur.first()
data = {}
while rec:
if rec[0] == 'next-key':
rec = cur.next()
continue
nd = skel.Node(rec[1])
nid,cid,tid = rec[0].split(".")
data[tid.rjust(20)+nd.createpath] = (rec[0], nd)
rec = cur.next()
k = data.keys()
k.sort()
reptype = {"fulltext":"F", "delta":"D"}
for i in k:
nd = data[i][1]
prkind = drkind = " "
if nd.proprep:
try:
rep = skel.Rep(ctx.reps_db[nd.proprep])
prkind = reptype[rep.kind]
if ctx.bad_reps.has_key(nd.proprep):
prkind += " *** BAD ***"
except KeyError:
prkind = "*** MISSING ***"
if nd.datarep:
try:
rep = skel.Rep(ctx.reps_db[nd.datarep])
drkind = reptype[rep.kind]
if ctx.bad_reps.has_key(nd.datarep):
drkind += " *** BAD ***"
except KeyError:
drkind = "*** MISSING ***"
stringdata = "%s: %s %s pred %s count %s prop %s %s data %s %s edit %s" \
% ( data[i][0], {"file":"F", "dir":"D"}[nd.kind], nd.createpath,
nd.prednode or "-", nd.predcount, prkind, nd.proprep or "-",
drkind, nd.datarep or "-", nd.editrep or "-")
if nd.createpath == "/":
print
print stringdata
finally:
cur.close()
def get_string(ctx, id):
try:
return ctx.get_whole_string(id)
except DbNotFoundError:
return "*** MISSING STRING ***"
def am_reps(ctx):
"representations"
ctx.bad_reps = {}
cur = ctx.reps_db.cursor()
try:
print "next-key: %s" % ctx.txns_db['next-key']
rec = cur.first()
while rec:
if rec[0] != 'next-key':
rep = skel.Rep(rec[1])
lead = "rep %s: txn %s: %s %s " % (rec[0], rep.txn, rep.cksumtype,
codecs.getencoder('hex_codec')(rep.cksum)[0])
if rep.kind == "fulltext":
note = ""
if not ctx.strings_db.has_key(rep.str):
note = " *MISS*"
ctx.bad_reps[rec[0]] = None
print lead+("fulltext str %s%s" % (rep.str, note))
if ctx.verbose:
print textwrap.fill(get_string(ctx, rep.str), initial_indent=" ",
subsequent_indent=" ", width=78)
elif rep.kind == "delta":
print lead+("delta of %s window%s" % (len(rep.windows),
len(rep.windows) != 1 and "s" or ""))
for window in rep.windows:
noterep = notestr = ""
if not ctx.reps_db.has_key(window.vs_rep):
noterep = " *MISS*"
ctx.bad_reps[rec[0]] = None
if not ctx.strings_db.has_key(window.str):
notestr = " *MISS*"
ctx.bad_reps[rec[0]] = None
print "\toff %s len %s vs-rep %s%s str %s%s" % (window.offset,
window.size, window.vs_rep, noterep, window.str, notestr)
else:
print lead+"*** UNKNOWN REPRESENTATION TYPE ***"
rec = cur.next()
finally:
cur.close()
def am_stringsize(ctx):
"string size"
if not ctx.verbose:
return
cur = ctx.strings_db.cursor()
try:
rec = cur.first()
size = 0
while rec:
size = size + len(rec[1] or "")
rec = cur.next()
print size, size/1024.0, size/1024.0/1024.0
finally:
cur.close()
modules = (
am_uuid,
am_revisions,
am_changes,
am_copies,
am_txns,
am_reps,
am_nodes,
# Takes too long: am_stringsize,
)
def main():
print "Repository View for '%s'" % dbhome
print
ctx = svnfs.Ctx(dbhome, readonly=1)
# Stash process state in a library data structure. Yuck!
ctx.verbose = 0
try:
for am in modules:
print "MODULE: %s" % am.__doc__
am(ctx)
print
finally:
ctx.close()
if __name__ == '__main__':
main()
syntax highlighted by Code2HTML, v. 0.9.1