diff options
Diffstat (limited to 'RepSys')
-rw-r--r-- | RepSys/commands/getsrpm.py | 24 | ||||
-rw-r--r-- | RepSys/commands/markrelease.py | 9 | ||||
-rw-r--r-- | RepSys/commands/rpmlog.py | 6 | ||||
-rw-r--r-- | RepSys/commands/submit.py | 13 | ||||
-rw-r--r-- | RepSys/log.py | 377 | ||||
-rw-r--r-- | RepSys/rpmutil.py | 54 | ||||
-rw-r--r-- | RepSys/svn.py | 25 |
7 files changed, 406 insertions, 102 deletions
diff --git a/RepSys/commands/getsrpm.py b/RepSys/commands/getsrpm.py index f2def54..a212b52 100644 --- a/RepSys/commands/getsrpm.py +++ b/RepSys/commands/getsrpm.py @@ -26,6 +26,7 @@ Options: -s FILE Run script with "FILE TOPDIR SPECFILE" command -n Rename the package to include the revision number -l Use subversion log to build rpm %changelog + -T FILE Template to be used to generate the %changelog -h Show this message Examples: @@ -41,7 +42,7 @@ def mode_callback(option, opt, val, parser, mode): try: opts.version, opts.release = val.split("-", 1) except ValueError: - raise Error, "wrong version, use something like 2.2-1cl" + raise Error, "wrong version, use something like 2.2-1mdk" elif mode == "revision": opts.revision = val @@ -51,20 +52,25 @@ def parse_options(): parser.defaults["version"] = None parser.defaults["release"] = None parser.defaults["revision"] = None - parser.add_option("-c", action="callback", callback=mode_callback, - callback_kwargs={"mode": "current"}) - parser.add_option("-p", action="callback", callback=mode_callback, - callback_kwargs={"mode": "pristine"}) - parser.add_option("-r", action="callback", callback=mode_callback, - callback_kwargs={"mode": "revision"}) - parser.add_option("-v", action="callback", callback=mode_callback, - callback_kwargs={"mode": "version"}) + parser.defaults["submit"] = False + callback_options = dict(action="callback", callback=mode_callback, + type="string", dest="__ignore") + parser.add_option("-c", callback_kwargs={"mode": "current"}, nargs=0, + **callback_options) + parser.add_option("-p", callback_kwargs={"mode": "pristine"}, nargs=0, + **callback_options) + parser.add_option("-r", callback_kwargs={"mode": "revision"}, nargs=1, + **callback_options) + parser.add_option("-v", callback_kwargs={"mode": "version"}, nargs=1, + **callback_options) parser.add_option("-t", dest="targetdirs", action="append", default=[]) parser.add_option("-s", dest="scripts", action="append", default=[]) parser.add_option("-P", dest="packager", default="") parser.add_option("-n", dest="revname", action="store_true") parser.add_option("-l", dest="svnlog", action="store_true") + parser.add_option("-T", dest="template", type="string", default=None) opts, args = parser.parse_args() + del opts.__ignore if len(args) != 1: raise Error, "invalid arguments" opts.pkgdirurl = default_parent(args[0]) diff --git a/RepSys/commands/markrelease.py b/RepSys/commands/markrelease.py index 628e02d..9e52a31 100644 --- a/RepSys/commands/markrelease.py +++ b/RepSys/commands/markrelease.py @@ -39,13 +39,14 @@ def version_callback(option, opt, val, parser): try: opts.version, opts.release = val.split("-", 1) except ValueError: - raise Error, "wrong version, use something like 1:2.2-1cl" + raise Error, "wrong version, use something like 1:2.2-1mdk" def parse_options(): parser = OptionParser(help=HELP) parser.defaults["version"] = None parser.defaults["release"] = None - parser.add_option("-v", action="callback", type="string", callback=version_callback) + parser.add_option("-v", action="callback", callback=version_callback, + nargs=1, type="string", dest="__ignore") parser.add_option("-r", dest="revision") parser.add_option("-f", dest="filename") parser.add_option("-n", dest="appendname", action="store_true") @@ -58,7 +59,7 @@ def parse_options(): filename = opts.filename appendname = opts.appendname - del opts.filename, opts.appendname + del opts.filename, opts.appendname, opts.__ignore if filename: if not os.path.isfile(filename): @@ -87,7 +88,7 @@ def parse_options(): raise Error, "no revision provided" elif not opts.version: raise Error, "no version provided" - get_auth() + #get_auth() return opts def main(): diff --git a/RepSys/commands/rpmlog.py b/RepSys/commands/rpmlog.py index 24eddfe..7ea1ac0 100644 --- a/RepSys/commands/rpmlog.py +++ b/RepSys/commands/rpmlog.py @@ -15,6 +15,7 @@ Usage: repsys rpmlog [OPTIONS] REPPKGDIRURL Options: -r REV Collect logs from given revision to revision 0 -n NUM Output only last NUM entries + -T FILE %changelog template file to be used -h Show this message Examples: @@ -25,14 +26,15 @@ def parse_options(): parser = OptionParser(help=HELP) parser.add_option("-r", dest="revision") parser.add_option("-n", dest="size", type="int") + parser.add_option("-T", "--template", dest="template", type="string") opts, args = parser.parse_args() if len(args) != 1: raise Error, "invalid arguments" opts.pkgdirurl = default_parent(args[0]) return opts -def rpmlog(pkgdirurl, revision, size): - sys.stdout.write(svn2rpm(pkgdirurl, revision, size)) +def rpmlog(pkgdirurl, revision, size, template): + sys.stdout.write(svn2rpm(pkgdirurl, revision, size, template=template)) def main(): do_command(parse_options, rpmlog) diff --git a/RepSys/commands/submit.py b/RepSys/commands/submit.py index 1c3bf8b..3840296 100644 --- a/RepSys/commands/submit.py +++ b/RepSys/commands/submit.py @@ -1,9 +1,8 @@ #!/usr/bin/python from RepSys import Error, config from RepSys.command import * -from RepSys.util import execcmd from RepSys.rpmutil import get_spec, get_submit_info -from RepSys.util import get_auth +from RepSys.util import get_auth, execcmd import urllib import getopt import sys @@ -70,7 +69,6 @@ def submit(pkgdirurl, revision, target, list=0): user, passwd = urllib.splitpasswd(user) if passwd: raise Error, "do not use a password in your command line" - if type == "https": user, passwd = get_auth(username=user) #soap = NINZ.client.Binding(host=host, @@ -101,12 +99,17 @@ def submit(pkgdirurl, revision, target, list=0): except xmlrpclib.Error, e: raise Error, "remote error: "+str(e) else: - status, output = execcmd("ssh %s /usr/share/repsys/create-srpm '%s' %s %s" % (host, pkgdirurl, revision, target)) + if list: + raise Error, "unable to list targets from svn+ssh:// URLs" + command = "ssh %s /usr/share/repsys/create-srpm '%s' %s %s" % ( + host, pkgdirurl, revision, target) + status, output = execcmd(command) if status == 0: print "Package submitted!" else: sys.exit(status) - + + def main(): do_command(parse_options, submit) diff --git a/RepSys/log.py b/RepSys/log.py index fa007fa..a5e5963 100644 --- a/RepSys/log.py +++ b/RepSys/log.py @@ -1,65 +1,328 @@ #!/usr/bin/python from RepSys import Error, config from RepSys.svn import SVN +from RepSys.util import execcmd + +from Cheetah.Template import Template + +import sys +import os +import re +import time +import locale +import glob import tempfile import shutil -import time -import os -def svn2rpm(pkgdirurl, rev=None, size=None): - concat = config.get("log", "concat", "").split() - svn = SVN() - log = svn.log(os.path.join(pkgdirurl, "current"), start=rev) - if (size is not None): - log = log[:size] - rpmlog = [] - lastauthor = None - for logentry in log: - entryheader = [] - if lastauthor != logentry.author or \ - not (logentry.author in concat or "*" in concat): - entryheader.append(time.strftime("* %a %b %d %Y ", logentry.date)) - entryheader.append(config.get("users", logentry.author, - logentry.author)) - entryheader.append("\n") - entryheader.append(time.strftime("+ %Y-%m-%d %H:%M:%S", - logentry.date)) - entryheader.append(" (%d)" % logentry.revision) - if lastauthor: - rpmlog.append("") - lastauthor = logentry.author - entrylines = [] - first = 1 - for line in logentry.lines: - if line: - line = line.replace("%", "%%") - if first: - first = 0 - if entryheader: - rpmlog.append("".join(entryheader)) - line = line.lstrip() - if line[0] != "-": - nextline = "- " + line - else: - nextline = line - elif line[0] != " " and line[0] != "-": - nextline = " " + line + +locale.setlocale(locale.LC_ALL, "C") + +default_template = """ +#for $rel in $releases_by_author +* $rel.date $rel.author_name <$rel.author_email> $rel.version-$rel.release + ## + #if not $rel.released + (not released yet) + #end if + #for $rev in $rel.release_revisions + #for $line in $rev.lines + $line + #end for + #end for + + #for $author in $rel.authors + + $author.name <$author.email> + #for $rev in $author.revisions + #for $line in $rev.lines + $line + #end for + #end for + + #end for +#end for +""" + +def getrelease(pkgdirurl, rev=None): + """Tries to obtain the version-release of the package for a + yet-not-markrelease revision of the package. + + Is here where things should be changed if "automatic release increasing" + will be used. + """ + svn = SVN(baseurl=pkgdirurl) + tmpdir = tempfile.mktemp() + try: + pkgname = os.path.basename(pkgdirurl) + pkgcurrenturl = os.path.join(pkgdirurl, "current") + specurl = os.path.join(pkgcurrenturl, "SPECS") + if svn.ls(specurl, noerror=1): + svn.export(specurl, tmpdir, rev=rev) + found = glob.glob(os.path.join(tmpdir, "*.spec")) + if found: + specpath = found[0] + command = (("rpm -q --qf '%%{VERSION}-%%{RELEASE}\n' " + "--specfile %s") % specpath) + status, output = execcmd(command) + if status != 0: + raise Error, "Error in command %s: %s" % (command, output) + releases = output.split() + try: + version, release = releases[0].split("-", 1) + except ValueError: + raise Error, "Invalid command output: %s: %s" % \ + (command, output) + return version, release + finally: + if os.path.isdir(tmpdir): + shutil.rmtree(tmpdir) + + +class _Revision: + lines = [] + date = None + raw_date = None + revision = None + author_name = None + author_email = None + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +class _Release(_Revision): + version = None + release = None + revisions = None + + def __init__(self, **kwargs): + self.revisions = [] + _Revision.__init__(self, **kwargs) + + +def format_lines(lines): + first = 1 + entrylines = [] + perexpr = re.compile(r"([^%])%([^%])") + for line in lines: + if line: + line = perexpr.sub("\\1%%\\2", line) + if first: + first = 0 + line = line.lstrip() + if line[0] != "-": + nextline = "- " + line else: nextline = line - if nextline not in entrylines: - rpmlog.append(nextline) - entrylines.append(nextline) - return "\n".join(rpmlog)+"\n" - -def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None): - file = open(specfile) - lines = file.readlines() - file.close() + elif line[0] != " " and line[0] != "-": + nextline = " " + line + else: + nextline = line + if nextline not in entrylines: + entrylines.append(nextline) + return entrylines + + +class _Author: + name = None + email = None + revisions = None + + +def group_releases_by_author(releases): + allauthors = [] + for release in releases: + authors = {} + for revision in release.revisions: + authors.setdefault(revision.author, []).append(revision) + + # all the mess below is to sort by author and by revision number + decorated = [] + for authorname, revs in authors.iteritems(): + author = _Author() + author.name = revs[0].author_name + author.email = revs[0].author_email + revdeco = [(r.revision, r) for r in revs] + revdeco.sort(reverse=1) + author.revisions = [t[1] for t in revdeco] + decorated.append((max(revdeco)[0], author)) + + decorated.sort(reverse=1) + release.authors = [t[1] for t in decorated] + # the difference between a released and a not released _Release is + # the way the release numbers is obtained. So, when this is a + # released, we already have it, but if we don't, we should get de + # version/release string using getrelease and then get the first + first, release.authors = release.authors[0], release.authors[1:] + release.author_name = first.name + release.author_email = first.email + release.date = first.revisions[0].date + release.raw_date = first.revisions[0].raw_date + release.release_revisions = first.revisions + release.revision = first.revisions[0].revision + + return releases + + +emailpat = re.compile("(?P<name>.*?)\s*<(?P<email>.*?)>") + + +def make_release(author=None, revision=None, date=None, lines=None, + entries=[], released=True, version=None, release=None): + rel = _Release() + rel.author = author + found = emailpat.match(config.get("users", author, author or "")) + rel.author_name = (found and found.group("name")) or author + rel.author_email = (found and found.group("email")) or author + rel.revision = revision + rel.version = version + rel.release = release + rel.date = (date and time.strftime("%a %b %d %Y", date)) or None + rel.lines = lines + rel.released = released + for entry in entries: + revision = _Revision() + revision.revision = entry.revision + revision.lines = format_lines(entry.lines) + revision.date = time.strftime("%a %b %d %Y", entry.date) + revision.raw_date = entry.date + revision.author = entry.author + found = emailpat.match(config.get("users", entry.author, entry.author)) + revision.author_name = ((found and found.group("name")) or + entry.author) + revision.author_email = ((found and found.group("email")) or + entry.author) + rel.revisions.append(revision) + return rel + + +def dump_file(releases, template=None): + + templpath = template or config.get("template", "path", None) + params = {} + if templpath is None or not os.path.exists(templpath): + params["source"] = default_template + sys.stderr.write("warning: %s not found. using built-in template.\n"% + templpath) + else: + params["file"] = templpath + releases = group_releases_by_author(releases) + params["searchList"] = [{"releases_by_author" : releases}] + t = Template(**params) + return repr(t) + + +class InvalidEntryError(Exception): + pass + +def parse_repsys_entry(revlog): + # parse entries in the format: + # %repsys <operation> + # key: value + # .. + # <newline> + # <comments> + # + if len(revlog.lines) == 0 or not revlog.lines[0].startswith("%repsys"): + raise InvalidEntryError + try: + data = {"operation" : revlog.lines[0].split()[1]} + except IndexError: + raise InvalidEntryError + for line in revlog.lines[1:]: + if not line: + break + try: + key, value = line.split(":", 1) + except ValueError: + raise InvalidEntryError + data[key.strip().lower()] = value.strip() # ??? + return data + + + +def svn2rpm(pkgdirurl, rev=None, size=None, submit=False, template=None): + concat = config.get("log", "concat", "").split() + svn = SVN(baseurl=pkgdirurl) + pkgreleasesurl = os.path.join(pkgdirurl, "releases") + pkgcurrenturl = os.path.join(pkgdirurl, "current") + releaseslog = svn.log(pkgreleasesurl, start=rev, noerror=1) + currentlog = svn.log(pkgcurrenturl, start=rev) + if size is not None: + currentlog = currentlog[:size] + lastauthor = None + previous_revision = 0 + currelease = None + releases = [] + + releases_data = [] + for relentry in releaseslog[::-1]: + try: + revinfo = parse_repsys_entry(relentry) + except InvalidEntryError: + continue + try: + release_number = int(revinfo["revision"]) + except (KeyError, ValueError): + raise Error, "Error parsing data from log entry from r%s" % \ + relentry.revision + releases_data.append((release_number, relentry, revinfo)) + releases_data.sort() + + for release_number, relentry, revinfo in releases_data: + try: + revinfo = parse_repsys_entry(relentry) + except InvalidEntryError: + continue + + try: + release_revision = int(revinfo["revision"]) + except (ValueError, KeyError): + raise Error, "Error parsing data from log entry from r%s" % \ + relentry.revision + + # get entries newer than 'previous' and older than 'relentry' + entries = [entry for entry in currentlog + if release_revision >= entry.revision and + (previous_revision < entry.revision)] + if not entries: + #XXX probably a forced release, without commits in current/, + # check if this is the right behavior and if some release is + # not being lost. + continue + + release = make_release(author=relentry.author, + revision=relentry.revision, date=relentry.date, + lines=relentry.lines, entries=entries, + version=revinfo["version"], release=revinfo["release"]) + releases.append(release) + previous_revision = release_revision + + # look for commits that have been not submited (released) yet + # this is done by getting all log entries newer (revision larger) + # than releaseslog[0] + latest_revision = releaseslog[0].revision + notsubmitted = [entry for entry in currentlog + if entry.revision > latest_revision] + if notsubmitted: + # if they are not submitted yet, what we have to do is to add + # a release/version number from getrelease() + version, release = getrelease(pkgdirurl) + toprelease = make_release(entries=notsubmitted, released=False, + version=version, release=release) + releases.append(toprelease) + + data = dump_file(releases[::-1], template=template) + return data + + + +def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None, + submit=False, template=None): newlines = [] found = 0 # Strip old changelogs - for line in lines: + for line in open(specfile): if line.startswith("%changelog"): found = 1 elif not found: @@ -70,18 +333,19 @@ def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None): # Create new changelog newlines.append("\n\n%changelog\n") - newlines.append(svn2rpm(pkgdirurl, rev, size)) + newlines.append(svn2rpm(pkgdirurl, rev=rev, size=size, submit=submit, + template=template)) # Merge old changelog, if available oldurl = config.get("log", "oldurl") if oldurl: - svn = SVN() + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: pkgname = os.path.basename(pkgdirurl) pkgoldurl = os.path.join(oldurl, pkgname) if svn.ls(pkgoldurl, noerror=1): - svn.checkout(pkgoldurl, tmpdir, rev=rev) + svn.export(pkgoldurl, tmpdir, rev=rev) logfile = os.path.join(tmpdir, "log") if os.path.isfile(logfile): file = open(logfile) @@ -94,7 +358,12 @@ def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None): # Write new specfile file = open(specfile, "w") - file.writelines(newlines) + file.write("".join(newlines)) file.close() + +if __name__ == "__main__": + l = svn2rpm(sys.argv[1]) + print l + # vim:et:ts=4:sw=4 diff --git a/RepSys/rpmutil.py b/RepSys/rpmutil.py index 7b6446f..40e712e 100644 --- a/RepSys/rpmutil.py +++ b/RepSys/rpmutil.py @@ -10,12 +10,12 @@ import glob import sys import os -def get_spec(pkgdirurl, targetdir="."): - svn = SVN() +def get_spec(pkgdirurl, targetdir=".", submit=False): + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: geturl = "/".join([pkgdirurl, "current", "SPECS"]) - svn.checkout("'%s'" % geturl, tmpdir) + svn.export("'%s'" % geturl, tmpdir) speclist = glob.glob(os.path.join(tmpdir, "*.spec")) if not speclist: raise Error, "no spec files found" @@ -34,8 +34,10 @@ def get_srpm(pkgdirurl, packager = "", revname = 0, svnlog = 0, - scripts = []): - svn = SVN() + scripts = [], + submit = False, + template = None): + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: if mode == "version": @@ -56,8 +58,10 @@ def get_srpm(pkgdirurl, raise Error, "no spec files found" spec = speclist[0] if svnlog: - specfile_svn2rpm(pkgdirurl, spec, revision) - revision = svn.revision(tmpdir) + submit = not not revision + specfile_svn2rpm(pkgdirurl, spec, revision, submit=submit, + template=template) + revisionreal = svn.revision(tmpdir) for script in scripts: status, output = execcmd(script, tmpdir, spec, str(revision), noerror=1) @@ -67,10 +71,12 @@ def get_srpm(pkgdirurl, packager = " --define 'packager %s'" % packager execcmd("rpm -bs --nodeps --define '_topdir %s'%s %s" % (tmpdir, packager, spec)) - if revision: + if revision and revisionreal: srpm = glob.glob(os.path.join(srpmsdir, "*.src.rpm"))[0] + srpminfo = SRPM(srpm) + release = srpminfo.release srpmbase = os.path.basename(srpm) - os.rename(srpm, "%s/@%i:%s" % (srpmsdir, revision, srpmbase)) + os.rename(srpm, "%s/@%s:%s" % (srpmsdir, revisionreal, srpmbase)) srpm = glob.glob(os.path.join(srpmsdir, "*.src.rpm"))[0] if not targetdirs: targetdirs = (".",) @@ -82,7 +88,7 @@ def get_srpm(pkgdirurl, shutil.rmtree(tmpdir) def patch_spec(pkgdirurl, patchfile, log=""): - svn = SVN() + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: geturl = "/".join([pkgdirurl, "current", "SPECS"]) @@ -104,7 +110,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): srpm = SRPM(srpmfile) if appendname: pkgdirurl = "/".join([pkgdirurl, srpm.name]) - svn = SVN() + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: if srpm.epoch: @@ -199,7 +205,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): (version, srpm.release)) def create_package(pkgdirurl, log="", verbose=0): - svn = SVN() + svn = SVN(baseurl=pkgdirurl) tmpdir = tempfile.mktemp() try: basename = os.path.basename(pkgdirurl) @@ -228,8 +234,19 @@ def create_package(pkgdirurl, log="", verbose=0): if os.path.isdir(tmpdir): shutil.rmtree(tmpdir) + +def create_markrelease_log(version, release, revision): + log = """%%repsys markrelease +version: %s +release: %s +revision: %s + +%s""" % (version, release, revision, + ("Copying %s-%s to releases/ directory." % (version, release))) + return log + def mark_release(pkgdirurl, version, release, revision): - svn = SVN() + svn = SVN(baseurl=pkgdirurl) releasesurl = "/".join([pkgdirurl, "releases"]) versionurl = "/".join([releasesurl, version]) releaseurl = "/".join([versionurl, release]) @@ -246,12 +263,12 @@ def mark_release(pkgdirurl, version, release, revision): svn.copy(currenturl, pristineurl, log="Copying release %s-%s to pristine/ directory." % (version, release)) + markreleaselog = create_markrelease_log(version, release, revision) svn.copy(currenturl, releaseurl, rev=revision, - log="Copying release %s-%s to releases/ directory." % - (version, release)) + log=markreleaselog) def check_changed(url, all=0, show=0, verbose=0): - svn = SVN() + svn = SVN(baseurl=pkgdirurl) if all: baseurl = url packages = [] @@ -311,7 +328,7 @@ def check_changed(url, all=0, show=0, verbose=0): "nopristine": nopristine} def checkout(url, path=None, revision=None): - svn = SVN() + svn = SVN(baseurl=pkgdirurl) current = os.path.join(url, "current") if path is None: _, path = os.path.split(url) @@ -337,7 +354,8 @@ def get_submit_info(path): if not os.path.isdir(os.path.join(path, ".svn")): raise Error, "subversion directory not found" - svn = SVN() + svn = SVN(baseurl=pkgdirurl) + # Now, extract the package name. for line in svn.info(path): diff --git a/RepSys/svn.py b/RepSys/svn.py index 6a42e6d..4804a39 100644 --- a/RepSys/svn.py +++ b/RepSys/svn.py @@ -1,5 +1,5 @@ from RepSys import Error, config, pexpect -from RepSys.util import execcmd +from RepSys.util import execcmd, get_auth import sys import re import time @@ -17,18 +17,18 @@ class SVNLogEntry: return cmp(self.date, other.date) class SVN: - def __init__(self, username=None, password=None): - if not username: - username = config.get("auth", "username") - if not password: - password = config.get("auth", "password") + def __init__(self, username=None, password=None, noauth=0, baseurl=None): + self.noauth = noauth or ( + baseurl and ( + baseurl.startswith("file:") or + baseurl.startswith("svn+ssh:"))) + if not self.noauth: # argh + self.username, self.password = get_auth() - self.username = username - self.password = password def _execsvn(self, *args, **kwargs): cmdstr = "svn "+" ".join(args) - if kwargs.get("local"): + if kwargs.get("local") or kwargs.get("noauth") or self.noauth: return execcmd(cmdstr, **kwargs) show = kwargs.get("show") noerror = kwargs.get("noerror") @@ -132,6 +132,11 @@ class SVN: self._add_log(cmd, kwargs) return self._execsvn_success(*cmd, **kwargs) + def export(self, url, targetpath, **kwargs): + cmd = ["export", "'%s'" % url, targetpath] + self._add_revision(cmd, kwargs, optional=1) + return self._execsvn_success(*cmd, **kwargs) + def checkout(self, url, targetpath, **kwargs): cmd = ["checkout", "'%s'" % url, targetpath] self._add_revision(cmd, kwargs, optional=1) @@ -260,7 +265,7 @@ class SVN: if status != 0: return None - revheader = re.compile("^r(?P<revision>[0-9]+) \| (?P<author>.+?) \| (?P<date>.+?) \| (?P<lines>[0-9]+) (?:line|lines)") + revheader = re.compile("^r(?P<revision>[0-9]+) \| (?P<author>[^\|]+) \| (?P<date>[^\|]+) \| (?P<lines>[0-9]+) (?:line|lines)$") logseparator = "-"*72 linesleft = 0 entry = None |