Skip to content

Instantly share code, notes, and snippets.

@skitazaki
Created August 22, 2012 15:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skitazaki/3426925 to your computer and use it in GitHub Desktop.
Save skitazaki/3426925 to your computer and use it in GitHub Desktop.
Dump Subversion log and diff
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""%prog [options] svn-repository [svn-repository [ ... ]]
"""
import argparse
import codecs
import datetime
import logging
import json
import os
import subprocess
from StringIO import StringIO
from xml.etree.ElementTree import parse
class SvnLog(object):
def __init__(self, directory_or_url):
logging.info("Base directory: %s", directory_or_url)
if subprocess.call(['svn', 'info', directory_or_url]):
raise ValueError("%s is not SVN directory." % (directory_or_url,))
self.url = directory_or_url
def dump(self, outdir, start, days):
end = start + datetime.timedelta(days=days)
revisions = "{%s}:{%s}" % (start.strftime("%Y-%m-%d"), end.strftime("%Y-%m-%d"))
logging.debug("Range: %s - %s, Days: %d", start, end, days)
if not os.path.exists(outdir):
os.mkdir(outdir)
logging.info("%s does not exist, then is created.", outdir)
entries = []
for e in self.logentry(revisions):
r = int(e.get('revision'))
io = self.parse_logentry(e)
fname = 'r%d.txt' % (r, )
out = os.path.join(outdir, fname)
with open(out, 'wb') as w:
w.write(io.getvalue())
logging.info("Wrote SVN DIFF into %s", out)
entries.append({
"revision": r,
"author": e.find('author').text,
"date": e.find('date').text,
"message": e.find('msg').text,
"paths": [p.text for p in e.findall('paths/path')],
"out": out
})
return entries
def logentry(self, revisions):
cmd = ['svn', 'log', '-v', '--xml', '--stop-on-copy', '-r', revisions, self.url]
logging.info(" ".join(cmd))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
e = parse(p.stdout)
entries = e.findall('logentry')
if entries:
for log in entries:
yield log
def parse_logentry(self, e):
io = StringIO()
r = int(e.get('revision'))
revisions = "%d:%d" % (r - 1, r)
cmd = ['svn', 'diff', '-r', revisions, self.url]
logging.info(" ".join(cmd))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
io.write("r%d - Author: %s\n" % (r, e.find('author').text))
io.write("Date: %s\n" % (e.find('date').text,))
io.write('\n')
if e.find('msg').text:
io.write(e.find('msg').text.encode('utf-8'))
io.write('\n\n')
io.write('Changed:\n')
for path in e.findall('paths/path'):
io.write("[%s] %s\n" % (path.get('action'), path.text.encode('utf-8')))
io.write('\n')
for l in p.stdout:
io.write(l)
return io
def main():
parser = argparse.ArgumentParser(__doc__, version='1.0')
parser.add_argument('--start', type=str, help='Start of day. (YYYY-MM-DD)')
parser.add_argument('--days', type=int, default=1, help='Amount of days.')
parser.add_argument('--output', default=os.getcwd(), help='Output directory.')
parser.add_argument('dirs', nargs='+', help='Subversion workspace or URL.')
parser.add_argument('--create-newdir', dest="newdir",
default=False, action="store_true", help="create new directory")
parser.add_argument('--verbose', default=False, action="store_true",
help="verbose mode for logging")
parser.add_argument('--quiet', default=False, action="store_true",
help="quiet mode for logging")
args = parser.parse_args()
# Set logging verbosity from command line option.
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
elif not args.quiet:
logging.basicConfig(level=logging.INFO)
# Set start date.
if args.start:
start = datetime.datetime.strptime(args.start, "%Y-%m-%d")
else:
start = datetime.date.today() - datetime.timedelta(days=1)
outdir = args.output
if args.newdir:
outdir = os.path.join(outdir, start.strftime('%Y-%m-%d'))
entries = []
for d in args.dirs:
try:
sl = SvnLog(d)
e = sl.dump(outdir, start, args.days)
entries.extend(e)
except ValueError, e:
logging.error(e)
with open(os.path.join(outdir, 'index.json'), 'w') as w:
json.dump(entries, w)
def test():
logging.basicConfig(level=logging.DEBUG)
url = "http://svn.apache.org/repos/asf/lucene/dev/trunk"
start = datetime.date.today() - datetime.timedelta(days=1)
sl = SvnLog(url)
entries = sl.dump('_test', start, 1)
with open(os.path.join('_test', 'index.json'), 'w') as w:
json.dump(entries, w)
if __name__ == '__main__':
main()
#test()
# vim: set et ts=4 sw=4 cindent fileencoding=utf-8 :
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment