diff options
Diffstat (limited to 'hgext/convert')
-rw-r--r-- | hgext/convert/__init__.py | 21 | ||||
-rw-r--r-- | hgext/convert/bzr.py | 101 | ||||
-rw-r--r-- | hgext/convert/common.py | 82 | ||||
-rw-r--r-- | hgext/convert/convcmd.py | 38 | ||||
-rw-r--r-- | hgext/convert/cvs.py | 7 | ||||
-rw-r--r-- | hgext/convert/cvsps.py | 60 | ||||
-rw-r--r-- | hgext/convert/darcs.py | 6 | ||||
-rw-r--r-- | hgext/convert/filemap.py | 52 | ||||
-rw-r--r-- | hgext/convert/git.py | 30 | ||||
-rw-r--r-- | hgext/convert/hg.py | 31 | ||||
-rw-r--r-- | hgext/convert/monotone.py | 12 | ||||
-rw-r--r-- | hgext/convert/p4.py | 3 | ||||
-rw-r--r-- | hgext/convert/subversion.py | 262 | ||||
-rw-r--r-- | hgext/convert/transport.py | 10 |
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, |