svn commit: r196100 - svnadmin/tools
Peter Wemm
peter at FreeBSD.org
Tue Aug 11 21:21:20 UTC 2009
Author: peter
Date: Tue Aug 11 21:21:20 2009
New Revision: 196100
URL: http://svn.freebsd.org/changeset/base/196100
Log:
Clone export.py before splitting into two parts.
Added:
svnadmin/tools/export1.py
- copied unchanged from r196099, svnadmin/tools/export.py
svnadmin/tools/export2.py
- copied unchanged from r196099, svnadmin/tools/export.py
Copied: svnadmin/tools/export1.py (from r196099, svnadmin/tools/export.py)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ svnadmin/tools/export1.py Tue Aug 11 21:21:20 2009 (r196100, copy of r196099, svnadmin/tools/export.py)
@@ -0,0 +1,381 @@
+#!/usr/local/bin/python
+
+# $FreeBSD$
+
+import string
+import sys
+import time
+import os
+import popen2
+import tempfile
+from svn import core, fs, delta, repos
+
+do_keywords = False
+
+# SVN's API structure is to do callbacks to a class to get notifications
+class ChangeReceiver(delta.Editor):
+ def __init__(self, fs_root, base_root, rev, fs_ptr, pool, do_kw):
+ self.fs_root = fs_root
+ self.base_root = base_root
+ self.fs_ptr = fs_ptr
+ self.rev = int(rev)
+ self.pool = pool
+ self.do_kw = do_kw
+ self.changes = []
+
+ def delete_entry(self, path, revision, parent_baton, pool):
+ self.changes.append(['D', path])
+
+ def add_file(self, path, parent_baton,
+ copyfrom_path, copyfrom_revision, file_pool):
+ self.changes.append(['A', path])
+ return [ '_', ' ', None ]
+
+ def open_file(self, path, parent_baton, base_revision, file_pool):
+ return [ '_', ' ', path ]
+
+ def apply_textdelta(self, file_baton, base_checksum):
+ text_mod, prop_mod, path = file_baton
+ file_baton[0] = 'U'
+ # no handler
+ return None
+
+ def change_file_prop(self, file_baton, name, value, pool):
+ text_mod, prop_mod, path = file_baton
+ if self.do_kw and name == core.SVN_PROP_KEYWORDS:
+ file_baton[1] = 'U'
+
+ def close_file(self, file_baton, text_checksum):
+ text_mod, prop_mod, path = file_baton
+ # test the path. it will be None if we added this file.
+ if path:
+ status = text_mod + prop_mod
+ # was there some kind of change?
+ if status != '_ ':
+ self.changes.append(['U', path])
+
+# Last path component
+def _basename(path):
+ idx = path.rfind('/')
+ if idx == -1:
+ return path
+ return path[idx+1:]
+
+# Directory component
+def _dirname(path):
+ idx = path.rfind('/')
+ if idx == -1:
+ return ''
+ return path[:idx]
+
+# Keep track of best common prefix per directory group to keep commit time down
+class pathcollector():
+ def __init__(self):
+ self.paths = {}
+
+ # Not really a set.. calculates common prefix instead.
+ def __setitem__(self, prefix, dir):
+ # If we haven't seen a dir, start here
+ if not self.paths.has_key(prefix):
+ self.paths[prefix] = dir
+ return
+ # If it is the same dir, we're finished
+ if self.paths[prefix] == dir:
+ return
+ # See if we've found a common parent
+ parent = dir
+ while _dirname(parent) != parent:
+ parent = _dirname(parent)
+ # See if common prefix works
+ if self.paths[prefix] == parent:
+ return
+ # Raise common prefix
+ if self.paths[prefix].startswith(parent):
+ self.paths[prefix] = parent
+ return
+ print "WTF?"
+ assert false
+
+ def __iter__(self):
+ return self.paths.iteritems()
+
+# Issue a cvs command, aborting if a problem happens
+def do_cvs(cvspath, dir, cmd):
+ ioerror = False
+ print("cvs path %s, dir %s, cmd %s" % (cvspath, dir, cmd))
+ cwd = os.getcwd()
+ os.chdir(os.path.join(cvspath, dir))
+ pipe = popen2.Popen3(cmd)
+ os.chdir(cwd)
+ output = ''
+ try:
+ output = pipe.fromchild.readlines()
+ pipe.fromchild.close()
+ pipe.tochild.close()
+ except IOError:
+ ioerror = True
+ rv = pipe.wait()
+ failed = (rv != 0) or ioerror
+ print 'Cvs output: ', output
+ return failed
+
+# Dump a file from svn into cvs. This has to apply the delta to the previous rev.
+def dump_file(fs_ptr, fs_root, rev, svnpath, cvspath, author, date, pool, workpath):
+ if do_keywords:
+ kw = fs.node_prop(fs_root, svnpath, core.SVN_PROP_KEYWORDS)
+ if not kw:
+ kw = ''
+ str = '$' + 'FreeBSD: %s %s %s %s $' % (cvspath, rev, date, author)
+ #str = '$' + 'FreeBSDId: %s %s %s %s $' % (cvspath, rev, date, author)
+
+ subpool = core.svn_pool_create(pool)
+ stream = core.Stream(fs.file_contents(fs_root, svnpath, subpool))
+ str_list = []
+ while 1:
+ data = stream.read(core.SVN_STREAM_CHUNK_SIZE)
+ str_list.append(data)
+ if len(data) < core.SVN_STREAM_CHUNK_SIZE:
+ break
+ string = ''.join(str_list)
+ # Expand keywords
+ if do_keywords:
+ if kw == r'FreeBSD=%H':
+ old = '$' + 'FreeBSD$'
+ string = string.replace(old, str)
+ cvsfile = os.path.join(workpath, cvspath)
+# sys.stdout.write('File contents:\n=========\n')
+# sys.stdout.write(string)
+# sys.stdout.write('=========\n')
+ executable = fs.node_prop(fs_root, svnpath, core.SVN_PROP_EXECUTABLE)
+ if executable:
+ mode = 0777
+ else:
+ mode = 0666
+ outfile = os.open(cvsfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, mode)
+ if not outfile:
+ sys.exit('cannot open %s for write' % cvsfile)
+ n = os.write(outfile, string)
+ if n != len(string):
+ sys.exit('short write. %d instead of %d' % (n, len(string)))
+ os.close(outfile)
+ core.svn_pool_destroy(subpool)
+
+# List of paths we export to cvs, and what branch tag (or None for HEAD)
+# Hard coded for now. This will be automatically implied based on path transforms.
+maptable = [
+ # -current head
+ ( 'head/', None ),
+ # -stable branches
+ ( 'stable/2.0.5/', 'RELENG_2_0_5' ),
+ ( 'stable/2.1/', 'RELENG_2_1_0' ),
+ ( 'stable/2.2/', 'RELENG_2_2' ),
+ ( 'stable/3/', 'RELENG_3' ),
+ ( 'stable/4/', 'RELENG_4' ),
+ ( 'stable/5/', 'RELENG_5' ),
+ ( 'stable/6/', 'RELENG_6' ),
+ ( 'stable/7/', 'RELENG_7' ),
+ # errata / security / releng branches
+ ( 'releng/ALPHA_2_0/','ALPHA_2_0' ),
+ ( 'releng/BETA_2_0/', 'BETA_2_0' ),
+ ( 'releng/4.3/', 'RELENG_4_3' ),
+ ( 'releng/4.4/', 'RELENG_4_4' ),
+ ( 'releng/4.5/', 'RELENG_4_5' ),
+ ( 'releng/4.6/', 'RELENG_4_6' ),
+ ( 'releng/4.7/', 'RELENG_4_7' ),
+ ( 'releng/4.8/', 'RELENG_4_8' ),
+ ( 'releng/4.9/', 'RELENG_4_9' ),
+ ( 'releng/4.10/', 'RELENG_4_10' ),
+ ( 'releng/4.11/', 'RELENG_4_11' ),
+ ( 'releng/4.12/', 'RELENG_4_12' ),
+ ( 'releng/5.0/', 'RELENG_5_0' ),
+ ( 'releng/5.1/', 'RELENG_5_1' ),
+ ( 'releng/5.2/', 'RELENG_5_2' ),
+ ( 'releng/5.3/', 'RELENG_5_3' ),
+ ( 'releng/5.4/', 'RELENG_5_4' ),
+ ( 'releng/5.5/', 'RELENG_5_5' ),
+ ( 'releng/5.6/', 'RELENG_5_6' ),
+ ( 'releng/6.0/', 'RELENG_6_0' ),
+ ( 'releng/6.1/', 'RELENG_6_1' ),
+ ( 'releng/6.2/', 'RELENG_6_2' ),
+ ( 'releng/6.3/', 'RELENG_6_3' ),
+ ( 'releng/6.4/', 'RELENG_6_4' ),
+ ( 'releng/7.0/', 'RELENG_7_0' ),
+ ( 'releng/7.1/', 'RELENG_7_1' ),
+ ( 'releng/7.2/', 'RELENG_7_2' ),
+]
+
+def map2cvs(svnpath):
+ for prefix, branch in maptable:
+ plen = len(prefix)
+ if svnpath.startswith(prefix):
+ return 'src/' + svnpath[plen:], branch
+ return None, None
+
+# List of special remap cases.
+roottable = [
+ ( 'svnadmin/conf/access', 'CVSROOT/access', 'CVSROOT' ),
+ ( 'svnadmin/conf/mentors', 'CVSROOT/mentors', 'CVSROOT' ),
+]
+
+def maproot(p):
+ for svnpath, path, dir in roottable:
+ if p == svnpath:
+ return path, dir
+ return None, None
+
+# Add intermediate directories to the cvs checkout area as needed.
+# XXX should use 'cvs update -d -l' if the dir exists in cvsroot
+def makedirs(cvspath, path, base):
+ #print 'Makedirs:', cvspath, path
+ if not path.startswith(base):
+ sys.exit('Illegal path %s' % path)
+ if path == base:
+ return
+ makedirs(cvspath, _dirname(path), base)
+ fullpath = os.path.join(cvspath, path)
+ if os.path.isfile(fullpath):
+ sys.exit('Dest dir is a file' % path)
+ if not os.path.isdir(fullpath):
+ try:
+ #print "Making directory " + fullpath
+ os.makedirs(fullpath)
+ failed = do_cvs(cvspath, _dirname(path), "cvs -q add %s" % _basename(path))
+ assert not failed
+ except OSError:
+ sys.exit('Cannot mkdir %s' % path)
+ #print 'Dirpath complete: ' + path
+
+# Export a single change to cvs.
+def exportrev(pool, fs_ptr, rev, cvspath):
+ def authz_cb(root, path, pool):
+ return True
+
+ subpool = core.svn_pool_create(pool)
+ # Connect up to the revision
+ fs_root = fs.revision_root(fs_ptr, rev, subpool)
+ base_root = fs.revision_root(fs_ptr, rev - 1, subpool)
+ editor = ChangeReceiver(fs_root, base_root, rev, fs_ptr, subpool, do_keywords)
+ e_ptr, e_baton = delta.make_editor(editor, subpool)
+ repos.dir_delta(base_root, '', '', fs_root, '', e_ptr, e_baton, authz_cb, 0, 1, 0, 0, subpool)
+
+ # Author
+ author = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_AUTHOR)
+ if not author:
+ author = 'NoAuthor'
+ if author == 'davidg':
+ author = 'dg'
+ os.environ['CVS_AUTHOR'] = author
+ print 'Author: ' + author
+
+ # Date
+ date = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_DATE)
+ if date:
+ aprtime = core.svn_time_from_cstring(date)
+ secs = aprtime / 1000000 # aprtime is microseconds; make seconds
+ tm = time.gmtime(secs)
+ date = time.strftime('%Y-%m-%d %H:%M:%SZ', tm)
+ os.environ['CVS_TIMESTAMP'] = "%d" % secs
+ else:
+ date = 'NoDate'
+ if os.environ.has_key('CVS_TIMESTAMP'):
+ del os.environ['CVS_TIMESTAMP']
+
+ # Build log message to export
+ cvslog = 'SVN rev %d on %s by %s\n' % (rev, date, author)
+ svnlog = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_LOG)
+ if svnlog:
+ cvslog += '\n' + svnlog
+
+ pc = pathcollector()
+
+ for k, p in editor.changes:
+ #print 'Path ', p
+ # Hack, hack
+ (path, dir) = maproot(p)
+ if path:
+ workpath = cvspath
+ dump_file(fs_ptr, fs_root, rev, p, path, author, date, subpool, workpath)
+ pc[workpath] = dir
+ continue
+ (path, tag) = map2cvs(p)
+ if not path:
+ continue
+ if tag:
+ workpath = os.path.join(cvspath, tag)
+ uptag = '-r ' + tag
+ else:
+ workpath = cvspath
+ uptag = '-A'
+ #print workpath
+ if not os.path.isdir(workpath):
+ os.makedirs(workpath)
+ if not os.path.isdir(os.path.join(workpath, 'src')):
+ failed = do_cvs(workpath, '', "cvs -Rq co %s src" % uptag)
+ assert not failed
+ # at this point, the top directory and /src should exist
+ print p, path, k
+ # hacks
+ #if p == 'head/contrib/file/FREEBSD-upgrade' and k == 'A':
+ # continue
+ #if p == 'head/contrib/file/magic2mime' and k == 'A':
+ # continue
+ #if p == 'head/lib/libc/stdio/asprintf.c' and k == 'D':
+ # continue
+ if p == 'head/tools/build/options/WITH_BIND_LIBS' and k == 'A' and rev == 193280:
+ continue
+ makedirs(workpath, _dirname(path), 'src')
+ # Now the directory for the files must exist, and branch tag will be sticky
+ assert os.path.isdir(os.path.join(workpath, _dirname(path)))
+ assert k == 'A' or k == 'U' or k == 'D'
+
+ if k == 'A' or k == 'U':
+ print 'add/update file ' + path + '.'
+ destpath = os.path.join(workpath, path)
+ existed = os.path.isfile(destpath)
+ dump_file(fs_ptr, fs_root, rev, p, path, author, date, subpool, workpath)
+ if not existed:
+ print 'cvs add file ' + path + '.'
+ failed = do_cvs(workpath, _dirname(path), "cvs -q add %s" % _basename(path))
+ assert not failed
+ elif k == 'D':
+ print 'cvs rm -f file ' + path + '.'
+ failed = do_cvs(workpath, _dirname(path), "cvs -q rm -f %s" % _basename(path))
+ assert not failed
+ pc[workpath] = _dirname(path)
+
+ # aggregate the commit
+ for root, dir in pc:
+ fd, logfile = tempfile.mkstemp()
+ os.write(fd, cvslog)
+ os.close(fd)
+ failed = do_cvs(root, dir, "cvs -q commit -F %s" % logfile)
+ assert not failed
+ os.remove(logfile)
+ core.svn_pool_destroy(subpool)
+
+# Loop for the export range
+def export(pool, repos_path, cvspath):
+ repos_path = core.svn_path_canonicalize(repos_path)
+ fs_ptr = repos.fs(repos.open(repos_path, pool))
+ while True:
+ curr_rev = fs.youngest_rev(fs_ptr)
+ last_rev = int(fs.revision_prop(fs_ptr, 0, 'fbsd:lastexp'))
+ if last_rev < curr_rev:
+ time.sleep(5)
+ print '%d %s' % (last_rev, curr_rev)
+ rev = '%d' % (last_rev + 1)
+ print '==========> export rev ' + rev
+ exportrev(pool, fs_ptr, last_rev + 1, cvspath)
+ fs.change_rev_prop(fs_ptr, 0, 'fbsd:lastexp', rev)
+ continue
+ print "."
+ time.sleep(15)
+
+
+if __name__ == '__main__':
+ print "Version: $FreeBSD$"
+ os.environ['CVSROOT'] = '/r/ncvs'
+ core.run_app(export, '/r/svnmirror/base', '/r/svn2cvs/cvs')
+ # test rig
+ #os.environ['CVSROOT'] = '/home/peter/exp/cvs'
+ #core.run_app(export, '/home/peter/exp/svn', '/home/peter/exp/co')
Copied: svnadmin/tools/export2.py (from r196099, svnadmin/tools/export.py)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ svnadmin/tools/export2.py Tue Aug 11 21:21:20 2009 (r196100, copy of r196099, svnadmin/tools/export.py)
@@ -0,0 +1,381 @@
+#!/usr/local/bin/python
+
+# $FreeBSD$
+
+import string
+import sys
+import time
+import os
+import popen2
+import tempfile
+from svn import core, fs, delta, repos
+
+do_keywords = False
+
+# SVN's API structure is to do callbacks to a class to get notifications
+class ChangeReceiver(delta.Editor):
+ def __init__(self, fs_root, base_root, rev, fs_ptr, pool, do_kw):
+ self.fs_root = fs_root
+ self.base_root = base_root
+ self.fs_ptr = fs_ptr
+ self.rev = int(rev)
+ self.pool = pool
+ self.do_kw = do_kw
+ self.changes = []
+
+ def delete_entry(self, path, revision, parent_baton, pool):
+ self.changes.append(['D', path])
+
+ def add_file(self, path, parent_baton,
+ copyfrom_path, copyfrom_revision, file_pool):
+ self.changes.append(['A', path])
+ return [ '_', ' ', None ]
+
+ def open_file(self, path, parent_baton, base_revision, file_pool):
+ return [ '_', ' ', path ]
+
+ def apply_textdelta(self, file_baton, base_checksum):
+ text_mod, prop_mod, path = file_baton
+ file_baton[0] = 'U'
+ # no handler
+ return None
+
+ def change_file_prop(self, file_baton, name, value, pool):
+ text_mod, prop_mod, path = file_baton
+ if self.do_kw and name == core.SVN_PROP_KEYWORDS:
+ file_baton[1] = 'U'
+
+ def close_file(self, file_baton, text_checksum):
+ text_mod, prop_mod, path = file_baton
+ # test the path. it will be None if we added this file.
+ if path:
+ status = text_mod + prop_mod
+ # was there some kind of change?
+ if status != '_ ':
+ self.changes.append(['U', path])
+
+# Last path component
+def _basename(path):
+ idx = path.rfind('/')
+ if idx == -1:
+ return path
+ return path[idx+1:]
+
+# Directory component
+def _dirname(path):
+ idx = path.rfind('/')
+ if idx == -1:
+ return ''
+ return path[:idx]
+
+# Keep track of best common prefix per directory group to keep commit time down
+class pathcollector():
+ def __init__(self):
+ self.paths = {}
+
+ # Not really a set.. calculates common prefix instead.
+ def __setitem__(self, prefix, dir):
+ # If we haven't seen a dir, start here
+ if not self.paths.has_key(prefix):
+ self.paths[prefix] = dir
+ return
+ # If it is the same dir, we're finished
+ if self.paths[prefix] == dir:
+ return
+ # See if we've found a common parent
+ parent = dir
+ while _dirname(parent) != parent:
+ parent = _dirname(parent)
+ # See if common prefix works
+ if self.paths[prefix] == parent:
+ return
+ # Raise common prefix
+ if self.paths[prefix].startswith(parent):
+ self.paths[prefix] = parent
+ return
+ print "WTF?"
+ assert false
+
+ def __iter__(self):
+ return self.paths.iteritems()
+
+# Issue a cvs command, aborting if a problem happens
+def do_cvs(cvspath, dir, cmd):
+ ioerror = False
+ print("cvs path %s, dir %s, cmd %s" % (cvspath, dir, cmd))
+ cwd = os.getcwd()
+ os.chdir(os.path.join(cvspath, dir))
+ pipe = popen2.Popen3(cmd)
+ os.chdir(cwd)
+ output = ''
+ try:
+ output = pipe.fromchild.readlines()
+ pipe.fromchild.close()
+ pipe.tochild.close()
+ except IOError:
+ ioerror = True
+ rv = pipe.wait()
+ failed = (rv != 0) or ioerror
+ print 'Cvs output: ', output
+ return failed
+
+# Dump a file from svn into cvs. This has to apply the delta to the previous rev.
+def dump_file(fs_ptr, fs_root, rev, svnpath, cvspath, author, date, pool, workpath):
+ if do_keywords:
+ kw = fs.node_prop(fs_root, svnpath, core.SVN_PROP_KEYWORDS)
+ if not kw:
+ kw = ''
+ str = '$' + 'FreeBSD: %s %s %s %s $' % (cvspath, rev, date, author)
+ #str = '$' + 'FreeBSDId: %s %s %s %s $' % (cvspath, rev, date, author)
+
+ subpool = core.svn_pool_create(pool)
+ stream = core.Stream(fs.file_contents(fs_root, svnpath, subpool))
+ str_list = []
+ while 1:
+ data = stream.read(core.SVN_STREAM_CHUNK_SIZE)
+ str_list.append(data)
+ if len(data) < core.SVN_STREAM_CHUNK_SIZE:
+ break
+ string = ''.join(str_list)
+ # Expand keywords
+ if do_keywords:
+ if kw == r'FreeBSD=%H':
+ old = '$' + 'FreeBSD$'
+ string = string.replace(old, str)
+ cvsfile = os.path.join(workpath, cvspath)
+# sys.stdout.write('File contents:\n=========\n')
+# sys.stdout.write(string)
+# sys.stdout.write('=========\n')
+ executable = fs.node_prop(fs_root, svnpath, core.SVN_PROP_EXECUTABLE)
+ if executable:
+ mode = 0777
+ else:
+ mode = 0666
+ outfile = os.open(cvsfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, mode)
+ if not outfile:
+ sys.exit('cannot open %s for write' % cvsfile)
+ n = os.write(outfile, string)
+ if n != len(string):
+ sys.exit('short write. %d instead of %d' % (n, len(string)))
+ os.close(outfile)
+ core.svn_pool_destroy(subpool)
+
+# List of paths we export to cvs, and what branch tag (or None for HEAD)
+# Hard coded for now. This will be automatically implied based on path transforms.
+maptable = [
+ # -current head
+ ( 'head/', None ),
+ # -stable branches
+ ( 'stable/2.0.5/', 'RELENG_2_0_5' ),
+ ( 'stable/2.1/', 'RELENG_2_1_0' ),
+ ( 'stable/2.2/', 'RELENG_2_2' ),
+ ( 'stable/3/', 'RELENG_3' ),
+ ( 'stable/4/', 'RELENG_4' ),
+ ( 'stable/5/', 'RELENG_5' ),
+ ( 'stable/6/', 'RELENG_6' ),
+ ( 'stable/7/', 'RELENG_7' ),
+ # errata / security / releng branches
+ ( 'releng/ALPHA_2_0/','ALPHA_2_0' ),
+ ( 'releng/BETA_2_0/', 'BETA_2_0' ),
+ ( 'releng/4.3/', 'RELENG_4_3' ),
+ ( 'releng/4.4/', 'RELENG_4_4' ),
+ ( 'releng/4.5/', 'RELENG_4_5' ),
+ ( 'releng/4.6/', 'RELENG_4_6' ),
+ ( 'releng/4.7/', 'RELENG_4_7' ),
+ ( 'releng/4.8/', 'RELENG_4_8' ),
+ ( 'releng/4.9/', 'RELENG_4_9' ),
+ ( 'releng/4.10/', 'RELENG_4_10' ),
+ ( 'releng/4.11/', 'RELENG_4_11' ),
+ ( 'releng/4.12/', 'RELENG_4_12' ),
+ ( 'releng/5.0/', 'RELENG_5_0' ),
+ ( 'releng/5.1/', 'RELENG_5_1' ),
+ ( 'releng/5.2/', 'RELENG_5_2' ),
+ ( 'releng/5.3/', 'RELENG_5_3' ),
+ ( 'releng/5.4/', 'RELENG_5_4' ),
+ ( 'releng/5.5/', 'RELENG_5_5' ),
+ ( 'releng/5.6/', 'RELENG_5_6' ),
+ ( 'releng/6.0/', 'RELENG_6_0' ),
+ ( 'releng/6.1/', 'RELENG_6_1' ),
+ ( 'releng/6.2/', 'RELENG_6_2' ),
+ ( 'releng/6.3/', 'RELENG_6_3' ),
+ ( 'releng/6.4/', 'RELENG_6_4' ),
+ ( 'releng/7.0/', 'RELENG_7_0' ),
+ ( 'releng/7.1/', 'RELENG_7_1' ),
+ ( 'releng/7.2/', 'RELENG_7_2' ),
+]
+
+def map2cvs(svnpath):
+ for prefix, branch in maptable:
+ plen = len(prefix)
+ if svnpath.startswith(prefix):
+ return 'src/' + svnpath[plen:], branch
+ return None, None
+
+# List of special remap cases.
+roottable = [
+ ( 'svnadmin/conf/access', 'CVSROOT/access', 'CVSROOT' ),
+ ( 'svnadmin/conf/mentors', 'CVSROOT/mentors', 'CVSROOT' ),
+]
+
+def maproot(p):
+ for svnpath, path, dir in roottable:
+ if p == svnpath:
+ return path, dir
+ return None, None
+
+# Add intermediate directories to the cvs checkout area as needed.
+# XXX should use 'cvs update -d -l' if the dir exists in cvsroot
+def makedirs(cvspath, path, base):
+ #print 'Makedirs:', cvspath, path
+ if not path.startswith(base):
+ sys.exit('Illegal path %s' % path)
+ if path == base:
+ return
+ makedirs(cvspath, _dirname(path), base)
+ fullpath = os.path.join(cvspath, path)
+ if os.path.isfile(fullpath):
+ sys.exit('Dest dir is a file' % path)
+ if not os.path.isdir(fullpath):
+ try:
+ #print "Making directory " + fullpath
+ os.makedirs(fullpath)
+ failed = do_cvs(cvspath, _dirname(path), "cvs -q add %s" % _basename(path))
+ assert not failed
+ except OSError:
+ sys.exit('Cannot mkdir %s' % path)
+ #print 'Dirpath complete: ' + path
+
+# Export a single change to cvs.
+def exportrev(pool, fs_ptr, rev, cvspath):
+ def authz_cb(root, path, pool):
+ return True
+
+ subpool = core.svn_pool_create(pool)
+ # Connect up to the revision
+ fs_root = fs.revision_root(fs_ptr, rev, subpool)
+ base_root = fs.revision_root(fs_ptr, rev - 1, subpool)
+ editor = ChangeReceiver(fs_root, base_root, rev, fs_ptr, subpool, do_keywords)
+ e_ptr, e_baton = delta.make_editor(editor, subpool)
+ repos.dir_delta(base_root, '', '', fs_root, '', e_ptr, e_baton, authz_cb, 0, 1, 0, 0, subpool)
+
+ # Author
+ author = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_AUTHOR)
+ if not author:
+ author = 'NoAuthor'
+ if author == 'davidg':
+ author = 'dg'
+ os.environ['CVS_AUTHOR'] = author
+ print 'Author: ' + author
+
+ # Date
+ date = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_DATE)
+ if date:
+ aprtime = core.svn_time_from_cstring(date)
+ secs = aprtime / 1000000 # aprtime is microseconds; make seconds
+ tm = time.gmtime(secs)
+ date = time.strftime('%Y-%m-%d %H:%M:%SZ', tm)
+ os.environ['CVS_TIMESTAMP'] = "%d" % secs
+ else:
+ date = 'NoDate'
+ if os.environ.has_key('CVS_TIMESTAMP'):
+ del os.environ['CVS_TIMESTAMP']
+
+ # Build log message to export
+ cvslog = 'SVN rev %d on %s by %s\n' % (rev, date, author)
+ svnlog = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_LOG)
+ if svnlog:
+ cvslog += '\n' + svnlog
+
+ pc = pathcollector()
+
+ for k, p in editor.changes:
+ #print 'Path ', p
+ # Hack, hack
+ (path, dir) = maproot(p)
+ if path:
+ workpath = cvspath
+ dump_file(fs_ptr, fs_root, rev, p, path, author, date, subpool, workpath)
+ pc[workpath] = dir
+ continue
+ (path, tag) = map2cvs(p)
+ if not path:
+ continue
+ if tag:
+ workpath = os.path.join(cvspath, tag)
+ uptag = '-r ' + tag
+ else:
+ workpath = cvspath
+ uptag = '-A'
+ #print workpath
+ if not os.path.isdir(workpath):
+ os.makedirs(workpath)
+ if not os.path.isdir(os.path.join(workpath, 'src')):
+ failed = do_cvs(workpath, '', "cvs -Rq co %s src" % uptag)
+ assert not failed
+ # at this point, the top directory and /src should exist
+ print p, path, k
+ # hacks
+ #if p == 'head/contrib/file/FREEBSD-upgrade' and k == 'A':
+ # continue
+ #if p == 'head/contrib/file/magic2mime' and k == 'A':
+ # continue
+ #if p == 'head/lib/libc/stdio/asprintf.c' and k == 'D':
+ # continue
+ if p == 'head/tools/build/options/WITH_BIND_LIBS' and k == 'A' and rev == 193280:
+ continue
+ makedirs(workpath, _dirname(path), 'src')
+ # Now the directory for the files must exist, and branch tag will be sticky
+ assert os.path.isdir(os.path.join(workpath, _dirname(path)))
+ assert k == 'A' or k == 'U' or k == 'D'
+
+ if k == 'A' or k == 'U':
+ print 'add/update file ' + path + '.'
+ destpath = os.path.join(workpath, path)
+ existed = os.path.isfile(destpath)
+ dump_file(fs_ptr, fs_root, rev, p, path, author, date, subpool, workpath)
+ if not existed:
+ print 'cvs add file ' + path + '.'
+ failed = do_cvs(workpath, _dirname(path), "cvs -q add %s" % _basename(path))
+ assert not failed
+ elif k == 'D':
+ print 'cvs rm -f file ' + path + '.'
+ failed = do_cvs(workpath, _dirname(path), "cvs -q rm -f %s" % _basename(path))
+ assert not failed
+ pc[workpath] = _dirname(path)
+
+ # aggregate the commit
+ for root, dir in pc:
+ fd, logfile = tempfile.mkstemp()
+ os.write(fd, cvslog)
+ os.close(fd)
+ failed = do_cvs(root, dir, "cvs -q commit -F %s" % logfile)
+ assert not failed
+ os.remove(logfile)
+ core.svn_pool_destroy(subpool)
+
+# Loop for the export range
+def export(pool, repos_path, cvspath):
+ repos_path = core.svn_path_canonicalize(repos_path)
+ fs_ptr = repos.fs(repos.open(repos_path, pool))
+ while True:
+ curr_rev = fs.youngest_rev(fs_ptr)
+ last_rev = int(fs.revision_prop(fs_ptr, 0, 'fbsd:lastexp'))
+ if last_rev < curr_rev:
+ time.sleep(5)
+ print '%d %s' % (last_rev, curr_rev)
+ rev = '%d' % (last_rev + 1)
+ print '==========> export rev ' + rev
+ exportrev(pool, fs_ptr, last_rev + 1, cvspath)
+ fs.change_rev_prop(fs_ptr, 0, 'fbsd:lastexp', rev)
+ continue
+ print "."
+ time.sleep(15)
+
+
+if __name__ == '__main__':
+ print "Version: $FreeBSD$"
+ os.environ['CVSROOT'] = '/r/ncvs'
+ core.run_app(export, '/r/svnmirror/base', '/r/svn2cvs/cvs')
+ # test rig
+ #os.environ['CVSROOT'] = '/home/peter/exp/cvs'
+ #core.run_app(export, '/home/peter/exp/svn', '/home/peter/exp/co')
More information about the svn-src-svnadmin
mailing list