blob: 475a9cd62227132926a121469dfba59cfa01675c [file] [log] [blame]
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Usage: logimport [options] <database-name> <repoa-url>
# Options:
# --svn=PATH Use a non-default svn binary
# --debug Enable debug-level logging to logimport.debug.log
# --sqldebug Enable SQL-level logging to logimport.sql.log
#
# Converts the history of the repository at <repos-url> into a
# single-tree directory index.
import logging
import subprocess
import sys
try:
from lxml.etree import iterparse
except ImportError:
from xml.etree.ElementTree import iterparse
from dirindex import Dirent, Index, Revision
def parse(index, stream):
kindmap = {"dir": Dirent.DIR, "file": Dirent.FILE}
version = None
revcount = 0
for event, logentry in iterparse(stream):
if logentry.tag != "logentry":
continue
version = int(logentry.get("revision"))
revcount += 1
if revcount == 1:
logging.info("initial: r%d", version)
else:
logger = not revcount % 1000 and logging.info or logging.debug
logger("%d: r%d", revcount, version)
created = logentry.find("date")
if created is not None:
created = created.text
else:
created = ""
author = logentry.find("author")
if author is not None:
author = author.text
log = logentry.find("msg")
if log is not None:
log = log.text
with Revision(index, version, created, author, log) as rev:
actionmap = dict(A = (rev.add, True),
R = (rev.replace, True),
M = (rev.modify, False),
D = (rev.delete, False))
for path in logentry.findall("paths/path"):
abspath = path.text
action = path.get("action")
handler, newnode = actionmap[action]
if not newnode:
logging.debug(" %s %s", action, abspath)
handler(abspath)
continue
kindstr = path.get("kind")
kind = kindmap[kindstr]
frompath = path.get("copyfrom-path")
if frompath is not None:
fromrev = int(path.get("copyfrom-rev"))
logging.debug(" %s %-4s %s <- %s@%d",
action, kindstr, abspath,
frompath, fromrev)
handler(abspath, kind, frompath, fromrev)
else:
logging.debug(" %s %-4s %s", action, kindstr, abspath)
handler(abspath, kind)
if version is not None:
logging.info("final: r%d", version)
def logimport(database, url, svn):
try:
index = Index(database)
index.cursor.execute("PRAGMA journal_mode = MEMORY")
index.cursor.execute("PRAGMA locking_mode = EXCLUSIVE")
index.cursor.execute("PRAGMA synchronous = OFF")
index.cursor.execute("PRAGMA cache_size = -100000")
index.initialize()
svnlog = subprocess.Popen(
[svn, "log", "-v", "--xml", "-r1:HEAD", url],
stdout = subprocess.PIPE)
parse(index, svnlog.stdout)
return svnlog.wait()
except:
logging.exception("logimport failed")
try:
svnlog.wait()
except:
pass
return 2
def main():
import logging.config
from optparse import OptionParser
from dirindex import SQLobject
parser = OptionParser("Usage: %prog [options] <database-name> <repoa-url>")
parser.add_option("--svn", action="store", default="svn",
help="Use a non-default svn binary", metavar="PATH")
parser.add_option("--debug", action="store_true", default=False,
help="Enable debug-level logging to logimport.debug.log")
parser.add_option("--sqldebug", action="store_true", default=False,
help="Enable SQL-level logging to logimport.debug.log")
opts, args = parser.parse_args()
if len(args) != 2:
parser.error("wrong number of arguments")
database, url = args
logconfig = {
"version": 1,
"formatters": {
"console": {"format": "%(levelname)-7s %(message)s"},
"logfile": {"format": "%(asctime)s %(levelname)-7s %(message)s"}},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": logging.INFO,
"stream": sys.stderr,
"formatter": "console"}},
"root": {
"level": logging.INFO,
"handlers": ["console"]}}
handlers = logconfig["root"]["handlers"]
if opts.debug:
logconfig["root"]["level"] = logging.DEBUG
logconfig["handlers"]["debug"] = {
"class": "logging.FileHandler",
"level": logging.DEBUG,
"mode": "w",
"filename": "./logimport.debug.log",
"formatter": "logfile"}
handlers.append("debug")
if opts.sqldebug:
logconfig["root"]["level"] = SQLobject.LOGLEVEL
logconfig["handlers"]["sqldebug"] = {
"class": "logging.FileHandler",
"level": SQLobject.LOGLEVEL,
"mode": "w",
"filename": "./logimport.sql.log",
"formatter": "logfile"}
handlers.append("sqldebug")
logging.config.dictConfig(logconfig)
sys.exit(logimport(database, url, opts.svn))
if __name__ == "__main__":
main()