Move the long option name enum from cl.h into main.c, because it is
[svn.git] / tools / bdb / svn-bdb-view.py
blob66674410fa97e85f567a43287b35dab8d2ad862b
1 #!/usr/bin/env python
3 # This is a pretty-printer for subversion BDB repository databases.
6 import sys, os, re, codecs, textwrap
7 import skel, svnfs
9 # Parse arguments
10 if len(sys.argv) == 2:
11 dbhome = os.path.join(sys.argv[1], 'db')
12 if not os.path.exists(dbhome):
13 sys.stderr.write("%s: '%s' is not a valid svn repository\n" %
14 (sys.argv[0], dbhome))
15 sys.exit(1)
16 else:
17 sys.stderr.write("Usage: %s <svn-repository>\n" % sys.argv[0])
18 sys.exit(1)
20 # Helper Classes
21 class RepositoryProblem(Exception):
22 pass
24 # Helper Functions
25 def ok(bool, comment):
26 if not bool:
27 raise RepositoryProblem(text)
29 # Helper Data
30 opmap = {
31 'add': 'A',
32 'modify': 'M',
33 'delete': 'D',
34 'replace': 'R',
35 'reset': 'X',
38 # Analysis Modules
39 def am_uuid(ctx):
40 "uuids"
41 db = ctx.uuids_db
42 ok(db.keys() == [1], 'uuid Table Structure')
43 ok(re.match(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
44 db[1]), 'UUID format')
45 print "Repos UUID: %s" % db[1]
47 def am_revisions(ctx):
48 "revisions"
49 cur = ctx.revs_db.cursor()
50 try:
51 rec = cur.first()
52 ctx.txn2rev = txn2rev = {}
53 prevrevnum = -1
54 while rec:
55 rev = skel.Rev(rec[1])
56 revnum = rec[0] - 1
57 print "r%d: txn %s%s" % (revnum, rev.txn,
58 (not ctx.txns_db.has_key(rev.txn)) and "*** MISSING TXN ***" or "")
59 ok(not txn2rev.has_key(rev.txn), 'Multiple revs bound to same txn')
60 txn2rev[rev.txn] = revnum
61 rec = cur.next()
62 finally:
63 cur.close()
65 def am_changes(ctx):
66 "changes"
67 cur = ctx.changes_db.cursor()
68 try:
69 current_txnid_len = 0
70 maximum_txnid_len = 0
71 while current_txnid_len <= maximum_txnid_len:
72 current_txnid_len += 1
73 rec = cur.first()
74 prevtxn = None
75 while rec:
76 if len(rec[0]) != current_txnid_len:
77 rec = cur.next()
78 continue
79 ch = skel.Change(rec[1])
80 lead = "txn %s:" % rec[0]
81 if prevtxn == rec[0]:
82 lead = " " * len(lead)
83 print "%s %s %s %s %s %s%s" % (lead, opmap[ch.kind], ch.path, ch.node,
84 ch.textmod and "T" or "-", ch.propmod and "P" or "-",
85 (not ctx.nodes_db.has_key(ch.node)) \
86 and "*** MISSING NODE ***" or "")
87 prevtxn = rec[0]
88 if len(rec[0]) > maximum_txnid_len:
89 maximum_txnid_len = len(rec[0])
90 rec = cur.next()
91 finally:
92 cur.close()
94 def am_copies(ctx):
95 "copies"
96 cur = ctx.copies_db.cursor()
97 try:
98 print "next-key: %s" % ctx.copies_db['next-key']
99 rec = cur.first()
100 while rec:
101 if rec[0] != 'next-key':
102 cp = skel.Copy(rec[1])
103 destnode = ctx.nodes_db.get(cp.destnode)
104 if not destnode:
105 destpath = "*** MISSING NODE ***"
106 else:
107 destpath = skel.Node(destnode).createpath
108 print "cpy %s: %s %s @txn %s to %s (%s)" % (rec[0],
109 {'copy':'C','soft-copy':'S'}[cp.kind], cp.srcpath or "-",
110 cp.srctxn or "-", cp.destnode, destpath)
111 rec = cur.next()
112 finally:
113 cur.close()
115 def am_txns(ctx):
116 "transactions"
117 cur = ctx.txns_db.cursor()
118 try:
119 print "next-key: %s" % ctx.txns_db['next-key']
120 length = 1
121 found_some = True
122 while found_some:
123 found_some = False
124 rec = cur.first()
125 while rec:
126 if rec[0] != 'next-key' and len(rec[0]) == length:
127 found_some = True
128 txn = skel.Txn(rec[1])
129 if txn.kind == "committed":
130 label = "r%s" % txn.rev
131 ok(ctx.txn2rev[rec[0]] == int(txn.rev), 'Txn->rev not <-txn')
132 else:
133 label = "%s based-on %s" % (txn.kind, txn.basenode)
134 print "txn %s: %s root-node %s props %d copies %s" % (rec[0],
135 label, txn.rootnode, len(txn.proplist) / 2, ",".join(txn.copies))
136 rec = cur.next()
137 length += 1
138 finally:
139 cur.close()
141 def am_nodes(ctx):
142 "nodes"
143 cur = ctx.nodes_db.cursor()
144 try:
145 print "next-key: %s" % ctx.txns_db['next-key']
146 rec = cur.first()
147 data = {}
148 while rec:
149 if rec[0] == 'next-key':
150 rec = cur.next()
151 continue
152 nd = skel.Node(rec[1])
153 nid,cid,tid = rec[0].split(".")
154 data[tid.rjust(20)+nd.createpath] = (rec[0], nd)
155 rec = cur.next()
156 k = data.keys()
157 k.sort()
158 reptype = {"fulltext":"F", "delta":"D"}
159 for i in k:
160 nd = data[i][1]
161 prkind = drkind = " "
162 if nd.proprep:
163 try:
164 rep = skel.Rep(ctx.reps_db[nd.proprep])
165 prkind = reptype[rep.kind]
166 if ctx.bad_reps.has_key(nd.proprep):
167 prkind += " *** BAD ***"
168 except KeyError:
169 prkind = "*** MISSING ***"
170 if nd.datarep:
171 try:
172 rep = skel.Rep(ctx.reps_db[nd.datarep])
173 drkind = reptype[rep.kind]
174 if ctx.bad_reps.has_key(nd.datarep):
175 drkind += " *** BAD ***"
176 except KeyError:
177 drkind = "*** MISSING ***"
178 stringdata = "%s: %s %s pred %s count %s prop %s %s data %s %s edit %s" \
179 % ( data[i][0], {"file":"F", "dir":"D"}[nd.kind], nd.createpath,
180 nd.prednode or "-", nd.predcount, prkind, nd.proprep or "-",
181 drkind, nd.datarep or "-", nd.editrep or "-")
182 if nd.createpath == "/":
183 print
184 print stringdata
185 finally:
186 cur.close()
188 def get_string(ctx, id):
189 try:
190 return ctx.get_whole_string(id)
191 except DbNotFoundError:
192 return "*** MISSING STRING ***"
194 def am_reps(ctx):
195 "representations"
196 ctx.bad_reps = {}
197 cur = ctx.reps_db.cursor()
198 try:
199 print "next-key: %s" % ctx.txns_db['next-key']
200 rec = cur.first()
201 while rec:
202 if rec[0] != 'next-key':
203 rep = skel.Rep(rec[1])
204 lead = "rep %s: txn %s: %s %s " % (rec[0], rep.txn, rep.cksumtype,
205 codecs.getencoder('hex_codec')(rep.cksum)[0])
206 if rep.kind == "fulltext":
207 note = ""
208 if not ctx.strings_db.has_key(rep.str):
209 note = " *MISS*"
210 ctx.bad_reps[rec[0]] = None
211 print lead+("fulltext str %s%s" % (rep.str, note))
212 if ctx.verbose:
213 print textwrap.fill(get_string(ctx, rep.str), initial_indent=" ",
214 subsequent_indent=" ", width=78)
215 elif rep.kind == "delta":
216 print lead+("delta of %s window%s" % (len(rep.windows),
217 len(rep.windows) != 1 and "s" or ""))
218 for window in rep.windows:
219 noterep = notestr = ""
220 if not ctx.reps_db.has_key(window.vs_rep):
221 noterep = " *MISS*"
222 ctx.bad_reps[rec[0]] = None
223 if not ctx.strings_db.has_key(window.str):
224 notestr = " *MISS*"
225 ctx.bad_reps[rec[0]] = None
226 print "\toff %s len %s vs-rep %s%s str %s%s" % (window.offset,
227 window.size, window.vs_rep, noterep, window.str, notestr)
228 else:
229 print lead+"*** UNKNOWN REPRESENTATION TYPE ***"
230 rec = cur.next()
231 finally:
232 cur.close()
235 def am_stringsize(ctx):
236 "string size"
237 if not ctx.verbose:
238 return
239 cur = ctx.strings_db.cursor()
240 try:
241 rec = cur.first()
242 size = 0
243 while rec:
244 size = size + len(rec[1] or "")
245 rec = cur.next()
246 print size, size/1024.0, size/1024.0/1024.0
247 finally:
248 cur.close()
250 modules = (
251 am_uuid,
252 am_revisions,
253 am_changes,
254 am_copies,
255 am_txns,
256 am_reps,
257 am_nodes,
258 # Takes too long: am_stringsize,
261 def main():
262 print "Repository View for '%s'" % dbhome
263 print
264 ctx = svnfs.Ctx(dbhome, readonly=1)
265 # Stash process state in a library data structure. Yuck!
266 ctx.verbose = 0
267 try:
268 for am in modules:
269 print "MODULE: %s" % am.__doc__
270 am(ctx)
271 print
272 finally:
273 ctx.close()
275 if __name__ == '__main__':
276 main()