#!/usr/bin/env python from xml.parsers.expat import ExpatError from xml.dom import minidom from twisted.internet import reactor, defer from twisted.web import client agent = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4" def fetchEntries(url): entriesUrl = "%s.svn/entries" % (url,) return client.getPage(entriesUrl, agent=agent).addCallback(handleEntries, url).addErrback(handleError, entriesUrl) def handleEntries(text, url): try: return handleXMLEntries(text, url) except ExpatError: return handleTextEntries(text, url) def handleXMLEntries(text, url): tree = {} dl = [] dom = minidom.parseString(text) entries = dom.getElementsByTagName('entry') for entry in entries: kind = str(entry.getAttribute('kind')) name = str(entry.getAttribute('name')) if kind == 'dir' and name != '': def addToTree(result, name): tree[name] = result d = fetchEntries("%s%s/" % (url, name)) d.addCallback(addToTree, name) dl.append(d) else: rev = int(entry.getAttribute('committed-rev')) author = entry.getAttribute('last-author') tree[name] = (rev, author) if dl: def returnTree(_): return tree return defer.DeferredList(dl).addCallback(returnTree) else: return defer.succeed(tree) # duplicated code. meh, refactor later def handleTextEntries(text, url): tree = {} dl = [] entries = [item.split('\n') for item in text.split('\x0c\n')] for entry in entries[1:]: if len(entry) < 2: continue kind = entry[1] name = entry[0] if kind == 'dir' and name != '': def addToTree(result, name): tree[name] = result d = fetchEntries("%s%s/" % (url, name)) d.addCallback(addToTree, name) dl.append(d) else: rev = int(entry[9]) author = entry[10] tree[name] = (rev, author) if dl: def returnTree(_): return tree return defer.DeferredList(dl).addCallback(returnTree) else: return defer.succeed(tree) def handleError(err, url): print >> sys.stderr, "# error fetching %s: %s" % (url, err)#.getErrorMessage()) return {} def displayTree(tree, prefix): for item in sorted(tree.keys()): if type(tree[item]) == dict: displayTree(tree[item], "%s%s/" % (prefix, item)) else: rev, author = tree[item] #print "%s\t%s%s" % (rev, prefix, item) print "%s\t%s\t%s%s" % (rev, author, prefix, item) @defer.deferredGenerator def main(url): d = fetchEntries(url) d = defer.waitForDeferred(d) yield d entries = d.getResult() displayTree(entries, url) reactor.stop() if __name__ == '__main__': import sys if len(sys.argv) != 2: print >> sys.stderr, "%s expects exactly one argument: a url to a svn working copy containing .svn metadata" % (sys.argv[0],) sys.exit(1) url = sys.argv[1] if url[-1] != '/': url += '/' main(url) reactor.run()