#!/usr/bin/python # Simple script to export darcs' changes as regular patches # Alberto Bertogli (albertito@blitiri.com.ar), 2008-08-03 # # The latest version should be available at # http://blitiri.com.ar/p/other/darcs-export import sys import os from xml.sax import saxutils from xml.sax import make_parser from xml.sax.handler import feature_namespaces, ContentHandler def run_darcs(*params): cmd = 'darcs ' + ' '.join(params) os.environ['DARCS_DONT_ESCAPE_8BIT'] = '1' inf, outf = os.popen4(cmd, 't') return outf class Patch: "Represents a single patch/record" def __init__(self): self.hash = '' self.author = '' self.date = '' self.local_date = '' self.name = '' self.comment = '' def tostr(self): s = "%s\n\tAuthor: %s\n\tDate: %s\n\tHash: %s\n" % \ (self.name, self.author, self.date, self.hash) return s def export(self, order, path): # '/'s are not allowed in filenames name = self.name.replace('/', '-') # avoid 'name..patch' if name[-1] == '.': name = name[:-1] fd = open("%s/%.2d - %s.patch" % (path, order, name), 'w') out = run_darcs("diff", "-u", '--match "hash %s"' % self.hash) buf = out.read() while buf: fd.write(buf) buf = out.read() class BuildPatchList(ContentHandler): def __init__(self): self.db = {} self.list = [] self.cur_hash = '' self.cur_elem = None self.cur_val = '' def startElement(self, name, attrs): if name == 'patch': p = Patch() p.author = attrs.get('author', None) p.date = attrs.get('date', None) p.local_date = attrs.get('local_date', None) p.hash = attrs.get('hash') self.db[p.hash] = p self.current = p.hash self.list.append(p.hash) elif name == 'name': self.db[self.current].name = '' self.cur_elem = 'name' elif name == 'comment': self.db[self.current].comment = '' self.cur_elem = 'name' else: self.cur_elem = None def characters(self, s): if not self.cur_elem: return self.cur_val += s def endElement(self, name): if name == 'name': self.db[self.current].name = self.cur_val elif name == 'comment': self.db[self.current].current = self.cur_val self.cur_elem = None self.cur_val = '' # main if len(sys.argv) < 3: print """\ Use: darcs-export [xmlfile|-] [list|export destdir]" Examples: $ darcs changes --last=4 --xml-output | darcs-export list $ darcs changes --last=4 --xml-output | darcs-export export /tmp """ sys.exit(1) if sys.argv[1] == '-': file = sys.stdin else: file = sys.argv[1] parser = make_parser() parser.setFeature(feature_namespaces, 0) handler = BuildPatchList() parser.setContentHandler(handler) parser.parse(file) # reverse the list so the oldest is the first, and the newest is the last handler.list.reverse() # we now have two main structures: handler.db is the hash table of Patches, # indexed by their hash, and handler.list is the ordered list of hashes. if sys.argv[2] == 'list': c = 1 for h in handler.list: print "%.2d:" % c, handler.db[h].tostr() c += 1 elif sys.argv[2] == 'export': if len(sys.argv) < 4: print "Destination directory missing" sys.exit(1) c = 1 for h in handler.list: p = handler.db[h] print "%.2d: %s" % (c, p.name) p.export(c, sys.argv[3]) c += 1 else: print "Unknown parameter"