summaryrefslogtreecommitdiff
path: root/hgext/convert
diff options
context:
space:
mode:
Diffstat (limited to 'hgext/convert')
-rw-r--r--hgext/convert/__init__.py21
-rw-r--r--hgext/convert/bzr.py101
-rw-r--r--hgext/convert/common.py82
-rw-r--r--hgext/convert/convcmd.py38
-rw-r--r--hgext/convert/cvs.py7
-rw-r--r--hgext/convert/cvsps.py60
-rw-r--r--hgext/convert/darcs.py6
-rw-r--r--hgext/convert/filemap.py52
-rw-r--r--hgext/convert/git.py30
-rw-r--r--hgext/convert/hg.py31
-rw-r--r--hgext/convert/monotone.py12
-rw-r--r--hgext/convert/p4.py3
-rw-r--r--hgext/convert/subversion.py262
-rw-r--r--hgext/convert/transport.py10
14 files changed, 256 insertions, 459 deletions
diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py
index e53c82c..abaa68a 100644
--- a/hgext/convert/__init__.py
+++ b/hgext/convert/__init__.py
@@ -13,8 +13,6 @@ import subversion
from mercurial import commands, templatekw
from mercurial.i18n import _
-testedwith = 'internal'
-
# Commands definition was moved elsewhere to ease demandload job.
def convert(ui, src, dest=None, revmapfile=None, **opts):
@@ -138,7 +136,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
repository from "default" to a named branch.
Mercurial Source
- ################
+ ''''''''''''''''
The Mercurial source recognizes the following configuration
options, which you can set on the command line with ``--config``:
@@ -148,14 +146,14 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
converting from and to Mercurial. Default is False.
:convert.hg.saverev: store original revision ID in changeset
- (forces target IDs to change). It takes a boolean argument and
- defaults to False.
+ (forces target IDs to change). It takes and boolean argument
+ and defaults to False.
:convert.hg.startrev: convert start revision and its descendants.
It takes a hg revision identifier and defaults to 0.
CVS Source
- ##########
+ ''''''''''
CVS source will use a sandbox (i.e. a checked-out copy) from CVS
to indicate the starting point of what will be converted. Direct
@@ -197,7 +195,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
delete them.
:hook.cvschangesets: Specify a Python function to be called after
- the changesets are calculated from the CVS log. The
+ the changesets are calculated from the the CVS log. The
function is passed a list with the changeset entries, and can
modify the changesets in-place, or add or delete them.
@@ -207,7 +205,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
the command help for more details.
Subversion Source
- #################
+ '''''''''''''''''
Subversion source detects classical trunk/branches/tags layouts.
By default, the supplied ``svn://repo/path/`` source URL is
@@ -239,7 +237,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
The default is 0.
Perforce Source
- ###############
+ '''''''''''''''
The Perforce (P4) importer can be given a p4 depot path or a
client specification as source. It will convert all files in the
@@ -255,7 +253,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
Perforce changelist number).
Mercurial Destination
- #####################
+ '''''''''''''''''''''
The following options are supported:
@@ -330,8 +328,7 @@ cmdtable = {
('', 'root', '', _('specify cvsroot')),
# Options specific to builtin cvsps
('', 'parents', '', _('show parent changesets')),
- ('', 'ancestors', '',
- _('show current changeset in ancestor branches')),
+ ('', 'ancestors', '', _('show current changeset in ancestor branches')),
# Options that are ignored for compatibility with cvsps-2.1
('A', 'cvs-direct', None, _('ignored for compatibility')),
],
diff --git a/hgext/convert/bzr.py b/hgext/convert/bzr.py
index 5eef902..cc16258 100644
--- a/hgext/convert/bzr.py
+++ b/hgext/convert/bzr.py
@@ -23,7 +23,7 @@ from common import NoRepo, commit, converter_source
try:
# bazaar imports
- from bzrlib import bzrdir, revision, errors
+ from bzrlib import branch, revision, errors
from bzrlib.revisionspec import RevisionSpec
except ImportError:
pass
@@ -42,17 +42,14 @@ class bzr_source(converter_source):
try:
# access bzrlib stuff
- bzrdir
+ branch
except NameError:
raise NoRepo(_('Bazaar modules could not be loaded'))
path = os.path.abspath(path)
self._checkrepotype(path)
- try:
- self.sourcerepo = bzrdir.BzrDir.open(path).open_repository()
- except errors.NoRepositoryPresent:
- raise NoRepo(_('%s does not look like a Bazaar repository')
- % path)
+ self.branch = branch.Branch.open(path)
+ self.sourcerepo = self.branch.repository
self._parentids = {}
def _checkrepotype(self, path):
@@ -72,7 +69,7 @@ class bzr_source(converter_source):
self.ui.warn(_('warning: lightweight checkouts may cause '
'conversion failures, try with a regular '
'branch instead.\n'))
- except Exception:
+ except:
self.ui.note(_('bzr source type could not be determined\n'))
def before(self):
@@ -91,28 +88,16 @@ class bzr_source(converter_source):
def after(self):
self.sourcerepo.unlock()
- def _bzrbranches(self):
- return self.sourcerepo.find_branches(using=True)
-
def getheads(self):
if not self.rev:
- # Set using=True to avoid nested repositories (see issue3254)
- heads = sorted([b.last_revision() for b in self._bzrbranches()])
- else:
- revid = None
- for branch in self._bzrbranches():
- try:
- r = RevisionSpec.from_string(self.rev)
- info = r.in_history(branch)
- except errors.BzrError:
- pass
- revid = info.rev_id
- if revid is None:
- raise util.Abort(_('%s is not a valid revision') % self.rev)
- heads = [revid]
- # Empty repositories return 'null:', which cannot be retrieved
- heads = [h for h in heads if h != 'null:']
- return heads
+ return [self.branch.last_revision()]
+ try:
+ r = RevisionSpec.from_string(self.rev)
+ info = r.in_history(self.branch)
+ except errors.BzrError:
+ raise util.Abort(_('%s is not a valid revision in current branch')
+ % self.rev)
+ return [info.rev_id]
def getfile(self, name, rev):
revtree = self.sourcerepo.revision_tree(rev)
@@ -155,24 +140,20 @@ class bzr_source(converter_source):
parents = self._filterghosts(rev.parent_ids)
self._parentids[version] = parents
- branch = self.recode(rev.properties.get('branch-nick', u'default'))
- if branch == 'trunk':
- branch = 'default'
return commit(parents=parents,
date='%d %d' % (rev.timestamp, -rev.timezone),
author=self.recode(rev.committer),
+ # bzr returns bytestrings or unicode, depending on the content
desc=self.recode(rev.message),
- branch=branch,
rev=version)
def gettags(self):
+ if not self.branch.supports_tags():
+ return {}
+ tagdict = self.branch.tags.get_tag_dict()
bytetags = {}
- for branch in self._bzrbranches():
- if not branch.supports_tags():
- return {}
- tagdict = branch.tags.get_tag_dict()
- for name, rev in tagdict.iteritems():
- bytetags[self.recode(name)] = rev
+ for name, rev in tagdict.iteritems():
+ bytetags[self.recode(name)] = rev
return bytetags
def getchangedfiles(self, rev, i):
@@ -192,14 +173,8 @@ class bzr_source(converter_source):
revid = current._revision_id
changes = []
renames = {}
- seen = set()
- # Process the entries by reverse lexicographic name order to
- # handle nested renames correctly, most specific first.
- curchanges = sorted(current.iter_changes(origin),
- key=lambda c: c[1][0] or c[1][1],
- reverse=True)
for (fileid, paths, changed_content, versioned, parent, name,
- kind, executable) in curchanges:
+ kind, executable) in current.iter_changes(origin):
if paths[0] == u'' or paths[1] == u'':
# ignore changes to tree root
@@ -213,8 +188,7 @@ class bzr_source(converter_source):
# so it can be removed.
changes.append((self.recode(paths[0]), revid))
- if kind[0] == 'directory' and None not in paths:
- renaming = paths[0] != paths[1]
+ if None not in paths and paths[0] != paths[1]:
# neither an add nor an delete - a move
# rename all directory contents manually
subdir = origin.inventory.path2id(paths[0])
@@ -224,16 +198,6 @@ class bzr_source(converter_source):
if entry.kind == 'directory':
continue
frompath = self.recode(paths[0] + '/' + name)
- if frompath in seen:
- # Already handled by a more specific change entry
- # This is important when you have:
- # a => b
- # a/c => a/c
- # Here a/c must not be renamed into b/c
- continue
- seen.add(frompath)
- if not renaming:
- continue
topath = self.recode(paths[1] + '/' + name)
# register the files as changed
changes.append((frompath, revid))
@@ -250,12 +214,7 @@ class bzr_source(converter_source):
continue
# we got unicode paths, need to convert them
- path, topath = paths
- if path is not None:
- path = self.recode(path)
- if topath is not None:
- topath = self.recode(topath)
- seen.add(path or topath)
+ path, topath = [self.recode(part) for part in paths]
if topath is None:
# file deleted
@@ -283,3 +242,19 @@ class bzr_source(converter_source):
parentmap = self.sourcerepo.get_parent_map(ids)
parents = tuple([parent for parent in ids if parent in parentmap])
return parents
+
+ def recode(self, s, encoding=None):
+ """This version of recode tries to encode unicode to bytecode,
+ and preferably using the UTF-8 codec.
+ Other types than Unicode are silently returned, this is by
+ intention, e.g. the None-type is not going to be encoded but instead
+ just passed through
+ """
+ if not encoding:
+ encoding = self.encoding or 'utf-8'
+
+ if isinstance(s, unicode):
+ return s.encode(encoding)
+ else:
+ # leave it alone
+ return s
diff --git a/hgext/convert/common.py b/hgext/convert/common.py
index e30ef2d..29b01b5 100644
--- a/hgext/convert/common.py
+++ b/hgext/convert/common.py
@@ -11,8 +11,6 @@ import cPickle as pickle
from mercurial import util
from mercurial.i18n import _
-propertycache = util.propertycache
-
def encodeargs(args):
def encodearg(s):
lines = base64.encodestring(s)
@@ -76,7 +74,7 @@ class converter_source(object):
def getheads(self):
"""Return a list of this repository's heads"""
- raise NotImplementedError
+ raise NotImplementedError()
def getfile(self, name, rev):
"""Return a pair (data, mode) where data is the file content
@@ -84,7 +82,7 @@ class converter_source(object):
identifier returned by a previous call to getchanges(). Raise
IOError to indicate that name was deleted in rev.
"""
- raise NotImplementedError
+ raise NotImplementedError()
def getchanges(self, version):
"""Returns a tuple of (files, copies).
@@ -95,18 +93,18 @@ class converter_source(object):
copies is a dictionary of dest: source
"""
- raise NotImplementedError
+ raise NotImplementedError()
def getcommit(self, version):
"""Return the commit object for version"""
- raise NotImplementedError
+ raise NotImplementedError()
def gettags(self):
"""Return the tags as a dictionary of name: revision
Tag names must be UTF-8 strings.
"""
- raise NotImplementedError
+ raise NotImplementedError()
def recode(self, s, encoding=None):
if not encoding:
@@ -116,10 +114,10 @@ class converter_source(object):
return s.encode("utf-8")
try:
return s.decode(encoding).encode("utf-8")
- except UnicodeError:
+ except:
try:
return s.decode("latin-1").encode("utf-8")
- except UnicodeError:
+ except:
return s.decode(encoding, "replace").encode("utf-8")
def getchangedfiles(self, rev, i):
@@ -133,7 +131,7 @@ class converter_source(object):
This function is only needed to support --filemap
"""
- raise NotImplementedError
+ raise NotImplementedError()
def converted(self, rev, sinkrev):
'''Notify the source that a revision has been converted.'''
@@ -175,13 +173,13 @@ class converter_sink(object):
def getheads(self):
"""Return a list of this repository's heads"""
- raise NotImplementedError
+ raise NotImplementedError()
def revmapfile(self):
"""Path to a file that will contain lines
source_rev_id sink_rev_id
mapping equivalent revision identifiers for each system."""
- raise NotImplementedError
+ raise NotImplementedError()
def authorfile(self):
"""Path to a file that will contain lines
@@ -203,7 +201,7 @@ class converter_sink(object):
a particular revision (or even what that revision would be)
before it receives the file data.
"""
- raise NotImplementedError
+ raise NotImplementedError()
def puttags(self, tags):
"""Put tags into sink.
@@ -212,7 +210,7 @@ class converter_sink(object):
Return a pair (tag_revision, tag_parent_revision), or (None, None)
if nothing was changed.
"""
- raise NotImplementedError
+ raise NotImplementedError()
def setbranch(self, branch, pbranches):
"""Set the current branch name. Called before the first putcommit
@@ -245,10 +243,6 @@ class converter_sink(object):
"""
pass
- def hascommit(self, rev):
- """Return True if the sink contains rev"""
- raise NotImplementedError
-
class commandline(object):
def __init__(self, ui, command):
self.ui = ui
@@ -327,13 +321,15 @@ class commandline(object):
self.checkexit(status, ''.join(output))
return output
- @propertycache
- def argmax(self):
+ def getargmax(self):
+ if '_argmax' in self.__dict__:
+ return self._argmax
+
# POSIX requires at least 4096 bytes for ARG_MAX
- argmax = 4096
+ self._argmax = 4096
try:
- argmax = os.sysconf("SC_ARG_MAX")
- except (AttributeError, ValueError):
+ self._argmax = os.sysconf("SC_ARG_MAX")
+ except:
pass
# Windows shells impose their own limits on command line length,
@@ -343,11 +339,13 @@ class commandline(object):
# Since ARG_MAX is for command line _and_ environment, lower our limit
# (and make happy Windows shells while doing this).
- return argmax // 2 - 1
+
+ self._argmax = self._argmax / 2 - 1
+ return self._argmax
def limit_arglist(self, arglist, cmd, closestdin, *args, **kwargs):
cmdlen = len(self._cmdline(cmd, closestdin, *args, **kwargs))
- limit = self.argmax - cmdlen
+ limit = self.getargmax() - cmdlen
bytes = 0
fl = []
for fn in arglist:
@@ -385,12 +383,8 @@ class mapfile(dict):
raise
return
for i, line in enumerate(fp):
- line = line.splitlines()[0].rstrip()
- if not line:
- # Ignore blank lines
- continue
try:
- key, value = line.rsplit(' ', 1)
+ key, value = line.splitlines()[0].rsplit(' ', 1)
except ValueError:
raise util.Abort(
_('syntax error in %s(%d): key/value pair expected')
@@ -415,31 +409,3 @@ class mapfile(dict):
if self.fp:
self.fp.close()
self.fp = None
-
-def parsesplicemap(path):
- """Parse a splicemap, return a child/parents dictionary."""
- if not path:
- return {}
- m = {}
- try:
- fp = open(path, 'r')
- for i, line in enumerate(fp):
- line = line.splitlines()[0].rstrip()
- if not line:
- # Ignore blank lines
- continue
- try:
- child, parents = line.split(' ', 1)
- parents = parents.replace(',', ' ').split()
- except ValueError:
- raise util.Abort(_('syntax error in %s(%d): child parent1'
- '[,parent2] expected') % (path, i + 1))
- pp = []
- for p in parents:
- if p not in pp:
- pp.append(p)
- m[child] = pp
- except IOError, e:
- if e.errno != errno.ENOENT:
- raise
- return m
diff --git a/hgext/convert/convcmd.py b/hgext/convert/convcmd.py
index c8fe845..578272a 100644
--- a/hgext/convert/convcmd.py
+++ b/hgext/convert/convcmd.py
@@ -15,7 +15,7 @@ from monotone import monotone_source
from gnuarch import gnuarch_source
from bzr import bzr_source
from p4 import p4_source
-import filemap, common
+import filemap
import os, shutil
from mercurial import hg, util, encoding
@@ -118,7 +118,7 @@ class converter(object):
self.readauthormap(opts.get('authormap'))
self.authorfile = self.dest.authorfile()
- self.splicemap = common.parsesplicemap(opts.get('splicemap'))
+ self.splicemap = mapfile(ui, opts.get('splicemap'))
self.branchmap = mapfile(ui, opts.get('branchmap'))
def walktree(self, heads):
@@ -142,29 +142,6 @@ class converter(object):
return parents
- def mergesplicemap(self, parents, splicemap):
- """A splicemap redefines child/parent relationships. Check the
- map contains valid revision identifiers and merge the new
- links in the source graph.
- """
- for c in splicemap:
- if c not in parents:
- if not self.dest.hascommit(self.map.get(c, c)):
- # Could be in source but not converted during this run
- self.ui.warn(_('splice map revision %s is not being '
- 'converted, ignoring\n') % c)
- continue
- pc = []
- for p in splicemap[c]:
- # We do not have to wait for nodes already in dest.
- if self.dest.hascommit(self.map.get(p, p)):
- continue
- # Parent is not in dest and not being converted, not good
- if p not in parents:
- raise util.Abort(_('unknown splice map parent: %s') % p)
- pc.append(p)
- parents[c] = pc
-
def toposort(self, parents, sortmode):
'''Return an ordering such that every uncommitted changeset is
preceeded by all its uncommitted ancestors.'''
@@ -190,7 +167,7 @@ class converter(object):
children.setdefault(n, [])
hasparent = False
for p in parents[n]:
- if p not in self.map:
+ if not p in self.map:
visit.append(p)
hasparent = True
children.setdefault(p, []).append(n)
@@ -280,7 +257,7 @@ class converter(object):
def writeauthormap(self):
authorfile = self.authorfile
if authorfile:
- self.ui.status(_('writing author map file %s\n') % authorfile)
+ self.ui.status(_('Writing author map file %s\n') % authorfile)
ofile = open(authorfile, 'w+')
for author in self.authors:
ofile.write("%s=%s\n" % (author, self.authors[author]))
@@ -297,7 +274,7 @@ class converter(object):
try:
srcauthor, dstauthor = line.split('=', 1)
except ValueError:
- msg = _('ignoring bad line in author map file %s: %s\n')
+ msg = _('Ignoring bad line in author map file %s: %s\n')
self.ui.warn(msg % (authorfile, line.rstrip()))
continue
@@ -342,7 +319,7 @@ class converter(object):
self.commitcache[prev].branch))
self.dest.setbranch(commit.branch, pbranches)
try:
- parents = self.splicemap[rev]
+ parents = self.splicemap[rev].replace(',', ' ').split()
self.ui.status(_('spliced in %s as parents of %s\n') %
(parents, rev))
parents = [self.map.get(p, p) for p in parents]
@@ -363,7 +340,6 @@ class converter(object):
self.ui.status(_("scanning source...\n"))
heads = self.source.getheads()
parents = self.walktree(heads)
- self.mergesplicemap(parents, self.splicemap)
self.ui.status(_("sorting...\n"))
t = self.toposort(parents, sortmode)
num = len(t)
@@ -462,7 +438,7 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
if not revmapfile:
try:
revmapfile = destc.revmapfile()
- except Exception:
+ except:
revmapfile = os.path.join(destc, "map")
c = converter(ui, srcc, destc, revmapfile, opts)
diff --git a/hgext/convert/cvs.py b/hgext/convert/cvs.py
index 38b1d34..d07ea20 100644
--- a/hgext/convert/cvs.py
+++ b/hgext/convert/cvs.py
@@ -70,7 +70,7 @@ class convert_cvs(converter_source):
cs.author = self.recode(cs.author)
self.lastbranch[cs.branch] = id
cs.comment = self.recode(cs.comment)
- date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
+ date = util.datestr(cs.date)
self.tags.update(dict.fromkeys(cs.tags, id))
files = {}
@@ -121,13 +121,12 @@ class convert_cvs(converter_source):
pf = open(cvspass)
for line in pf.read().splitlines():
part1, part2 = line.split(' ', 1)
- # /1 :pserver:user@example.com:2401/cvsroot/foo
- # Ah<Z
if part1 == '/1':
+ # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
part1, part2 = part2.split(' ', 1)
format = format1
- # :pserver:user@example.com:/cvsroot/foo Ah<Z
else:
+ # :pserver:user@example.com:/cvsroot/foo Ah<Z
format = format0
if part1 == format:
passw = part2
diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py
index 97184d5..1519d41 100644
--- a/hgext/convert/cvsps.py
+++ b/hgext/convert/cvsps.py
@@ -11,7 +11,6 @@ import cPickle as pickle
from mercurial import util
from mercurial.i18n import _
from mercurial import hook
-from mercurial import util
class logentry(object):
'''Class logentry has the following attributes:
@@ -336,8 +335,7 @@ def createlog(ui, directory=None, root="", rlog=True, cache=None):
else:
myrev = '.'.join(myrev[:-2] + ['0', myrev[-2]])
branches = [b for b in branchmap if branchmap[b] == myrev]
- assert len(branches) == 1, ('unknown branch: %s'
- % e.mergepoint)
+ assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
e.mergepoint = branches[0]
else:
e.mergepoint = None
@@ -364,14 +362,8 @@ def createlog(ui, directory=None, root="", rlog=True, cache=None):
elif state == 8:
# store commit log message
if re_31.match(line):
- cpeek = peek
- if cpeek.endswith('\n'):
- cpeek = cpeek[:-1]
- if re_50.match(cpeek):
- state = 5
- store = True
- else:
- e.comment.append(line)
+ state = 5
+ store = True
elif re_32.match(line):
state = 0
store = True
@@ -521,8 +513,8 @@ def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
e.comment == c.comment and
e.author == c.author and
e.branch == c.branch and
- (not util.safehasattr(e, 'branchpoints') or
- not util.safehasattr (c, 'branchpoints') or
+ (not hasattr(e, 'branchpoints') or
+ not hasattr (c, 'branchpoints') or
e.branchpoints == c.branchpoints) and
((c.date[0] + c.date[1]) <=
(e.date[0] + e.date[1]) <=
@@ -557,25 +549,27 @@ def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
# Sort files in each changeset
- def entitycompare(l, r):
- 'Mimic cvsps sorting order'
- l = l.file.split('/')
- r = r.file.split('/')
- nl = len(l)
- nr = len(r)
- n = min(nl, nr)
- for i in range(n):
- if i + 1 == nl and nl < nr:
- return -1
- elif i + 1 == nr and nl > nr:
- return +1
- elif l[i] < r[i]:
- return -1
- elif l[i] > r[i]:
- return +1
- return 0
-
for c in changesets:
+ def pathcompare(l, r):
+ 'Mimic cvsps sorting order'
+ l = l.split('/')
+ r = r.split('/')
+ nl = len(l)
+ nr = len(r)
+ n = min(nl, nr)
+ for i in range(n):
+ if i + 1 == nl and nl < nr:
+ return -1
+ elif i + 1 == nr and nl > nr:
+ return +1
+ elif l[i] < r[i]:
+ return -1
+ elif l[i] > r[i]:
+ return +1
+ return 0
+ def entitycompare(l, r):
+ return pathcompare(l.file, r.file)
+
c.entries.sort(entitycompare)
# Sort changesets by date
@@ -706,11 +700,11 @@ def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
if mergeto:
m = mergeto.search(c.comment)
if m:
- if m.groups():
+ try:
m = m.group(1)
if m == 'HEAD':
m = None
- else:
+ except:
m = None # if no group found then merge to HEAD
if m in branches and c.branch != m:
# insert empty changeset for merge
diff --git a/hgext/convert/darcs.py b/hgext/convert/darcs.py
index b10a533..38d79ba 100644
--- a/hgext/convert/darcs.py
+++ b/hgext/convert/darcs.py
@@ -24,7 +24,7 @@ except ImportError:
try:
from elementtree.ElementTree import ElementTree, XMLParser
except ImportError:
- pass
+ ElementTree = None
class darcs_source(converter_source, commandline):
def __init__(self, ui, path, rev=None):
@@ -42,7 +42,7 @@ class darcs_source(converter_source, commandline):
raise util.Abort(_('darcs version 2.1 or newer needed (found %r)') %
version)
- if "ElementTree" not in globals():
+ if ElementTree is None:
raise util.Abort(_("Python ElementTree module is not available"))
self.path = os.path.realpath(path)
@@ -139,7 +139,7 @@ class darcs_source(converter_source, commandline):
# etree can return unicode objects for name, comment, and author,
# so recode() is used to ensure str objects are emitted.
return commit(author=self.recode(elt.get('author')),
- date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
+ date=util.datestr(date),
desc=self.recode(desc).strip(),
parents=self.parents[rev])
diff --git a/hgext/convert/filemap.py b/hgext/convert/filemap.py
index c14df16..34033c7 100644
--- a/hgext/convert/filemap.py
+++ b/hgext/convert/filemap.py
@@ -99,8 +99,6 @@ class filemapper(object):
if newpre == '.':
return suf
if suf:
- if newpre.endswith('/'):
- return newpre + suf
return newpre + '/' + suf
return newpre
return name
@@ -294,34 +292,23 @@ class filemap_source(converter_source):
# A parent p is interesting if its mapped version (self.parentmap[p]):
# - is not SKIPREV
# - is still not in the list of parents (we don't want duplicates)
- # - is not an ancestor of the mapped versions of the other parents or
- # there is no parent in the same branch than the current revision.
+ # - is not an ancestor of the mapped versions of the other parents
mparents = []
- knownparents = set()
- branch = self.commits[rev].branch
- hasbranchparent = False
+ wp = None
for i, p1 in enumerate(parents):
mp1 = self.parentmap[p1]
- if mp1 == SKIPREV or mp1 in knownparents:
+ if mp1 == SKIPREV or mp1 in mparents:
continue
- isancestor = util.any(p2 for p2 in parents
- if p1 != p2 and mp1 != self.parentmap[p2]
- and mp1 in self.wantedancestors[p2])
- if not isancestor and not hasbranchparent and len(parents) > 1:
- # This could be expensive, avoid unnecessary calls.
- if self._cachedcommit(p1).branch == branch:
- hasbranchparent = True
- mparents.append((p1, mp1, i, isancestor))
- knownparents.add(mp1)
- # Discard parents ancestors of other parents if there is a
- # non-ancestor one on the same branch than current revision.
- if hasbranchparent:
- mparents = [p for p in mparents if not p[3]]
- wp = None
- if mparents:
- wp = max(p[2] for p in mparents)
- mparents = [p[1] for p in mparents]
- elif parents:
+ for p2 in parents:
+ if p1 == p2 or mp1 == self.parentmap[p2]:
+ continue
+ if mp1 in self.wantedancestors[p2]:
+ break
+ else:
+ mparents.append(mp1)
+ wp = i
+
+ if wp is None and parents:
wp = 0
self.origparents[rev] = parents
@@ -330,6 +317,7 @@ class filemap_source(converter_source):
if 'close' in self.commits[rev].extra:
# A branch closing revision is only useful if one of its
# parents belong to the branch being closed
+ branch = self.commits[rev].branch
pbranches = [self._cachedcommit(p).branch for p in mparents]
if branch in pbranches:
closed = True
@@ -357,12 +345,13 @@ class filemap_source(converter_source):
# able to get the files later on in getfile, we hide the
# original filename in the rev part of the return value.
changes, copies = self.base.getchanges(rev)
- files = {}
+ newnames = {}
+ files = []
for f, r in changes:
newf = self.filemapper(f)
- if newf and (newf != f or newf not in files):
- files[newf] = (f, r)
- files = sorted(files.items())
+ if newf:
+ files.append((newf, (f, r)))
+ newnames[f] = newf
ncopies = {}
for c in copies:
@@ -386,6 +375,3 @@ class filemap_source(converter_source):
def lookuprev(self, rev):
return self.base.lookuprev(rev)
-
- def getbookmarks(self):
- return self.base.getbookmarks()
diff --git a/hgext/convert/git.py b/hgext/convert/git.py
index 8058399..e35e128 100644
--- a/hgext/convert/git.py
+++ b/hgext/convert/git.py
@@ -16,7 +16,7 @@ class convert_git(converter_source):
# Windows does not support GIT_DIR= construct while other systems
# cannot remove environment variable. Just assume none have
# both issues.
- if util.safehasattr(os, 'unsetenv'):
+ if hasattr(os, 'unsetenv'):
def gitopen(self, s, noerr=False):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
@@ -69,7 +69,7 @@ class convert_git(converter_source):
def catfile(self, rev, type):
if rev == hex(nullid):
- raise IOError
+ raise IOError()
data, ret = self.gitread("git cat-file %s %s" % (type, rev))
if ret:
raise util.Abort(_('cannot read %r object at %s') % (type, rev))
@@ -97,8 +97,6 @@ class convert_git(converter_source):
seen.add(f)
entry = entry.split()
h = entry[3]
- if entry[1] == '160000':
- raise util.Abort('git submodules are not supported!')
p = (entry[1] == "100755")
s = (entry[1] == "120000")
self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
@@ -145,30 +143,20 @@ class convert_git(converter_source):
def gettags(self):
tags = {}
- alltags = {}
fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
prefix = 'refs/tags/'
-
- # Build complete list of tags, both annotated and bare ones
for line in fh:
line = line.strip()
+ if not line.endswith("^{}"):
+ continue
node, tag = line.split(None, 1)
if not tag.startswith(prefix):
continue
- alltags[tag[len(prefix):]] = node
+ tag = tag[len(prefix):-3]
+ tags[tag] = node
if fh.close():
raise util.Abort(_('cannot read tags from %s') % self.path)
- # Filter out tag objects for annotated tag refs
- for tag in alltags:
- if tag.endswith('^{}'):
- tags[tag[:-3]] = alltags[tag]
- else:
- if tag + '^{}' in alltags:
- continue
- else:
- tags[tag] = alltags[tag]
-
return tags
def getchangedfiles(self, version, i):
@@ -181,8 +169,8 @@ class convert_git(converter_source):
m, f = l[:-1].split("\t")
changes.append(f)
else:
- fh = self.gitopen('git diff-tree --name-only --root -r %s '
- '"%s^%s" --' % (version, version, i + 1))
+ fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
+ % (version, version, i + 1))
changes = [f.rstrip('\n') for f in fh]
if fh.close():
raise util.Abort(_('cannot read changes in %s') % version)
@@ -211,7 +199,7 @@ class convert_git(converter_source):
continue
name = '%s%s' % (reftype, name[prefixlen:])
bookmarks[name] = rev
- except Exception:
+ except:
pass
return bookmarks
diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py
index 287c771..26c43a5 100644
--- a/hgext/convert/hg.py
+++ b/hgext/convert/hg.py
@@ -70,10 +70,10 @@ class mercurial_sink(converter_sink):
self.wlock.release()
def revmapfile(self):
- return self.repo.join("shamap")
+ return os.path.join(self.path, ".hg", "shamap")
def authorfile(self):
- return self.repo.join("authormap")
+ return os.path.join(self.path, ".hg", "authormap")
def getheads(self):
h = self.repo.changelog.heads()
@@ -95,7 +95,7 @@ class mercurial_sink(converter_sink):
self.after()
try:
self.repo = hg.repository(self.ui, branchpath)
- except Exception:
+ except:
self.repo = hg.repository(self.ui, branchpath, create=True)
self.before()
@@ -105,7 +105,7 @@ class mercurial_sink(converter_sink):
for b in pbranches:
try:
self.repo.lookup(b[0])
- except Exception:
+ except:
missings.setdefault(b[1], []).append(b[0])
if missings:
@@ -178,7 +178,7 @@ class mercurial_sink(converter_sink):
closed = 'close' in commit.extra
if not closed and not man.cmp(m1node, man.revision(mnode)):
self.ui.status(_("filtering out empty revision\n"))
- self.repo.rollback(force=True)
+ self.repo.rollback()
return parent
return p2
@@ -192,7 +192,7 @@ class mercurial_sink(converter_sink):
try:
oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
- except Exception:
+ except:
oldlines = []
newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
@@ -223,12 +223,6 @@ class mercurial_sink(converter_sink):
self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark])
bookmarks.write(self.repo)
- def hascommit(self, rev):
- if rev not in self.repo and self.clonebranches:
- raise util.Abort(_('revision %s not found in destination '
- 'repository (lookups with clonebranches=true '
- 'are not implemented)') % rev)
- return rev in self.repo
class mercurial_source(converter_source):
def __init__(self, ui, path, rev=None):
@@ -241,7 +235,7 @@ class mercurial_source(converter_source):
# try to provoke an exception if this isn't really a hg
# repo, but some other bogus compatible-looking url
if not self.repo.local():
- raise error.RepoError
+ raise error.RepoError()
except error.RepoError:
ui.traceback()
raise NoRepo(_("%s is not a local Mercurial repository") % path)
@@ -259,7 +253,7 @@ class mercurial_source(converter_source):
% startnode)
startrev = self.repo.changelog.rev(startnode)
children = {startnode: 1}
- for rev in self.repo.changelog.descendants([startrev]):
+ for rev in self.repo.changelog.descendants(startrev):
children[self.repo.changelog.node(rev)] = 1
self.keep = children.__contains__
else:
@@ -294,8 +288,7 @@ class mercurial_source(converter_source):
if not parents:
files = sorted(ctx.manifest())
# getcopies() is not needed for roots, but it is a simple way to
- # detect missing revlogs and abort on errors or populate
- # self.ignored
+ # detect missing revlogs and abort on errors or populate self.ignored
self.getcopies(ctx, parents, files)
return [(f, rev) for f in files if f not in self.ignored], {}
if self._changescache and self._changescache[0] == rev:
@@ -343,8 +336,7 @@ class mercurial_source(converter_source):
crev = rev
else:
crev = None
- return commit(author=ctx.user(),
- date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
+ return commit(author=ctx.user(), date=util.datestr(ctx.date()),
desc=ctx.description(), rev=crev, parents=parents,
branch=ctx.branch(), extra=ctx.extra(),
sortkey=ctx.rev())
@@ -372,7 +364,8 @@ class mercurial_source(converter_source):
def converted(self, rev, destrev):
if self.convertfp is None:
- self.convertfp = open(self.repo.join('shamap'), 'a')
+ self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
+ 'a')
self.convertfp.write('%s %s\n' % (destrev, rev))
self.convertfp.flush()
diff --git a/hgext/convert/monotone.py b/hgext/convert/monotone.py
index 969e0e5..c951089 100644
--- a/hgext/convert/monotone.py
+++ b/hgext/convert/monotone.py
@@ -30,7 +30,7 @@ class monotone_source(converter_source, commandline):
f = file(path, 'rb')
header = f.read(16)
f.close()
- except IOError:
+ except:
header = ''
if header != 'SQLite format 3\x00':
raise norepo
@@ -113,7 +113,7 @@ class monotone_source(converter_source, commandline):
stream = self.mtnreadfp.read(1)
if stream not in 'mewptl':
- raise util.Abort(_('bad mtn packet - bad stream type %s') % stream)
+ raise util.Abort(_('bad mtn packet - bad stream type %s' % stream))
read = self.mtnreadfp.read(1)
if read != ':':
@@ -283,11 +283,11 @@ class monotone_source(converter_source, commandline):
def getfile(self, name, rev):
if not self.mtnisfile(name, rev):
- raise IOError # file was deleted or renamed
+ raise IOError() # file was deleted or renamed
try:
data = self.mtnrun("get_file_of", name, r=rev)
- except Exception:
- raise IOError # file was deleted or renamed
+ except:
+ raise IOError() # file was deleted or renamed
self.mtnloadmanifest(rev)
node, attr = self.files.get(name, (None, ""))
return data, attr
@@ -317,7 +317,7 @@ class monotone_source(converter_source, commandline):
def getchangedfiles(self, rev, i):
# This function is only needed to support --filemap
# ... and we don't support that
- raise NotImplementedError
+ raise NotImplementedError()
def before(self):
# Check if we have a new enough version to use automate stdio
diff --git a/hgext/convert/p4.py b/hgext/convert/p4.py
index 76b28e7..5d640ad 100644
--- a/hgext/convert/p4.py
+++ b/hgext/convert/p4.py
@@ -119,8 +119,7 @@ class p4_source(converter_source):
parents = []
date = (int(d["time"]), 0) # timezone not set
- c = commit(author=self.recode(d["user"]),
- date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
+ c = commit(author=self.recode(d["user"]), date=util.datestr(date),
parents=parents, desc=desc, branch='',
extra={"p4": change})
diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py
index 094988b..3e64ce6 100644
--- a/hgext/convert/subversion.py
+++ b/hgext/convert/subversion.py
@@ -2,14 +2,17 @@
#
# Copyright(C) 2007 Daniel Holth et al
-import os, re, sys, tempfile, urllib, urllib2, xml.dom.minidom
+import os
+import re
+import sys
import cPickle as pickle
+import tempfile
+import urllib
+import urllib2
from mercurial import strutil, scmutil, util, encoding
from mercurial.i18n import _
-propertycache = util.propertycache
-
# 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.
@@ -47,21 +50,10 @@ def revsplit(rev):
mod = '/' + parts[1]
return parts[0][4:], mod, int(revnum)
-def quote(s):
- # As of svn 1.7, many svn calls expect "canonical" paths. In
- # theory, we should call svn.core.*canonicalize() on all paths
- # before passing them to the API. Instead, we assume the base url
- # is canonical and copy the behaviour of svn URL encoding function
- # so we can extend it safely with new components. The "safe"
- # characters were taken from the "svn_uri__char_validity" table in
- # libsvn_subr/path.c.
- return urllib.quote(s, "!$&'()*+,-./:=@_~")
-
def geturl(path):
try:
return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
except SubversionException:
- # svn.client.url_from_path() fails with local repositories
pass
if os.path.isdir(path):
path = os.path.normpath(os.path.abspath(path))
@@ -70,8 +62,8 @@ def geturl(path):
# Module URL is later compared with the repository URL returned
# by svn API, which is UTF-8.
path = encoding.tolocal(path)
- path = 'file://%s' % quote(path)
- return svn.core.svn_path_canonicalize(path)
+ return 'file://%s' % urllib.quote(path)
+ return path
def optrev(number):
optrev = svn.core.svn_opt_revision_t()
@@ -85,8 +77,8 @@ class changedpath(object):
self.copyfrom_rev = p.copyfrom_rev
self.action = p.action
-def get_log_child(fp, url, paths, start, end, limit=0,
- discover_changed_paths=True, strict_node_history=False):
+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:
@@ -103,11 +95,11 @@ def get_log_child(fp, url, paths, start, end, limit=0,
discover_changed_paths,
strict_node_history,
receiver)
+ except SubversionException, (inst, num):
+ pickle.dump(num, fp, protocol)
except IOError:
# Caller may interrupt the iteration
pickle.dump(None, fp, protocol)
- except Exception, inst:
- pickle.dump(str(inst), fp, protocol)
else:
pickle.dump(None, fp, protocol)
fp.close()
@@ -120,10 +112,6 @@ def debugsvnlog(ui, **opts):
"""Fetch SVN log in a subprocess and channel them back to parent to
avoid memory collection issues.
"""
- if svn is None:
- raise util.Abort(_('debugsvnlog could not load Subversion python '
- 'bindings'))
-
util.setbinary(sys.stdin)
util.setbinary(sys.stdout)
args = decodeargs(sys.stdin.read())
@@ -143,10 +131,10 @@ class logstream(object):
' hg executable is in PATH'))
try:
orig_paths, revnum, author, date, message = entry
- except (TypeError, ValueError):
+ except:
if entry is None:
break
- raise util.Abort(_("log stream exception '%s'") % entry)
+ raise SubversionException("child raised exception", entry)
yield entry
def close(self):
@@ -180,7 +168,7 @@ def httpcheck(ui, path, proto):
'know better.\n'))
return True
data = inst.fp.read()
- except Exception:
+ except:
# Could be urllib2.URLError if the URL is invalid or anything else.
return False
return '<m:human-readable errcode="160013">' in data
@@ -193,15 +181,12 @@ def issvnurl(ui, url):
try:
proto, path = url.split('://', 1)
if proto == 'file':
- if (os.name == 'nt' and path[:1] == '/' and path[1:2].isalpha()
- and path[2:6].lower() == '%3a/'):
- path = path[:2] + ':/' + path[6:]
path = urllib.url2pathname(path)
except ValueError:
proto = 'file'
path = os.path.abspath(url)
if proto == 'file':
- path = util.pconvert(path)
+ path = path.replace(os.sep, '/')
check = protomap.get(proto, lambda *args: False)
while '/' in path:
if check(ui, path, proto):
@@ -234,7 +219,7 @@ class svn_source(converter_source):
raise NoRepo(_("%s does not look like a Subversion repository")
% url)
if svn is None:
- raise MissingTool(_('could not load Subversion python bindings'))
+ raise MissingTool(_('Could not load Subversion python bindings'))
try:
version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
@@ -283,8 +268,7 @@ class svn_source(converter_source):
except ValueError:
raise util.Abort(_('svn: revision %s is not an integer') % rev)
- self.trunkname = self.ui.config('convert', 'svn.trunk',
- 'trunk').strip('/')
+ self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
try:
self.startrev = int(self.startrev)
@@ -322,7 +306,7 @@ class svn_source(converter_source):
def exists(self, path, optrev):
try:
- svn.client.ls(self.url.rstrip('/') + '/' + quote(path),
+ svn.client.ls(self.url.rstrip('/') + '/' + urllib.quote(path),
optrev, False, self.ctx)
return True
except SubversionException:
@@ -374,7 +358,7 @@ class svn_source(converter_source):
# Check if branches bring a few more heads to the list
if branches:
rpath = self.url.strip('/')
- branchnames = svn.client.ls(rpath + '/' + quote(branches),
+ branchnames = svn.client.ls(rpath + '/' + urllib.quote(branches),
rev, False, self.ctx)
for branch in branchnames.keys():
module = '%s/%s/%s' % (oldmodule, branches, branch)
@@ -410,7 +394,7 @@ class svn_source(converter_source):
else:
# Perform a full checkout on roots
uuid, module, revnum = revsplit(rev)
- entries = svn.client.ls(self.baseurl + quote(module),
+ entries = svn.client.ls(self.baseurl + urllib.quote(module),
optrev(revnum), True, self.ctx)
files = [n for n, e in entries.iteritems()
if e.kind == svn.core.svn_node_file]
@@ -444,8 +428,6 @@ class svn_source(converter_source):
if revnum < stop:
stop = revnum + 1
self._fetch_revisions(revnum, stop)
- if rev not in self.commits:
- raise util.Abort(_('svn: revision %s not found') % revnum)
commit = self.commits[rev]
# caller caches the result, so free it here to release memory
del self.commits[rev]
@@ -519,11 +501,11 @@ class svn_source(converter_source):
and not p[2].startswith(badroot + '/')]
# Tell tag renamings from tag creations
- renamings = []
+ remainings = []
for source, sourcerev, dest in pendings:
tagname = dest.split('/')[-1]
if source.startswith(srctagspath):
- renamings.append([source, sourcerev, tagname])
+ remainings.append([source, sourcerev, tagname])
continue
if tagname in tags:
# Keep the latest tag value
@@ -539,7 +521,7 @@ class svn_source(converter_source):
# but were really created in the tag
# directory.
pass
- pendings = renamings
+ pendings = remainings
tagspath = srctagspath
finally:
stream.close()
@@ -560,47 +542,18 @@ class svn_source(converter_source):
def revnum(self, rev):
return int(rev.split('@')[-1])
- def latest(self, path, stop=None):
- """Find the latest revid affecting path, up to stop revision
- number. If stop is None, default to repository latest
- revision. It may return a revision in a different module,
- since a branch may be moved without a change being
- reported. Return None if computed module does not belong to
- rootmodule subtree.
+ def latest(self, path, stop=0):
+ """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
+ a change being reported. Return None if computed module does not
+ belong to rootmodule subtree.
"""
- def findchanges(path, start, stop=None):
- stream = self._getlog([path], start, stop or 1)
- try:
- for entry in stream:
- paths, revnum, author, date, message = entry
- if stop is None and paths:
- # We do not know the latest changed revision,
- # keep the first one with changed paths.
- break
- if revnum <= stop:
- 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):]
- self.ui.debug("branch renamed from %s to %s at %d\n" %
- (path, newpath, revnum))
- path = newpath
- break
- if not paths:
- revnum = None
- return revnum, path
- finally:
- stream.close()
-
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
- if stop is None:
+ if not stop:
stop = svn.ra.get_latest_revnum(self.ra)
try:
prevmodule = self.reparent('')
@@ -615,30 +568,34 @@ class svn_source(converter_source):
# 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.
- revnum, realpath = findchanges(path, stop, dirent.created_rev)
- if revnum is None:
- # Tools like svnsync can create empty revision, when
- # synchronizing only a subtree for instance. These empty
- # revisions created_rev still have their original values
- # despite all changes having disappeared and can be
- # returned by ra.stat(), at least when stating the root
- # module. In that case, do not trust created_rev and scan
- # the whole history.
- revnum, realpath = findchanges(path, stop)
- if revnum is None:
- self.ui.debug('ignoring empty branch %r\n' % realpath)
- return None
+ stream = self._getlog([path], stop, dirent.created_rev)
+ try:
+ for entry in stream:
+ paths, revnum, author, date, message = entry
+ if revnum <= dirent.created_rev:
+ break
- if not realpath.startswith(self.rootmodule):
- self.ui.debug('ignoring foreign branch %r\n' % realpath)
+ for p in paths:
+ if not path.startswith(p) or not paths[p].copyfrom_path:
+ continue
+ newpath = paths[p].copyfrom_path + path[len(p):]
+ self.ui.debug("branch renamed from %s to %s at %d\n" %
+ (path, newpath, revnum))
+ path = newpath
+ break
+ finally:
+ stream.close()
+
+ if not path.startswith(self.rootmodule):
+ self.ui.debug('ignoring foreign branch %r\n' % path)
return None
- return self.revid(revnum, realpath)
+ return self.revid(dirent.created_rev, path)
def reparent(self, module):
"""Reparent the svn transport and return the previous parent."""
if self.prevmodule == module:
return module
- svnurl = self.baseurl + quote(module)
+ svnurl = self.baseurl + urllib.quote(module)
prevmodule = self.prevmodule
if prevmodule is None:
prevmodule = ''
@@ -813,7 +770,7 @@ class svn_source(converter_source):
branch = None
cset = commit(author=author,
- date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
+ date=util.datestr(date),
desc=log,
parents=parents,
branch=branch,
@@ -870,14 +827,13 @@ class svn_source(converter_source):
pass
except SubversionException, (inst, num):
if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
- raise util.Abort(_('svn: branch has no revision %s')
- % to_revnum)
+ raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
raise
def getfile(self, file, rev):
# TODO: ra.get_file transmits the whole file instead of diffs.
if file in self.removed:
- raise IOError
+ raise IOError()
mode = ''
try:
new_module, revnum = revsplit(rev)[1:]
@@ -898,7 +854,7 @@ class svn_source(converter_source):
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 IOError()
raise
if mode == 'l':
link_prefix = "link "
@@ -910,7 +866,7 @@ class svn_source(converter_source):
"""Enumerate all files in path at revnum, recursively."""
path = path.strip('/')
pool = Pool()
- rpath = '/'.join([self.baseurl, quote(path)]).strip('/')
+ rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)
if path:
path += '/'
@@ -958,8 +914,8 @@ class svn_source(converter_source):
if not p.startswith('/'):
p = self.module + '/' + p
relpaths.append(p.strip('/'))
- args = [self.baseurl, relpaths, start, end, limit,
- discover_changed_paths, strict_node_history]
+ args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
+ strict_node_history]
arg = encodeargs(args)
hgexe = util.hgexecutable()
cmd = '%s debugsvnlog' % util.shellquote(hgexe)
@@ -1020,25 +976,26 @@ class svn_sink(converter_sink, commandline):
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 = os.path.realpath(path)
+ self.wc = path
self.run0('update')
else:
- if not re.search(r'^(file|http|https|svn|svn\+ssh)\://', path):
- path = os.path.realpath(path)
- if os.path.isdir(os.path.dirname(path)):
- if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
- ui.status(_('initializing svn repository %r\n') %
- os.path.basename(path))
- commandline(ui, 'svnadmin').run0('create', path)
- created = path
- path = util.normpath(path)
- if not path.startswith('/'):
- path = '/' + path
- path = 'file://' + path
-
wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc')
+
+ if os.path.isdir(os.path.dirname(path)):
+ if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
+ ui.status(_('initializing svn repository %r\n') %
+ os.path.basename(path))
+ commandline(ui, 'svnadmin').run0('create', path)
+ created = path
+ path = util.normpath(path)
+ if not path.startswith('/'):
+ path = '/' + path
+ path = 'file://' + path
+
ui.status(_('initializing svn working copy %r\n')
% os.path.basename(wcpath))
self.run0('checkout', path, wcpath)
@@ -1062,29 +1019,6 @@ class svn_sink(converter_sink, commandline):
def wjoin(self, *names):
return os.path.join(self.wc, *names)
- @propertycache
- def manifest(self):
- # As of svn 1.7, the "add" command fails when receiving
- # already tracked entries, so we have to track and filter them
- # ourselves.
- m = set()
- output = self.run0('ls', recursive=True, xml=True)
- doc = xml.dom.minidom.parseString(output)
- for e in doc.getElementsByTagName('entry'):
- for n in e.childNodes:
- if n.nodeType != n.ELEMENT_NODE or n.tagName != 'name':
- continue
- name = ''.join(c.data for c in n.childNodes
- if c.nodeType == c.TEXT_NODE)
- # Entries are compared with names coming from
- # mercurial, so bytes with undefined encoding. Our
- # best bet is to assume they are in local
- # encoding. They will be passed to command line calls
- # later anyway, so they better be.
- m.add(encoding.tolocal(name.encode('utf-8')))
- break
- return m
-
def putfile(self, filename, flags, data):
if 'l' in flags:
self.wopener.symlink(data, filename)
@@ -1097,13 +1031,20 @@ class svn_sink(converter_sink, commandline):
self.wopener.write(filename, data)
if self.is_exec:
- if self.is_exec(self.wjoin(filename)):
- if 'x' not in flags:
- self.delexec.append(filename)
- else:
- if 'x' in flags:
- self.setexec.append(filename)
- util.setflags(self.wjoin(filename), False, 'x' in flags)
+ 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
+
+ util.setflags(self.wjoin(filename), False, 'x' in flags)
+ if was_exec:
+ if 'x' not in flags:
+ self.delexec.append(filename)
+ else:
+ if 'x' in flags:
+ self.setexec.append(filename)
def _copyfile(self, source, dest):
# SVN's copy command pukes if the destination file exists, but
@@ -1120,7 +1061,6 @@ class svn_sink(converter_sink, commandline):
try:
self.run0('copy', source, dest)
finally:
- self.manifest.add(dest)
if exists:
try:
os.unlink(wdest)
@@ -1139,16 +1079,13 @@ class svn_sink(converter_sink, commandline):
def add_dirs(self, files):
add_dirs = [d for d in sorted(self.dirs_of(files))
- if d not in self.manifest]
+ if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
if add_dirs:
- self.manifest.update(add_dirs)
self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
return add_dirs
def add_files(self, files):
- files = [f for f in files if f not in self.manifest]
if files:
- self.manifest.update(files)
self.xargs(files, 'add', quiet=True)
return files
@@ -1158,7 +1095,6 @@ class svn_sink(converter_sink, commandline):
wd = self.wjoin(d)
if os.listdir(wd) == '.svn':
self.run0('delete', d)
- self.manifest.remove(d)
deleted.append(d)
return deleted
@@ -1169,12 +1105,6 @@ class svn_sink(converter_sink, commandline):
return u"svn:%s@%s" % (self.uuid, rev)
def putcommit(self, files, copies, parents, commit, source, revmap):
- for parent in parents:
- try:
- return self.revid(self.childmap[parent])
- except KeyError:
- pass
-
# Apply changes to working copy
for f, v in files:
try:
@@ -1187,6 +1117,11 @@ class svn_sink(converter_sink, commandline):
self.copies.append([copies[f], f])
files = [f[0] for f in files]
+ for parent in parents:
+ try:
+ return self.revid(self.childmap[parent])
+ except KeyError:
+ pass
entries = set(self.delete)
files = frozenset(files)
entries.update(self.add_dirs(files.difference(entries)))
@@ -1196,8 +1131,6 @@ class svn_sink(converter_sink, commandline):
self.copies = []
if self.delete:
self.xargs(self.delete, 'delete')
- for f in self.delete:
- self.manifest.remove(f)
self.delete = []
entries.update(self.add_files(files.difference(entries)))
entries.update(self.tidy_dirs(entries))
@@ -1240,12 +1173,3 @@ class svn_sink(converter_sink, commandline):
def puttags(self, tags):
self.ui.warn(_('writing Subversion tags is not yet implemented\n'))
return None, None
-
- def hascommit(self, rev):
- # This is not correct as one can convert to an existing subversion
- # repository and childmap would not list all revisions. Too bad.
- if rev in self.childmap:
- return True
- raise util.Abort(_('splice map revision %s not found in subversion '
- 'child map (revision lookups are not implemented)')
- % rev)
diff --git a/hgext/convert/transport.py b/hgext/convert/transport.py
index 6a8c565..db68ede 100644
--- a/hgext/convert/transport.py
+++ b/hgext/convert/transport.py
@@ -15,9 +15,9 @@
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-from mercurial import util
from svn.core import SubversionException, Pool
import svn.ra
import svn.client
@@ -54,7 +54,7 @@ def _create_auth_baton(pool):
if p:
providers.append(p)
else:
- if util.safehasattr(svn.client, 'get_windows_simple_provider'):
+ if hasattr(svn.client, 'get_windows_simple_provider'):
providers.append(svn.client.get_windows_simple_provider(pool))
return svn.core.svn_auth_open(providers, pool)
@@ -73,7 +73,7 @@ class SvnRaTransport(object):
self.password = ''
# Only Subversion 1.4 has reparent()
- if ra is None or not util.safehasattr(svn.ra, 'reparent'):
+ if ra is None or not hasattr(svn.ra, 'reparent'):
self.client = svn.client.create_context(self.pool)
ab = _create_auth_baton(self.pool)
if False:
@@ -85,7 +85,7 @@ class SvnRaTransport(object):
self.client.config = svn_config
try:
self.ra = svn.client.open_ra_session(
- self.svn_url,
+ self.svn_url.encode('utf8'),
self.client, self.pool)
except SubversionException, (inst, num):
if num in (svn.core.SVN_ERR_RA_ILLEGAL_URL,