##// END OF EJS Templates
Use the pager given by the environment to display long output...
Use the pager given by the environment to display long output Unix systems usually have a PAGER environment variable set. If it is set, mercurial will use the pager application to display output. Two configuration variables are available to influence the behaviour of the pager. ui.pager sets the pager application. The pager is only used if ui.usepager is true. By default ui.usepager is disabled.

File last commit:

r6281:3e5f99b1 default
r6302:8e3dc3de default
Show More
subversion.py
1073 lines | 41.4 KiB | text/x-python | PythonLexer
Daniel Holth
convert extension: Add SVN converter
r4765 # Subversion 1.4/1.5 Python API backend
#
# Copyright(C) 2007 Daniel Holth et al
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 #
# Configuration options:
#
# convert.svn.trunk
# Relative path to the trunk (default: "trunk")
# convert.svn.branches
# Relative path to tree of branches (default: "branches")
Kirill Smelkov
convert: svn -- fix tags handling...
r5462 # convert.svn.tags
# Relative path to tree of tags (default: "tags")
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 #
# Set these in a hgrc, or on the command line as follows:
#
# hg convert --config convert.svn.trunk=wackoname [...]
Daniel Holth
convert extension: Add SVN converter
r4765
import locale
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 import os
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 import re
Thomas Arendsen Hein
Move debugsvnlog to subversion module.
r5139 import sys
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 import cPickle as pickle
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 import tempfile
from mercurial import strutil, util
from mercurial.i18n import _
Daniel Holth
convert extension: Add SVN converter
r4765
# Subversion stuff. Works best with very recent Python SVN bindings
# e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
# these bindings.
from cStringIO import StringIO
Thomas Arendsen Hein
Move debugsvnlog to subversion module.
r5139 from common import NoRepo, commit, converter_source, encodeargs, decodeargs
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 from common import commandline, converter_sink, mapfile
Brendan Cully
convert: activate subversion engine...
r4766
try:
from svn.core import SubversionException, Pool
Brendan Cully
convert svn: try to extract URL from source if it is a working directory
r5010 import svn
import svn.client
Brendan Cully
convert: activate subversion engine...
r4766 import svn.core
import svn.ra
import svn.delta
import transport
except ImportError:
pass
Daniel Holth
convert extension: Add SVN converter
r4765
Brendan Cully
convert: urlify svn repos if necessary....
r5008 def geturl(path):
Brendan Cully
convert svn: try to extract URL from source if it is a working directory
r5010 try:
Brendan Cully
convert svn: canonicalize path before calling url_from_path....
r5020 return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
Brendan Cully
convert svn: try to extract URL from source if it is a working directory
r5010 except SubversionException:
pass
Brendan Cully
convert: urlify svn repos if necessary....
r5008 if os.path.isdir(path):
Shun-ichi GOTO
convert: Accept local path on win32.
r5793 path = os.path.normpath(os.path.abspath(path))
if os.name == 'nt':
Shun-ichi GOTO
Use util.normpath() instead of direct path string operation....
r5842 path = '/' + util.normpath(path)
Shun-ichi GOTO
convert: Accept local path on win32.
r5793 return 'file://%s' % path
Brendan Cully
convert: urlify svn repos if necessary....
r5008 return path
Brendan Cully
convert: svn: add helper function for optrevs
r5117 def optrev(number):
optrev = svn.core.svn_opt_revision_t()
optrev.kind = svn.core.svn_opt_revision_number
optrev.value.number = number
return optrev
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 class changedpath(object):
def __init__(self, p):
self.copyfrom_path = p.copyfrom_path
self.copyfrom_rev = p.copyfrom_rev
self.action = p.action
Patrick Mezard
convert: replace fork with subprocess call.
r5127 def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
strict_node_history=False):
protocol = -1
def receiver(orig_paths, revnum, author, date, message, pool):
if orig_paths is not None:
for k, v in orig_paths.iteritems():
orig_paths[k] = changedpath(v)
pickle.dump((orig_paths, revnum, author, date, message),
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143 fp, protocol)
Patrick Mezard
convert: replace fork with subprocess call.
r5127 try:
# Use an ra of our own so that our parent can consume
# our results without confusing the server.
t = transport.SvnRaTransport(url=url)
svn.ra.get_log(t.ra, paths, start, end, limit,
discover_changed_paths,
strict_node_history,
receiver)
Thomas Arendsen Hein
Replace _ with inst for catching exceptions to not shadow gettext....
r5140 except SubversionException, (inst, num):
Patrick Mezard
convert: replace fork with subprocess call.
r5127 pickle.dump(num, fp, protocol)
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 except IOError:
# Caller may interrupt the iteration
pickle.dump(None, fp, protocol)
Patrick Mezard
convert: replace fork with subprocess call.
r5127 else:
pickle.dump(None, fp, protocol)
fp.close()
Thomas Arendsen Hein
Move debugsvnlog to subversion module.
r5139 def debugsvnlog(ui, **opts):
"""Fetch SVN log in a subprocess and channel them back to parent to
avoid memory collection issues.
"""
util.set_binary(sys.stdin)
util.set_binary(sys.stdout)
args = decodeargs(sys.stdin.read())
get_log_child(sys.stdout, *args)
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 class logstream:
"""Interruptible revision log iterator."""
def __init__(self, stdout):
self._stdout = stdout
def __iter__(self):
while True:
entry = pickle.load(self._stdout)
try:
orig_paths, revnum, author, date, message = entry
except:
if entry is None:
break
raise SubversionException("child raised exception", entry)
yield entry
def close(self):
if self._stdout:
self._stdout.close()
self._stdout = None
def get_log(url, paths, start, end, limit=0, discover_changed_paths=True,
strict_node_history=False):
args = [url, paths, start, end, limit, discover_changed_paths,
strict_node_history]
arg = encodeargs(args)
hgexe = util.hgexecutable()
cmd = '%s debugsvnlog' % util.shellquote(hgexe)
stdin, stdout = os.popen2(cmd, 'b')
stdin.write(arg)
stdin.close()
return logstream(stdout)
Daniel Holth
convert extension: Add SVN converter
r4765 # SVN conversion code stolen from bzr-svn and tailor
Patrick Mezard
convert: document the subversion conversion model
r5876 #
# Subversion looks like a versioned filesystem, branches structures
# are defined by conventions and not enforced by the tool. First,
# we define the potential branches (modules) as "trunk" and "branches"
# children directories. Revisions are then identified by their
# module and revision number (and a repository identifier).
#
# The revision graph is really a tree (or a forest). By default, a
# revision parent is the previous revision in the same module. If the
# module directory is copied/moved from another module then the
# revision is the module root and its parent the source revision in
# the parent module. A revision has at most one parent.
#
Bryan O'Sullivan
convert: rename convert_svn to svn_source
r5438 class svn_source(converter_source):
Brendan Cully
convert: activate subversion engine...
r4766 def __init__(self, ui, url, rev=None):
Bryan O'Sullivan
convert: rename convert_svn to svn_source
r5438 super(svn_source, self).__init__(ui, url, rev=rev)
Brendan Cully
convert: call superclass init from engine init functions
r4807
Brendan Cully
convert: activate subversion engine...
r4766 try:
SubversionException
except NameError:
Alexis S. L. Carvalho
convert: display all errors if we couldn't open the source repo...
r5521 raise NoRepo('Subversion python bindings could not be loaded')
Brendan Cully
convert: activate subversion engine...
r4766
Daniel Holth
convert extension: Add SVN converter
r4765 self.encoding = locale.getpreferredencoding()
Brendan Cully
convert: svn: use revmap to parse only new revisions in incremental conversions
r4813 self.lastrevs = {}
Brendan Cully
convert: activate subversion engine...
r4766 latest = None
Daniel Holth
convert extension: Add SVN converter
r4765 try:
# Support file://path@rev syntax. Useful e.g. to convert
# deleted branches.
Bryan O'Sullivan
convert/subversion.py: str.rsplit is not available in Python 2.3
r4927 at = url.rfind('@')
if at >= 0:
latest = int(url[at+1:])
url = url[:at]
Daniel Holth
convert extension: Add SVN converter
r4765 except ValueError, e:
Brendan Cully
convert: activate subversion engine...
r4766 pass
Brendan Cully
convert: urlify svn repos if necessary....
r5008 self.url = geturl(url)
Daniel Holth
convert extension: Add SVN converter
r4765 self.encoding = 'UTF-8' # Subversion is always nominal UTF-8
try:
Brendan Cully
convert: urlify svn repos if necessary....
r5008 self.transport = transport.SvnRaTransport(url=self.url)
Daniel Holth
convert extension: Add SVN converter
r4765 self.ra = self.transport.ra
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 self.ctx = self.transport.client
Daniel Holth
convert extension: Add SVN converter
r4765 self.base = svn.ra.get_repos_root(self.ra)
self.module = self.url[len(self.base):]
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 self.rootmodule = self.module
Daniel Holth
convert extension: Add SVN converter
r4765 self.commits = {}
Brendan Cully
convert: look up copies in getchanges instead of getcommit...
r5121 self.paths = {}
Daniel Holth
convert extension: Add SVN converter
r4765 self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
except SubversionException, e:
Bryan O'Sullivan
convert: report errors more meaningfully if run with --traceback
r5437 ui.print_exc()
Alexis S. L. Carvalho
convert: display all errors if we couldn't open the source repo...
r5521 raise NoRepo("%s does not look like a Subversion repo" % self.url)
Daniel Holth
convert extension: Add SVN converter
r4765
Thomas Arendsen Hein
raise util.Abort again if specified revision is not an integer....
r5145 if rev:
try:
latest = int(rev)
except ValueError:
raise util.Abort('svn: revision %s is not an integer' % rev)
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
try:
self.startrev = int(self.startrev)
if self.startrev < 0:
self.startrev = 0
except ValueError:
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 raise util.Abort(_('svn: start revision %s is not an integer')
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 % self.startrev)
Daniel Holth
convert extension: Add SVN converter
r4765 try:
self.get_blacklist()
except IOError, e:
pass
Patrick Mezard
convert: fix svn_source.latest()
r5955 self.head = self.latest(self.module, latest)
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if not self.head:
raise util.Abort(_('no revision found in module %s') %
self.module.encode(self.encoding))
Patrick Mezard
convert: fix svn_source.latest()
r5955 self.last_changed = self.revnum(self.head)
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Alexis S. L. Carvalho
convert_svn: add --filemap support
r5382 self._changescache = None
Daniel Holth
convert extension: Add SVN converter
r4765
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 if os.path.exists(os.path.join(url, '.svn/entries')):
self.wc = url
else:
self.wc = None
self.convertfp = None
Bryan O'Sullivan
convert: abstract map files into a class
r5510 def setrevmap(self, revmap):
Brendan Cully
convert: svn code movement (no actual changes)
r4840 lastrevs = {}
Bryan O'Sullivan
convert: iterate
r5511 for revid in revmap.iterkeys():
Brendan Cully
convert: svn code movement (no actual changes)
r4840 uuid, module, revnum = self.revsplit(revid)
lastrevnum = lastrevs.setdefault(module, revnum)
if revnum > lastrevnum:
lastrevs[module] = revnum
self.lastrevs = lastrevs
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 def exists(self, path, optrev):
try:
Kirill Smelkov
convert: svn -- fix 'exists'...
r5461 svn.client.ls(self.url.rstrip('/') + '/' + path,
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 optrev, False, self.ctx)
Kirill Smelkov
convert: svn -- fix 'exists'...
r5461 return True
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 except SubversionException, err:
Kirill Smelkov
convert: svn -- fix 'exists'...
r5461 return False
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925
Brendan Cully
convert: svn code movement (no actual changes)
r4840 def getheads(self):
Edouard Gomez
convert: separate trunk detection from branch layout detection...
r5854
def getcfgpath(name, rev):
cfgpath = self.ui.config('convert', 'svn.' + name)
Patrick Mezard
convert: allow svn trunk/branches/tags detection to be skipped...
r6172 if cfgpath is not None and cfgpath.strip() == '':
return None
Edouard Gomez
convert: separate trunk detection from branch layout detection...
r5854 path = (cfgpath or name).strip('/')
if not self.exists(path, rev):
if cfgpath:
raise util.Abort(_('expected %s to be at %r, but not found')
% (name, path))
return None
self.ui.note(_('found %s at %r\n') % (name, path))
return path
Brendan Cully
convert: svn: add helper function for optrevs
r5117 rev = optrev(self.last_changed)
Edouard Gomez
convert: separate trunk detection from branch layout detection...
r5854 oldmodule = ''
trunk = getcfgpath('trunk', rev)
tags = getcfgpath('tags', rev)
branches = getcfgpath('branches', rev)
# If the project has a trunk or branches, we will extract heads
# from them. We keep the project root otherwise.
if trunk:
oldmodule = self.module or ''
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 self.module += '/' + trunk
Patrick Mezard
convert: fix svn_source.latest()
r5955 self.head = self.latest(self.module, self.last_changed)
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if not self.head:
raise util.Abort(_('no revision found in module %s') %
self.module.encode(self.encoding))
Edouard Gomez
convert: separate trunk detection from branch layout detection...
r5854
# First head in the list is the module's head
self.heads = [self.head]
self.tags = '%s/%s' % (oldmodule , (tags or 'tags'))
# Check if branches bring a few more heads to the list
if branches:
rpath = self.url.strip('/')
Brendan Cully
convert: svn: add helper function for optrevs
r5117 branchnames = svn.client.ls(rpath + '/' + branches, rev, False,
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 self.ctx)
for branch in branchnames.keys():
Edouard Gomez
convert: separate trunk detection from branch layout detection...
r5854 module = '%s/%s/%s' % (oldmodule, branches, branch)
Patrick Mezard
convert: fix svn_source.latest()
r5955 brevid = self.latest(module, self.last_changed)
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if not brevid:
self.ui.note(_('ignoring empty branch %s\n') %
branch.encode(self.encoding))
continue
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 self.ui.note('found branch %s at %d\n' %
Patrick Mezard
convert: fix svn_source.latest()
r5955 (branch, self.revnum(brevid)))
self.heads.append(brevid)
Kirill Smelkov
convert: svn -- fix tags handling...
r5462
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 if self.startrev and self.heads:
if len(self.heads) > 1:
raise util.Abort(_('svn: start revision is not supported with '
'with more than one branch'))
revnum = self.revnum(self.heads[0])
if revnum < self.startrev:
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 raise util.Abort(_('svn: no revision found after start revision %d')
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 % self.startrev)
Brendan Cully
convert: svn code movement (no actual changes)
r4840 return self.heads
def getfile(self, file, rev):
data, mode = self._getfile(file, rev)
self.modecache[(file, rev)] = mode
return data
Thomas Arendsen Hein
removed trailing whitespace
r4957 def getmode(self, file, rev):
Brendan Cully
convert: svn code movement (no actual changes)
r4840 return self.modecache[(file, rev)]
def getchanges(self, rev):
Alexis S. L. Carvalho
convert_svn: add --filemap support
r5382 if self._changescache and self._changescache[0] == rev:
return self._changescache[1]
self._changescache = None
Brendan Cully
convert: svn code movement (no actual changes)
r4840 self.modecache = {}
Brendan Cully
convert: look up copies in getchanges instead of getcommit...
r5121 (paths, parents) = self.paths[rev]
Patrick Mezard
convert: checkout svn root revisions...
r5956 if parents:
files, copies = self.expandpaths(rev, paths, parents)
else:
# Perform a full checkout on roots
uuid, module, revnum = self.revsplit(rev)
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 entries = svn.client.ls(self.base + module, optrev(revnum),
Patrick Mezard
convert: checkout svn root revisions...
r5956 True, self.ctx)
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 files = [n for n,e in entries.iteritems()
Patrick Mezard
convert: checkout svn root revisions...
r5956 if e.kind == svn.core.svn_node_file]
copies = {}
Brendan Cully
convert: look up copies in getchanges instead of getcommit...
r5121 files.sort()
files = zip(files, [rev] * len(files))
Brendan Cully
convert: svn code movement (no actual changes)
r4840 # caller caches the result, so free it here to release memory
Brendan Cully
convert: look up copies in getchanges instead of getcommit...
r5121 del self.paths[rev]
return (files, copies)
Brendan Cully
convert: svn code movement (no actual changes)
r4840
Alexis S. L. Carvalho
convert_svn: add --filemap support
r5382 def getchangedfiles(self, rev, i):
changes = self.getchanges(rev)
self._changescache = (rev, changes)
return [f[0] for f in changes[0]]
Brendan Cully
convert: svn code movement (no actual changes)
r4840 def getcommit(self, rev):
if rev not in self.commits:
uuid, module, revnum = self.revsplit(rev)
self.module = module
self.reparent(module)
Patrick Mezard
convert: fetch less revisions when looking for a branch parent
r5875 # We assume that:
# - requests for revisions after "stop" come from the
# revision graph backward traversal. Cache all of them
# down to stop, they will be used eventually.
# - requests for revisions before "stop" come to get
# isolated branches parents. Just fetch what is needed.
Brendan Cully
convert: svn code movement (no actual changes)
r4840 stop = self.lastrevs.get(module, 0)
Patrick Mezard
convert: fetch less revisions when looking for a branch parent
r5875 if revnum < stop:
stop = revnum + 1
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871 self._fetch_revisions(revnum, stop)
Brendan Cully
convert: svn code movement (no actual changes)
r4840 commit = self.commits[rev]
# caller caches the result, so free it here to release memory
del self.commits[rev]
return commit
def gettags(self):
tags = {}
Patrick Mezard
convert: allow svn trunk/branches/tags detection to be skipped...
r6172 if self.tags is None:
return tags
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Bryan O'Sullivan
convert/subversion: rehandle the no-tags case
r4949 start = self.revnum(self.head)
try:
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 for entry in get_log(self.url, [self.tags], self.startrev, start):
Bryan O'Sullivan
convert/subversion: rehandle the no-tags case
r4949 orig_paths, revnum, author, date, message = entry
for path in orig_paths:
Kirill Smelkov
convert: svn -- fix tags handling...
r5462 if not path.startswith(self.tags+'/'):
Bryan O'Sullivan
convert/subversion: rehandle the no-tags case
r4949 continue
ent = orig_paths[path]
source = ent.copyfrom_path
rev = ent.copyfrom_rev
Kirill Smelkov
convert: svn -- fix tags handling...
r5462 tag = path.split('/')[-1]
Bryan O'Sullivan
convert/subversion: rehandle the no-tags case
r4949 tags[tag] = self.revid(rev, module=source)
Thomas Arendsen Hein
Replace _ with inst for catching exceptions to not shadow gettext....
r5140 except SubversionException, (inst, num):
Bryan O'Sullivan
convert/subversion: rehandle the no-tags case
r4949 self.ui.note('no tags found at revision %d\n' % start)
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 return tags
Brendan Cully
convert: svn code movement (no actual changes)
r4840
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 def converted(self, rev, destrev):
if not self.wc:
return
if self.convertfp is None:
self.convertfp = open(os.path.join(self.wc, '.svn', 'hg-shamap'),
'a')
self.convertfp.write('%s %d\n' % (destrev, self.revnum(rev)))
self.convertfp.flush()
Brendan Cully
convert: svn code movement (no actual changes)
r4840 # -- helper functions --
Brendan Cully
convert: move some code into common init function
r4810 def revid(self, revnum, module=None):
Brendan Cully
convert: svn: get parent for branch creation events
r4795 if not module:
module = self.module
Thomas Arendsen Hein
Don't decode unicode strings....
r5287 return u"svn:%s%s@%s" % (self.uuid, module.decode(self.encoding),
revnum)
Brendan Cully
convert: svn: add revnum() to convert rev to revnum
r4774
def revnum(self, rev):
return int(rev.split('@')[-1])
Brendan Cully
convert: svn: add function to get the latest revision touching a path...
r4789
Brendan Cully
convert: add optional module argument to svn._fetch_revisions
r4794 def revsplit(self, rev):
url, revnum = rev.encode(self.encoding).split('@', 1)
revnum = int(revnum)
parts = url.split('/', 1)
uuid = parts.pop(0)[4:]
Brendan Cully
convert: svn: autodetect /branches, /tags, /trunk....
r4797 mod = ''
Brendan Cully
convert: add optional module argument to svn._fetch_revisions
r4794 if parts:
Brendan Cully
convert: svn: autodetect /branches, /tags, /trunk....
r4797 mod = '/' + parts[0]
Brendan Cully
convert: add optional module argument to svn._fetch_revisions
r4794 return uuid, mod, revnum
Brendan Cully
convert: typo in svn.latest
r4790 def latest(self, path, stop=0):
Patrick Mezard
convert: fix svn_source.latest()
r5955 """Find the latest revid affecting path, up to stop. It may return
a revision in a different module, since a branch may be moved without
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 a change being reported. Return None if computed module does not
belong to rootmodule subtree.
Patrick Mezard
convert: fix svn_source.latest()
r5955 """
Patrick Mezard
convert: avoid querying log of foreign svn branches...
r6281 if not path.startswith(self.rootmodule):
# Requests on foreign branches may be forbidden at server level
self.ui.debug(_('ignoring foreign branch %r\n') % path)
return None
Brendan Cully
convert: svn: add function to get the latest revision touching a path...
r4789 if not stop:
stop = svn.ra.get_latest_revnum(self.ra)
try:
self.reparent('')
dirent = svn.ra.stat(self.ra, path.strip('/'), stop)
self.reparent(self.module)
except SubversionException:
dirent = None
if not dirent:
Bryan O'Sullivan
convert/subversion.py: fix bad assumptions about SVN path naming...
r4925 raise util.Abort('%s not found up to revision %d' % (path, stop))
Brendan Cully
convert: svn: add function to get the latest revision touching a path...
r4789
Patrick Mezard
convert: fix svn_source.latest()
r5955 # stat() gives us the previous revision on this line of development, but
# it might be in *another module*. Fetch the log and detect renames down
# to the latest revision.
stream = get_log(self.url, [path], stop, dirent.created_rev)
try:
for entry in stream:
paths, revnum, author, date, message = entry
if revnum <= dirent.created_rev:
break
for p in paths:
if not path.startswith(p) or not paths[p].copyfrom_path:
continue
newpath = paths[p].copyfrom_path + path[len(p):]
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 self.ui.debug("branch renamed from %s to %s at %d\n" %
Patrick Mezard
convert: fix svn_source.latest()
r5955 (path, newpath, revnum))
path = newpath
break
finally:
stream.close()
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if not path.startswith(self.rootmodule):
self.ui.debug(_('ignoring foreign branch %r\n') % path)
return None
Patrick Mezard
convert: fix svn_source.latest()
r5955 return self.revid(dirent.created_rev, path)
Brendan Cully
convert: svn: add function to get the latest revision touching a path...
r4789
Daniel Holth
convert extension: Add SVN converter
r4765 def get_blacklist(self):
"""Avoid certain revision numbers.
It is not uncommon for two nearby revisions to cancel each other
out, e.g. 'I copied trunk into a subdirectory of itself instead
of making a branch'. The converted repository is significantly
smaller if we ignore such revisions."""
Thomas Arendsen Hein
convert/subversion: Use util.set() instead of set() for python2.3 compatibility
r5276 self.blacklist = util.set()
Daniel Holth
convert extension: Add SVN converter
r4765 blacklist = self.blacklist
for line in file("blacklist.txt", "r"):
if not line.startswith("#"):
try:
svn_rev = int(line.strip())
blacklist.add(svn_rev)
except ValueError, e:
pass # not an integer or a comment
def is_blacklisted(self, svn_rev):
return svn_rev in self.blacklist
def reparent(self, module):
svn_url = self.base + module
self.ui.debug("reparent to %s\n" % svn_url.encode(self.encoding))
svn.ra.reparent(self.ra, svn_url.encode(self.encoding))
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 def expandpaths(self, rev, paths, parents):
Daniel Holth
convert extension: Add SVN converter
r4765 def get_entry_from_path(path, module=self.module):
# Given the repository url of this wc, say
# "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
# extract the "entry" portion (a relative path) from what
# svn log --xml says, ie
# "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
# that is to say "tests/PloneTestCase.py"
if path.startswith(module):
relative = path[len(module):]
if relative.startswith('/'):
return relative[1:]
else:
return relative
# The path is outside our tracked tree...
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
Daniel Holth
convert extension: Add SVN converter
r4765 return None
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 entries = []
copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
copies = {}
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 new_module, revnum = self.revsplit(rev)[1:]
if new_module != self.module:
self.module = new_module
self.reparent(self.module)
Brendan Cully
convert: look up copies in getchanges instead of getcommit...
r5121
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 for path, ent in paths:
entrypath = get_entry_from_path(path, module=self.module)
entry = entrypath.decode(self.encoding)
kind = svn.ra.check_path(self.ra, entrypath, revnum)
if kind == svn.core.svn_node_file:
if ent.copyfrom_path:
copyfrom_path = get_entry_from_path(ent.copyfrom_path)
if copyfrom_path:
"Shun-ichi GOTO"
svn: fix copy exception
r5841 self.ui.debug("Copied to %s from %s@%s\n" %
(entrypath, copyfrom_path,
ent.copyfrom_rev))
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 # It's probably important for hg that the source
# exists in the revision's parent, not just the
# ent.copyfrom_rev
fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev)
if fromkind != 0:
copies[self.recode(entry)] = self.recode(copyfrom_path)
entries.append(self.recode(entry))
elif kind == 0: # gone, but had better be a deleted *file*
self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
# if a branch is created but entries are removed in the same
# changeset, get the right fromrev
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 # parents cannot be empty here, you cannot remove things from
# a root revision.
uuid, old_module, fromrev = self.revsplit(parents[0])
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120
basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
def lookup_parts(p):
rc = None
parts = p.split("/")
for i in range(len(parts)):
part = "/".join(parts[:i])
info = part, copyfrom.get(part, None)
if info[1] is not None:
self.ui.debug("Found parent directory %s\n" % info[1])
rc = info
return rc
self.ui.debug("base, entry %s %s\n" % (basepath, entrypath))
frompath, froment = lookup_parts(entrypath) or (None, revnum - 1)
# need to remove fragment from lookup_parts and replace with copyfrom_path
if frompath is not None:
self.ui.debug("munge-o-matic\n")
self.ui.debug(entrypath + '\n')
self.ui.debug(entrypath[len(frompath):] + '\n')
entrypath = froment.copyfrom_path + entrypath[len(frompath):]
fromrev = froment.copyfrom_rev
self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath))
Patrick Mezard
convert: fix invalid svn.ra.check_path() call (issue 771)...
r5880 # We can avoid the reparent calls if the module has not changed
# but it probably does not worth the pain.
self.reparent('')
fromkind = svn.ra.check_path(self.ra, entrypath.strip('/'), fromrev)
self.reparent(self.module)
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 if fromkind == svn.core.svn_node_file: # a deleted file
entries.append(self.recode(entry))
elif fromkind == svn.core.svn_node_dir:
# print "Deleted/moved non-file:", revnum, path, ent
# children = self._find_children(path, revnum - 1)
# print "find children %s@%d from %d action %s" % (path, revnum, ent.copyfrom_rev, ent.action)
# Sometimes this is tricky. For example: in
# The Subversion Repository revision 6940 a dir
# was copied and one of its files was deleted
# from the new location in the same commit. This
# code can't deal with that yet.
if ent.action == 'C':
children = self._find_children(path, fromrev)
else:
oroot = entrypath.strip('/')
nroot = path.strip('/')
children = self._find_children(oroot, fromrev)
children = [s.replace(oroot,nroot) for s in children]
# Mark all [files, not directories] as deleted.
for child in children:
# Can we move a child directory and its
# parent in the same commit? (probably can). Could
# cause problems if instead of revnum -1,
# we have to look in (copyfrom_path, revnum - 1)
entrypath = get_entry_from_path("/" + child, module=old_module)
if entrypath:
entry = self.recode(entrypath.decode(self.encoding))
if entry in copies:
# deleted file within a copy
del copies[entry]
else:
entries.append(entry)
else:
self.ui.debug('unknown path in revision %d: %s\n' % \
(revnum, path))
elif kind == svn.core.svn_node_dir:
# Should probably synthesize normal file entries
# and handle as above to clean up copy/rename handling.
# If the directory just had a prop change,
# then we shouldn't need to look for its children.
Patrick Mezard
convert: don't scan directories on property changes
r5870 if ent.action == 'M':
continue
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 # Also this could create duplicate entries. Not sure
# whether this will matter. Maybe should make entries a set.
# print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev
# This will fail if a directory was copied
# from another branch and then some of its files
# were deleted in the same transaction.
children = self._find_children(path, revnum)
children.sort()
for child in children:
# Can we move a child directory and its
# parent in the same commit? (probably can). Could
# cause problems if instead of revnum -1,
# we have to look in (copyfrom_path, revnum - 1)
entrypath = get_entry_from_path("/" + child, module=self.module)
# print child, self.module, entrypath
if entrypath:
# Need to filter out directories here...
kind = svn.ra.check_path(self.ra, entrypath, revnum)
if kind != svn.core.svn_node_dir:
entries.append(self.recode(entrypath))
# Copies here (must copy all from source)
# Probably not a real problem for us if
# source does not exist
# Can do this with the copy command "hg copy"
# if ent.copyfrom_path:
# copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding),
# module=self.module)
# copyto_entry = entrypath
#
# print "copy directory", copyfrom_entry, 'to', copyto_entry
#
# copies.append((copyfrom_entry, copyto_entry))
if ent.copyfrom_path:
copyfrom_path = ent.copyfrom_path.decode(self.encoding)
copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module)
if copyfrom_entry:
copyfrom[path] = ent
self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path]))
# Good, /probably/ a regular copy. Really should check
# to see whether the parent revision actually contains
# the directory in question.
children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev)
children.sort()
for child in children:
entrypath = get_entry_from_path("/" + child, module=self.module)
if entrypath:
entry = entrypath.decode(self.encoding)
# print "COPY COPY From", copyfrom_entry, entry
copyto_path = path + entry[len(copyfrom_entry):]
copyto_entry = get_entry_from_path(copyto_path, module=self.module)
# print "COPY", entry, "COPY To", copyto_entry
copies[self.recode(copyto_entry)] = self.recode(entry)
# copy from quux splort/quuxfile
Patrick Mezard
convert: enforce svn_source.getchanges() files unicity
r5883 return (util.unique(entries), copies)
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871 def _fetch_revisions(self, from_revnum, to_revnum):
if from_revnum < to_revnum:
from_revnum, to_revnum = to_revnum, from_revnum
Bryan O'Sullivan
convert/subversion: reduce memory usage by filtering early...
r4940 self.child_cset = None
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 def parselogentry(orig_paths, revnum, author, date, message):
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 """Return the parsed commit object or None, and True if
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 the revision is a branch root.
"""
Bryan O'Sullivan
convert/subversion: work around memory leak in svn's python bindings...
r4946 self.ui.debug("parsing revision %d (%d changes)\n" %
(revnum, len(orig_paths)))
Bryan O'Sullivan
convert/subversion: reduce memory usage by filtering early...
r4940
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 branched = False
Brendan Cully
convert: move some code into common init function
r4810 rev = self.revid(revnum)
Brendan Cully
convert: svn: some improvements in memory usage
r4837 # branch log might return entries for a parent we already have
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871
if (rev in self.commits or revnum < to_revnum):
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 return None, branched
Brendan Cully
convert: svn: some improvements in memory usage
r4837
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 parents = []
Patrick Mezard
convert: follow svn module parent moves
r5958 # check whether this revision is the start of a branch or part
# of a branch renaming
orig_paths = orig_paths.items()
orig_paths.sort()
root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)]
if root_paths:
path, ent = root_paths[-1]
Brendan Cully
convert: svn: hoist up branch creation check
r5119 if ent.copyfrom_path:
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 branched = True
Patrick Mezard
convert: follow svn module parent moves
r5958 newpath = ent.copyfrom_path + self.module[len(path):]
Brendan Cully
convert: svn: hoist up branch creation check
r5119 # ent.copyfrom_rev may not be the actual last revision
Patrick Mezard
convert: follow svn module parent moves
r5958 previd = self.latest(newpath, ent.copyfrom_rev)
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if previd is not None:
prevmodule, prevnum = self.revsplit(previd)[1:]
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 if prevnum >= self.startrev:
parents = [previd]
self.ui.note('found parent of branch %s at %d: %s\n' %
(self.module, prevnum, prevmodule))
Brendan Cully
convert: svn: hoist up branch creation check
r5119 else:
self.ui.debug("No copyfrom path, don't know what to do.\n")
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 paths = []
# filter out unrelated paths
Bryan O'Sullivan
convert/subversion: reduce memory usage by filtering early...
r4940 for path, ent in orig_paths:
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 if not path.startswith(self.module):
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788 self.ui.debug("boring@%s: %s\n" % (revnum, path))
continue
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 paths.append((path, ent))
Daniel Holth
convert extension: Add SVN converter
r4765
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788 # Example SVN datetime. Includes microseconds.
# ISO-8601 conformant
# '2007-01-04T17:35:00.902377Z'
David J. Mellor
convert: fix SVN date parser dropping the final whole second digit
r5617 date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
Daniel Holth
convert extension: Add SVN converter
r4765
Thomas Arendsen Hein
convert: Do not abort with TypeError if svn commit message is None (issue934)
r5916 log = message and self.recode(message) or ''
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788 author = author and self.recode(author) or ''
Brendan Cully
convert: svn: pull up path to file expansion code into separate function....
r5120 try:
branch = self.module.split("/")[-1]
if branch == 'trunk':
branch = ''
except IndexError:
branch = None
Daniel Holth
convert extension: Add SVN converter
r4765
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788 cset = commit(author=author,
Thomas Arendsen Hein
removed trailing whitespace
r4957 date=util.datestr(date),
desc=log,
Brendan Cully
convert: svn: get parent for branch creation events
r4795 parents=parents,
Brendan Cully
convert: record the source revision in the changelog
r4873 branch=branch,
rev=rev.encode('utf-8'))
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788
Brendan Cully
convert: svn: pull out broken batching code, add alpha tags support
r4796 self.commits[rev] = cset
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 # The parents list is *shared* among self.paths and the
# commit object. Both will be updated below.
self.paths[rev] = (paths, cset.parents)
Brendan Cully
convert: svn: pull out broken batching code, add alpha tags support
r4796 if self.child_cset and not self.child_cset.parents:
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 self.child_cset.parents[:] = [rev]
Brendan Cully
convert: svn: add an early return to move most changeset parsing out an indent level
r4788 self.child_cset = cset
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 return cset, branched
Brendan Cully
convert: svn: pull out broken batching code, add alpha tags support
r4796
Bryan O'Sullivan
convert/subversion: reduce memory usage by filtering early...
r4940 self.ui.note('fetching revision log for "%s" from %d to %d\n' %
Brendan Cully
convert: svn: autodetect /branches, /tags, /trunk....
r4797 (self.module, from_revnum, to_revnum))
Daniel Holth
convert extension: Add SVN converter
r4765
try:
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871 firstcset = None
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 lastonbranch = False
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 stream = get_log(self.url, [self.module], from_revnum, to_revnum)
try:
for entry in stream:
paths, revnum, author, date, message = entry
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 if revnum < self.startrev:
lastonbranch = True
break
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 if self.is_blacklisted(revnum):
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 self.ui.note('skipping blacklisted revision %d\n'
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 % revnum)
continue
if paths is None:
self.ui.debug('revision %d has no entries\n' % revnum)
continue
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210 cset, lastonbranch = parselogentry(paths, revnum, author,
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 date, message)
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 if cset:
firstcset = cset
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 if lastonbranch:
Patrick Mezard
convert: make svn revision iterator interruptible
r5873 break
finally:
stream.close()
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871
Patrick Mezard
convert: add shallow, single branch svn conversions via svn.startrev
r6173 if not lastonbranch and firstcset and not firstcset.parents:
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871 # The first revision of the sequence (the last fetched one)
# has invalid parents if not a branch root. Find the parent
# revision now, if any.
try:
firstrevnum = self.revnum(firstcset.rev)
if firstrevnum > 1:
latest = self.latest(self.module, firstrevnum - 1)
Patrick Mezard
convert: prevent svn branches to leave the root module tree
r5957 if latest:
firstcset.parents.append(latest)
Patrick Mezard
convert: fix parents of last fetched svn revision
r5871 except util.Abort:
pass
Thomas Arendsen Hein
Replace _ with inst for catching exceptions to not shadow gettext....
r5140 except SubversionException, (inst, num):
Daniel Holth
convert extension: Add SVN converter
r4765 if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
Joel Rosdahl
convert: Fix unbound name error in the subversion backend
r6214 raise util.Abort('svn: branch has no revision %s' % to_revnum)
Daniel Holth
convert extension: Add SVN converter
r4765 raise
def _getfile(self, file, rev):
io = StringIO()
# TODO: ra.get_file transmits the whole file instead of diffs.
mode = ''
try:
Patrick Mezard
convert: fix cross-branches subversion revisions handling...
r5872 new_module, revnum = self.revsplit(rev)[1:]
if self.module != new_module:
self.module = new_module
Daniel Holth
convert extension: Add SVN converter
r4765 self.reparent(self.module)
info = svn.ra.get_file(self.ra, file, revnum, io)
if isinstance(info, list):
info = info[-1]
mode = ("svn:executable" in info) and 'x' or ''
mode = ("svn:special" in info) and 'l' or mode
except SubversionException, e:
notfound = (svn.core.SVN_ERR_FS_NOT_FOUND,
svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND)
if e.apr_err in notfound: # File not found
raise IOError()
raise
data = io.getvalue()
if mode == 'l':
link_prefix = "link "
if data.startswith(link_prefix):
data = data[len(link_prefix):]
return data, mode
def _find_children(self, path, revnum):
Brendan Cully
convert: svn: ensure leading / is removed from paths in _find_children (broken in 2bd996d0aaf8)
r5114 path = path.strip('/')
Daniel Holth
convert extension: Add SVN converter
r4765 pool = Pool()
Brendan Cully
convert: svn: ensure leading / is removed from paths in _find_children (broken in 2bd996d0aaf8)
r5114 rpath = '/'.join([self.base, path]).strip('/')
Brendan Cully
convert: svn: add helper function for optrevs
r5117 return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()]
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513
pre_revprop_change = '''#!/bin/sh
REPOS="$1"
REV="$2"
USER="$3"
PROPNAME="$4"
ACTION="$5"
if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi
if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi
echo "Changing prohibited revision property" >&2
exit 1
'''
class svn_sink(converter_sink, commandline):
commit_re = re.compile(r'Committed revision (\d+).', re.M)
def prerun(self):
if self.wc:
os.chdir(self.wc)
def postrun(self):
if self.wc:
os.chdir(self.cwd)
def join(self, name):
return os.path.join(self.wc, '.svn', name)
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 def revmapfile(self):
return self.join('hg-shamap')
def authorfile(self):
return self.join('hg-authormap')
def __init__(self, ui, path):
converter_sink.__init__(self, ui, path)
commandline.__init__(self, ui, 'svn')
self.delete = []
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.setexec = []
self.delexec = []
self.copies = []
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 self.wc = None
self.cwd = os.getcwd()
path = os.path.realpath(path)
created = False
if os.path.isfile(os.path.join(path, '.svn', 'entries')):
self.wc = path
self.run0('update')
else:
Patrick Mezard
convert: fix svn file:// URL generation under Windows
r5535 wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc')
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 if os.path.isdir(os.path.dirname(path)):
if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
ui.status(_('initializing svn repo %r\n') %
os.path.basename(path))
commandline(ui, 'svnadmin').run0('create', path)
created = path
Shun-ichi GOTO
Use util.normpath() instead of direct path string operation....
r5842 path = util.normpath(path)
Patrick Mezard
convert: fix svn file:// URL generation under Windows
r5535 if not path.startswith('/'):
path = '/' + path
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 path = 'file://' + path
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 ui.status(_('initializing svn wc %r\n') % os.path.basename(wcpath))
self.run0('checkout', path, wcpath)
self.wc = wcpath
self.opener = util.opener(self.wc)
self.wopener = util.opener(self.wc)
self.childmap = mapfile(ui, self.join('hg-childmap'))
Patrick Mezard
convert: force svn:executable when execute-bit is not supported...
r5536 self.is_exec = util.checkexec(self.wc) and util.is_exec or None
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513
if created:
hook = os.path.join(created, 'hooks', 'pre-revprop-change')
fp = open(hook, 'w')
fp.write(pre_revprop_change)
fp.close()
Matt Mackall
convert/svn: use util.set_flags
r5705 util.set_flags(hook, "x")
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 xport = transport.SvnRaTransport(url=geturl(path))
self.uuid = svn.ra.get_uuid(xport.ra)
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 def wjoin(self, *names):
return os.path.join(self.wc, *names)
def putfile(self, filename, flags, data):
if 'l' in flags:
self.wopener.symlink(data, filename)
else:
try:
if os.path.islink(self.wjoin(filename)):
os.unlink(filename)
except OSError:
pass
self.wopener(filename, 'w').write(data)
Patrick Mezard
convert: force svn:executable when execute-bit is not supported...
r5536
if self.is_exec:
was_exec = self.is_exec(self.wjoin(filename))
else:
# On filesystems not supporting execute-bit, there is no way
# to know if it is set but asking subversion. Setting it
# systematically is just as expensive and much simpler.
was_exec = 'x' not in flags
Matt Mackall
convert/svn: use util.set_flags
r5705 util.set_flags(self.wjoin(filename), flags)
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 if was_exec:
if 'x' not in flags:
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.delexec.append(filename)
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 else:
if 'x' in flags:
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.setexec.append(filename)
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 def delfile(self, name):
self.delete.append(name)
def copyfile(self, source, dest):
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.copies.append([source, dest])
def _copyfile(self, source, dest):
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 # SVN's copy command pukes if the destination file exists, but
# our copyfile method expects to record a copy that has
# already occurred. Cross the semantic gap.
wdest = self.wjoin(dest)
exists = os.path.exists(wdest)
if exists:
fd, tempname = tempfile.mkstemp(
prefix='hg-copy-', dir=os.path.dirname(wdest))
os.close(fd)
os.unlink(tempname)
os.rename(wdest, tempname)
try:
self.run0('copy', source, dest)
finally:
if exists:
try:
os.unlink(wdest)
except OSError:
pass
os.rename(tempname, wdest)
def dirs_of(self, files):
Thomas Arendsen Hein
convert: Compatibility fixes for python2.3...
r6053 dirs = util.set()
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 for f in files:
if os.path.isdir(self.wjoin(f)):
dirs.add(f)
for i in strutil.rfindall(f, '/'):
dirs.add(f[:i])
return dirs
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 def add_dirs(self, files):
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 add_dirs = [d for d in self.dirs_of(files)
if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
if add_dirs:
Patrick Mezard
convert: add directories to subversion from root to leaves
r5537 add_dirs.sort()
Maxim Dounin
convert: add commandline.xargs(), use it in svn_sink class...
r5832 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 return add_dirs
def add_files(self, files):
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 if files:
Maxim Dounin
convert: add commandline.xargs(), use it in svn_sink class...
r5832 self.xargs(files, 'add', quiet=True)
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 return files
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 def tidy_dirs(self, names):
dirs = list(self.dirs_of(names))
Thomas Arendsen Hein
convert: Compatibility fixes for python2.3...
r6053 dirs.sort()
dirs.reverse()
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 deleted = []
for d in dirs:
wd = self.wjoin(d)
if os.listdir(wd) == '.svn':
self.run0('delete', d)
deleted.append(d)
return deleted
def addchild(self, parent, child):
self.childmap[parent] = child
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 def revid(self, rev):
return u"svn:%s@%s" % (self.uuid, rev)
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 def putcommit(self, files, parents, commit):
for parent in parents:
try:
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 return self.revid(self.childmap[parent])
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 except KeyError:
pass
Thomas Arendsen Hein
convert: Compatibility fixes for python2.3...
r6053 entries = util.set(self.delete)
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 files = util.frozenset(files)
entries.update(self.add_dirs(files.difference(entries)))
if self.copies:
for s, d in self.copies:
self._copyfile(s, d)
self.copies = []
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 if self.delete:
Maxim Dounin
convert: add commandline.xargs(), use it in svn_sink class...
r5832 self.xargs(self.delete, 'delete')
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 self.delete = []
entries.update(self.add_files(files.difference(entries)))
entries.update(self.tidy_dirs(entries))
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 if self.delexec:
Maxim Dounin
convert: add commandline.xargs(), use it in svn_sink class...
r5832 self.xargs(self.delexec, 'propdel', 'svn:executable')
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.delexec = []
if self.setexec:
Maxim Dounin
convert: add commandline.xargs(), use it in svn_sink class...
r5832 self.xargs(self.setexec, 'propset', 'svn:executable', '*')
Maxim Dounin
convert: svn-sink: copy and set properties after adding dirs/files...
r5698 self.setexec = []
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
fp = os.fdopen(fd, 'w')
fp.write(commit.desc)
fp.close()
try:
output = self.run0('commit',
username=util.shortuser(commit.author),
file=messagefile,
Shun-ichi GOTO
convert: svn_sink: workaround of command line size limitation on win32....
r5790 encoding='utf-8')
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 try:
rev = self.commit_re.search(output).group(1)
except AttributeError:
self.ui.warn(_('unexpected svn output:\n'))
self.ui.warn(output)
raise util.Abort(_('unable to cope with svn output'))
if commit.rev:
self.run('propset', 'hg:convert-rev', commit.rev,
revprop=True, revision=rev)
if commit.branch and commit.branch != 'default':
self.run('propset', 'hg:convert-branch', commit.branch,
revprop=True, revision=rev)
for parent in parents:
self.addchild(parent, rev)
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 return self.revid(rev)
Bryan O'Sullivan
convert: add support for Subversion as a sink
r5513 finally:
os.unlink(messagefile)
def puttags(self, tags):
self.ui.warn(_('XXX TAGS NOT IMPLEMENTED YET\n'))