Created
August 22, 2012 15:47
-
-
Save skitazaki/3426925 to your computer and use it in GitHub Desktop.
Dump Subversion log and diff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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