#! /usr/bin/env python """svncpforce.py: Replace a file or directory in repository with another one. usage: 1. svncpforce.py SRC DST 2. svncpforce.py -r REVISION URL 1. Remove the item specified by DST, which must be an existing URL, and replace it with (a copy of) the item specified by SRC, which must be an URL from the same repository. 2. Server-side revert: remove an existing URL and replace it with the item at the same URL from an older REVISION. Valid options: -r [--revision] NUMBER : the revision of the source URL Limitations: This tool only works with 'svn://' or 'svn+ssh://' URLs. The standard options of 'svn' are not supported. """ import sys, posixpath import pysvn.ra, pysvn.delta def svncpforce(srcurl, dsturl, srcrev='HEAD', logmsg=None): extra = '' root = posixpath.commonprefix([srcurl, dsturl]) if '/' not in root: raise ValueError('source and target are not ' 'valid URLs with a common prefix') root = root[:root.rfind('/')] repo = pysvn.ra.connect(root) srcpath = repo.relpath(srcurl) dstpath = repo.relpath(dsturl) rootrev = repo.get_latest_rev() if srcrev == 'HEAD': srcrev = rootrev dstkind = repo.check_path(dstpath, rootrev) if not dstkind: raise IOError("%s does not exist at HEAD (r%d)" % (dsturl, rootrev)) srckind = repo.check_path(srcpath, srcrev) if not srckind: raise IOError("%s does not exist at r%d" % (srcurl, srcrev)) if srckind != dstkind: extra += "WARNING: mixed kinds (%s->%s)!\n" % (srckind, dstkind) sys.stderr.write(extra) if not logmsg: from pysvn.tool import ask_for_log userinfo = extra userinfo += 'D %s\n' % (dsturl,) userinfo += 'A+ %s\n' % (dsturl,) userinfo += ' (copied from %s, r%d)\n' % (srcurl, srcrev) logmsg = ask_for_log(userinfo) delta1 = pysvn.delta.Delta(dstpath, dstkind, rootrev, None) delta2 = pysvn.delta.Delta(dstpath, srckind, None, 'HEAD') delta2.copyfrom = srcurl, srcrev newrev, date = repo.commit(logmsg, [delta1, delta2], {'': rootrev}) print '%s: committed revision %d.' % (sys.argv[0], newrev) if __name__ == '__main__': args = sys.argv[1:] rev = 'HEAD' while args and args[0].startswith('-'): if len(args) >= 2 and args[0] in ('-r', '--revision'): rev = int(args[1]) args = args[2:] elif args[0].startswith('-r') and len(args[0]) > 2: rev = int(args[0][2:]) args = args[1:] else: break else: if len(args) == 1 and rev != 'HEAD': svncpforce(args[0], args[0], rev) sys.exit(0) elif len(args) == 2: svncpforce(args[0], args[1], rev) sys.exit(0) print >> sys.stderr, __doc__ sys.exit(2)