hgext/convert/subversion.py
changeset 26587 56b2bcea2529
parent 25748 baea47cafe75
child 27314 a434f15dc0f4
equal deleted inserted replaced
26586:d51c658d3f04 26587:56b2bcea2529
     4 
     4 
     5 import os, re, sys, tempfile, urllib, urllib2
     5 import os, re, sys, tempfile, urllib, urllib2
     6 import xml.dom.minidom
     6 import xml.dom.minidom
     7 import cPickle as pickle
     7 import cPickle as pickle
     8 
     8 
     9 from mercurial import strutil, scmutil, util, encoding
     9 from mercurial import strutil, scmutil, util, encoding, error
    10 from mercurial.i18n import _
    10 from mercurial.i18n import _
    11 
    11 
    12 propertycache = util.propertycache
    12 propertycache = util.propertycache
    13 
    13 
    14 # Subversion stuff. Works best with very recent Python SVN bindings
    14 # Subversion stuff. Works best with very recent Python SVN bindings
   139 def debugsvnlog(ui, **opts):
   139 def debugsvnlog(ui, **opts):
   140     """Fetch SVN log in a subprocess and channel them back to parent to
   140     """Fetch SVN log in a subprocess and channel them back to parent to
   141     avoid memory collection issues.
   141     avoid memory collection issues.
   142     """
   142     """
   143     if svn is None:
   143     if svn is None:
   144         raise util.Abort(_('debugsvnlog could not load Subversion python '
   144         raise error.Abort(_('debugsvnlog could not load Subversion python '
   145                            'bindings'))
   145                            'bindings'))
   146 
   146 
   147     util.setbinary(sys.stdin)
   147     util.setbinary(sys.stdin)
   148     util.setbinary(sys.stdout)
   148     util.setbinary(sys.stdout)
   149     args = decodeargs(sys.stdin.read())
   149     args = decodeargs(sys.stdin.read())
   157     def __iter__(self):
   157     def __iter__(self):
   158         while True:
   158         while True:
   159             try:
   159             try:
   160                 entry = pickle.load(self._stdout)
   160                 entry = pickle.load(self._stdout)
   161             except EOFError:
   161             except EOFError:
   162                 raise util.Abort(_('Mercurial failed to run itself, check'
   162                 raise error.Abort(_('Mercurial failed to run itself, check'
   163                                    ' hg executable is in PATH'))
   163                                    ' hg executable is in PATH'))
   164             try:
   164             try:
   165                 orig_paths, revnum, author, date, message = entry
   165                 orig_paths, revnum, author, date, message = entry
   166             except (TypeError, ValueError):
   166             except (TypeError, ValueError):
   167                 if entry is None:
   167                 if entry is None:
   168                     break
   168                     break
   169                 raise util.Abort(_("log stream exception '%s'") % entry)
   169                 raise error.Abort(_("log stream exception '%s'") % entry)
   170             yield entry
   170             yield entry
   171 
   171 
   172     def close(self):
   172     def close(self):
   173         if self._stdout:
   173         if self._stdout:
   174             self._stdout.close()
   174             self._stdout.close()
   325                            "to libsvn version %s")
   325                            "to libsvn version %s")
   326                          % (self.url, svnversion))
   326                          % (self.url, svnversion))
   327 
   327 
   328         if revs:
   328         if revs:
   329             if len(revs) > 1:
   329             if len(revs) > 1:
   330                 raise util.Abort(_('subversion source does not support '
   330                 raise error.Abort(_('subversion source does not support '
   331                                    'specifying multiple revisions'))
   331                                    'specifying multiple revisions'))
   332             try:
   332             try:
   333                 latest = int(revs[0])
   333                 latest = int(revs[0])
   334             except ValueError:
   334             except ValueError:
   335                 raise util.Abort(_('svn: revision %s is not an integer') %
   335                 raise error.Abort(_('svn: revision %s is not an integer') %
   336                                  revs[0])
   336                                  revs[0])
   337 
   337 
   338         self.trunkname = self.ui.config('convert', 'svn.trunk',
   338         self.trunkname = self.ui.config('convert', 'svn.trunk',
   339                                         'trunk').strip('/')
   339                                         'trunk').strip('/')
   340         self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
   340         self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
   341         try:
   341         try:
   342             self.startrev = int(self.startrev)
   342             self.startrev = int(self.startrev)
   343             if self.startrev < 0:
   343             if self.startrev < 0:
   344                 self.startrev = 0
   344                 self.startrev = 0
   345         except ValueError:
   345         except ValueError:
   346             raise util.Abort(_('svn: start revision %s is not an integer')
   346             raise error.Abort(_('svn: start revision %s is not an integer')
   347                              % self.startrev)
   347                              % self.startrev)
   348 
   348 
   349         try:
   349         try:
   350             self.head = self.latest(self.module, latest)
   350             self.head = self.latest(self.module, latest)
   351         except SvnPathNotFound:
   351         except SvnPathNotFound:
   352             self.head = None
   352             self.head = None
   353         if not self.head:
   353         if not self.head:
   354             raise util.Abort(_('no revision found in module %s')
   354             raise error.Abort(_('no revision found in module %s')
   355                              % self.module)
   355                              % self.module)
   356         self.last_changed = self.revnum(self.head)
   356         self.last_changed = self.revnum(self.head)
   357 
   357 
   358         self._changescache = (None, None)
   358         self._changescache = (None, None)
   359 
   359 
   394             if not self.exists(path, rev):
   394             if not self.exists(path, rev):
   395                 if self.module.endswith(path) and name == 'trunk':
   395                 if self.module.endswith(path) and name == 'trunk':
   396                     # we are converting from inside this directory
   396                     # we are converting from inside this directory
   397                     return None
   397                     return None
   398                 if cfgpath:
   398                 if cfgpath:
   399                     raise util.Abort(_('expected %s to be at %r, but not found')
   399                     raise error.Abort(_('expected %s to be at %r, but not found'
   400                                  % (name, path))
   400                                        ) % (name, path))
   401                 return None
   401                 return None
   402             self.ui.note(_('found %s at %r\n') % (name, path))
   402             self.ui.note(_('found %s at %r\n') % (name, path))
   403             return path
   403             return path
   404 
   404 
   405         rev = optrev(self.last_changed)
   405         rev = optrev(self.last_changed)
   413         if trunk:
   413         if trunk:
   414             oldmodule = self.module or ''
   414             oldmodule = self.module or ''
   415             self.module += '/' + trunk
   415             self.module += '/' + trunk
   416             self.head = self.latest(self.module, self.last_changed)
   416             self.head = self.latest(self.module, self.last_changed)
   417             if not self.head:
   417             if not self.head:
   418                 raise util.Abort(_('no revision found in module %s')
   418                 raise error.Abort(_('no revision found in module %s')
   419                                  % self.module)
   419                                  % self.module)
   420 
   420 
   421         # First head in the list is the module's head
   421         # First head in the list is the module's head
   422         self.heads = [self.head]
   422         self.heads = [self.head]
   423         if self.tags is not None:
   423         if self.tags is not None:
   440                              (branch, self.revnum(brevid)))
   440                              (branch, self.revnum(brevid)))
   441                 self.heads.append(brevid)
   441                 self.heads.append(brevid)
   442 
   442 
   443         if self.startrev and self.heads:
   443         if self.startrev and self.heads:
   444             if len(self.heads) > 1:
   444             if len(self.heads) > 1:
   445                 raise util.Abort(_('svn: start revision is not supported '
   445                 raise error.Abort(_('svn: start revision is not supported '
   446                                    'with more than one branch'))
   446                                    'with more than one branch'))
   447             revnum = self.revnum(self.heads[0])
   447             revnum = self.revnum(self.heads[0])
   448             if revnum < self.startrev:
   448             if revnum < self.startrev:
   449                 raise util.Abort(
   449                 raise error.Abort(
   450                     _('svn: no revision found after start revision %d')
   450                     _('svn: no revision found after start revision %d')
   451                                  % self.startrev)
   451                                  % self.startrev)
   452 
   452 
   453         return self.heads
   453         return self.heads
   454 
   454 
   500             stop = self.lastrevs.get(module, 0)
   500             stop = self.lastrevs.get(module, 0)
   501             if revnum < stop:
   501             if revnum < stop:
   502                 stop = revnum + 1
   502                 stop = revnum + 1
   503             self._fetch_revisions(revnum, stop)
   503             self._fetch_revisions(revnum, stop)
   504             if rev not in self.commits:
   504             if rev not in self.commits:
   505                 raise util.Abort(_('svn: revision %s not found') % revnum)
   505                 raise error.Abort(_('svn: revision %s not found') % revnum)
   506         revcommit = self.commits[rev]
   506         revcommit = self.commits[rev]
   507         # caller caches the result, so free it here to release memory
   507         # caller caches the result, so free it here to release memory
   508         del self.commits[rev]
   508         del self.commits[rev]
   509         return revcommit
   509         return revcommit
   510 
   510 
   511     def checkrevformat(self, revstr, mapname='splicemap'):
   511     def checkrevformat(self, revstr, mapname='splicemap'):
   512         """ fails if revision format does not match the correct format"""
   512         """ fails if revision format does not match the correct format"""
   513         if not re.match(r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-'
   513         if not re.match(r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-'
   514                               '[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]'
   514                               '[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]'
   515                               '{12,12}(.*)\@[0-9]+$',revstr):
   515                               '{12,12}(.*)\@[0-9]+$',revstr):
   516             raise util.Abort(_('%s entry %s is not a valid revision'
   516             raise error.Abort(_('%s entry %s is not a valid revision'
   517                                ' identifier') % (mapname, revstr))
   517                                ' identifier') % (mapname, revstr))
   518 
   518 
   519     def numcommits(self):
   519     def numcommits(self):
   520         return int(self.head.rsplit('@', 1)[1]) - self.startrev
   520         return int(self.head.rsplit('@', 1)[1]) - self.startrev
   521 
   521 
   949                 except SvnPathNotFound:
   949                 except SvnPathNotFound:
   950                     pass
   950                     pass
   951         except SubversionException as xxx_todo_changeme:
   951         except SubversionException as xxx_todo_changeme:
   952             (inst, num) = xxx_todo_changeme.args
   952             (inst, num) = xxx_todo_changeme.args
   953             if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
   953             if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
   954                 raise util.Abort(_('svn: branch has no revision %s')
   954                 raise error.Abort(_('svn: branch has no revision %s')
   955                                  % to_revnum)
   955                                  % to_revnum)
   956             raise
   956             raise
   957 
   957 
   958     def getfile(self, file, rev):
   958     def getfile(self, file, rev):
   959         # TODO: ra.get_file transmits the whole file instead of diffs.
   959         # TODO: ra.get_file transmits the whole file instead of diffs.
  1050         stdin, stdout = util.popen2(util.quotecommand(cmd))
  1050         stdin, stdout = util.popen2(util.quotecommand(cmd))
  1051         stdin.write(arg)
  1051         stdin.write(arg)
  1052         try:
  1052         try:
  1053             stdin.close()
  1053             stdin.close()
  1054         except IOError:
  1054         except IOError:
  1055             raise util.Abort(_('Mercurial failed to run itself, check'
  1055             raise error.Abort(_('Mercurial failed to run itself, check'
  1056                                ' hg executable is in PATH'))
  1056                                ' hg executable is in PATH'))
  1057         return logstream(stdout)
  1057         return logstream(stdout)
  1058 
  1058 
  1059 pre_revprop_change = '''#!/bin/sh
  1059 pre_revprop_change = '''#!/bin/sh
  1060 
  1060 
  1300             except AttributeError:
  1300             except AttributeError:
  1301                 if parents and not files:
  1301                 if parents and not files:
  1302                     return parents[0]
  1302                     return parents[0]
  1303                 self.ui.warn(_('unexpected svn output:\n'))
  1303                 self.ui.warn(_('unexpected svn output:\n'))
  1304                 self.ui.warn(output)
  1304                 self.ui.warn(output)
  1305                 raise util.Abort(_('unable to cope with svn output'))
  1305                 raise error.Abort(_('unable to cope with svn output'))
  1306             if commit.rev:
  1306             if commit.rev:
  1307                 self.run('propset', 'hg:convert-rev', commit.rev,
  1307                 self.run('propset', 'hg:convert-rev', commit.rev,
  1308                          revprop=True, revision=rev)
  1308                          revprop=True, revision=rev)
  1309             if commit.branch and commit.branch != 'default':
  1309             if commit.branch and commit.branch != 'default':
  1310                 self.run('propset', 'hg:convert-branch', commit.branch,
  1310                 self.run('propset', 'hg:convert-branch', commit.branch,
  1327     def hascommitforsplicemap(self, rev):
  1327     def hascommitforsplicemap(self, rev):
  1328         # This is not correct as one can convert to an existing subversion
  1328         # This is not correct as one can convert to an existing subversion
  1329         # repository and childmap would not list all revisions. Too bad.
  1329         # repository and childmap would not list all revisions. Too bad.
  1330         if rev in self.childmap:
  1330         if rev in self.childmap:
  1331             return True
  1331             return True
  1332         raise util.Abort(_('splice map revision %s not found in subversion '
  1332         raise error.Abort(_('splice map revision %s not found in subversion '
  1333                            'child map (revision lookups are not implemented)')
  1333                            'child map (revision lookups are not implemented)')
  1334                          % rev)
  1334                          % rev)