convert: add support for specifying multiple revs
authorDurham Goode <durham@fb.com>
Wed, 08 Jul 2015 10:27:43 -0700
changeset 25748 baea47cafe75
parent 25747 5a15236f142a
child 25749 f2748cc43b2a
convert: add support for specifying multiple revs Previously convert could only take one '--rev'. This change allows the user to specify multiple --rev entries. For instance, this could allow converting multiple branches (but not all branches) at once from git. In this first patch, we disable support for this for all sources. Future patches will enable it for select sources (like git).
hgext/convert/__init__.py
hgext/convert/bzr.py
hgext/convert/common.py
hgext/convert/convcmd.py
hgext/convert/cvs.py
hgext/convert/darcs.py
hgext/convert/git.py
hgext/convert/gnuarch.py
hgext/convert/hg.py
hgext/convert/monotone.py
hgext/convert/p4.py
hgext/convert/subversion.py
tests/test-convert.t
--- a/hgext/convert/__init__.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/__init__.py	Wed Jul 08 10:27:43 2015 -0700
@@ -29,7 +29,7 @@
       _('FILE')),
     ('s', 'source-type', '', _('source repository type'), _('TYPE')),
     ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
-    ('r', 'rev', '', _('import up to source revision REV'), _('REV')),
+    ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
     ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
     ('', 'filemap', '', _('remap file names using contents of file'),
      _('FILE')),
--- a/hgext/convert/bzr.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/bzr.py	Wed Jul 08 10:27:43 2015 -0700
@@ -33,8 +33,8 @@
 class bzr_source(converter_source):
     """Reads Bazaar repositories by using the Bazaar Python libraries"""
 
-    def __init__(self, ui, path, rev=None):
-        super(bzr_source, self).__init__(ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        super(bzr_source, self).__init__(ui, path, revs=revs)
 
         if not os.path.exists(os.path.join(path, '.bzr')):
             raise NoRepo(_('%s does not look like a Bazaar repository')
@@ -95,20 +95,20 @@
         return self.sourcerepo.find_branches(using=True)
 
     def getheads(self):
-        if not self.rev:
+        if not self.revs:
             # 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)
+                    r = RevisionSpec.from_string(self.revs[0])
                     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)
+                raise util.Abort(_('%s is not a valid revision') % self.revs[0])
             heads = [revid]
         # Empty repositories return 'null:', which cannot be retrieved
         heads = [h for h in heads if h != 'null:']
--- a/hgext/convert/common.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/common.py	Wed Jul 08 10:27:43 2015 -0700
@@ -59,12 +59,12 @@
 class converter_source(object):
     """Conversion source interface"""
 
-    def __init__(self, ui, path=None, rev=None):
+    def __init__(self, ui, path=None, revs=None):
         """Initialize conversion source (or raise NoRepo("message")
         exception if path is not a valid repository)"""
         self.ui = ui
         self.path = path
-        self.rev = rev
+        self.revs = revs
 
         self.encoding = 'utf-8'
 
--- a/hgext/convert/convcmd.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/convcmd.py	Wed Jul 08 10:27:43 2015 -0700
@@ -46,14 +46,14 @@
     ('svn', svn_sink),
     ]
 
-def convertsource(ui, path, type, rev):
+def convertsource(ui, path, type, revs):
     exceptions = []
     if type and type not in [s[0] for s in source_converters]:
         raise util.Abort(_('%s: invalid source repository type') % type)
     for name, source, sortmode in source_converters:
         try:
             if not type or name == type:
-                return source(ui, path, rev), sortmode
+                return source(ui, path, revs), sortmode
         except (NoRepo, MissingTool) as inst:
             exceptions.append(inst)
     if not ui.quiet:
--- a/hgext/convert/cvs.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/cvs.py	Wed Jul 08 10:27:43 2015 -0700
@@ -15,8 +15,8 @@
 import cvsps
 
 class convert_cvs(converter_source):
-    def __init__(self, ui, path, rev=None):
-        super(convert_cvs, self).__init__(ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        super(convert_cvs, self).__init__(ui, path, revs=revs)
 
         cvs = os.path.join(path, "CVS")
         if not os.path.exists(cvs):
@@ -41,14 +41,17 @@
         self.changeset = {}
 
         maxrev = 0
-        if self.rev:
+        if self.revs:
+            if len(self.revs) > 1:
+                raise util.Abort(_('cvs source does not support specifying '
+                                   'multiple revs'))
             # TODO: handle tags
             try:
                 # patchset number?
-                maxrev = int(self.rev)
+                maxrev = int(self.revs[0])
             except ValueError:
                 raise util.Abort(_('revision %s is not a patchset number')
-                                 % self.rev)
+                                 % self.revs[0])
 
         d = os.getcwd()
         try:
--- a/hgext/convert/darcs.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/darcs.py	Wed Jul 08 10:27:43 2015 -0700
@@ -27,8 +27,8 @@
                 pass
 
 class darcs_source(converter_source, commandline):
-    def __init__(self, ui, path, rev=None):
-        converter_source.__init__(self, ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        converter_source.__init__(self, ui, path, revs=revs)
         commandline.__init__(self, ui, 'darcs')
 
         # check for _darcs, ElementTree so that we can easily skip
--- a/hgext/convert/git.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/git.py	Wed Jul 08 10:27:43 2015 -0700
@@ -86,8 +86,12 @@
         data = fh.read()
         return data, fh.close()
 
-    def __init__(self, ui, path, rev=None):
-        super(convert_git, self).__init__(ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        super(convert_git, self).__init__(ui, path, revs=revs)
+
+        if revs and len(revs) > 1:
+            raise util.Abort(_("git source does not support specifying "
+                               "multiple revs"))
 
         if os.path.isdir(path + "/.git"):
             path += "/.git"
@@ -119,11 +123,12 @@
             f.close()
 
     def getheads(self):
-        if not self.rev:
+        if not self.revs:
             heads, ret = self.gitread('git rev-parse --branches --remotes')
             heads = heads.splitlines()
         else:
-            heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
+            heads, ret = self.gitread("git rev-parse --verify %s" %
+                                      self.revs[0])
             heads = [heads[:-1]]
         if ret:
             raise util.Abort(_('cannot retrieve git heads'))
--- a/hgext/convert/gnuarch.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/gnuarch.py	Wed Jul 08 10:27:43 2015 -0700
@@ -27,8 +27,8 @@
             self.ren_files = {}
             self.ren_dirs = {}
 
-    def __init__(self, ui, path, rev=None):
-        super(gnuarch_source, self).__init__(ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        super(gnuarch_source, self).__init__(ui, path, revs=revs)
 
         if not os.path.exists(os.path.join(path, '{arch}')):
             raise NoRepo(_("%s does not look like a GNU Arch repository")
--- a/hgext/convert/hg.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/hg.py	Wed Jul 08 10:27:43 2015 -0700
@@ -372,8 +372,11 @@
         return rev in self.repo
 
 class mercurial_source(converter_source):
-    def __init__(self, ui, path, rev=None):
-        converter_source.__init__(self, ui, path, rev)
+    def __init__(self, ui, path, revs=None):
+        converter_source.__init__(self, ui, path, revs)
+        if revs and len(revs) > 1:
+            raise util.Abort(_("mercurial source does not support specifying "
+                               "multiple revisions"))
         self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
         self.ignored = set()
         self.saverev = ui.configbool('convert', 'hg.saverev', False)
@@ -407,12 +410,12 @@
                 self.keep = children.__contains__
             else:
                 self.keep = util.always
-            if rev:
-                self._heads = [self.repo[rev].node()]
+            if revs:
+                self._heads = [self.repo[revs[0]].node()]
             else:
                 self._heads = self.repo.heads()
         else:
-            if rev or startnode is not None:
+            if revs or startnode is not None:
                 raise util.Abort(_('hg.revs cannot be combined with '
                                    'hg.startrev or --rev'))
             nodes = set()
--- a/hgext/convert/monotone.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/monotone.py	Wed Jul 08 10:27:43 2015 -0700
@@ -13,14 +13,17 @@
 from mercurial.i18n import _
 
 class monotone_source(converter_source, commandline):
-    def __init__(self, ui, path=None, rev=None):
-        converter_source.__init__(self, ui, path, rev)
+    def __init__(self, ui, path=None, revs=None):
+        converter_source.__init__(self, ui, path, revs)
+        if revs and len(revs) > 1:
+            raise util.Abort(_('monotone source does not support specifying '
+                               'multiple revs'))
         commandline.__init__(self, ui, 'mtn')
 
         self.ui = ui
         self.path = path
         self.automatestdio = False
-        self.rev = rev
+        self.revs = revs
 
         norepo = NoRepo(_("%s does not look like a monotone repository")
                         % path)
@@ -219,10 +222,10 @@
     # implement the converter_source interface:
 
     def getheads(self):
-        if not self.rev:
+        if not self.revs:
             return self.mtnrun("leaves").splitlines()
         else:
-            return [self.rev]
+            return self.revs
 
     def getchanges(self, rev, full):
         if full:
--- a/hgext/convert/p4.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/p4.py	Wed Jul 08 10:27:43 2015 -0700
@@ -24,8 +24,8 @@
         pass
 
 class p4_source(converter_source):
-    def __init__(self, ui, path, rev=None):
-        super(p4_source, self).__init__(ui, path, rev=rev)
+    def __init__(self, ui, path, revs=None):
+        super(p4_source, self).__init__(ui, path, revs=revs)
 
         if "/" in path and not path.startswith('//'):
             raise NoRepo(_('%s does not look like a P4 repository') % path)
@@ -49,6 +49,9 @@
             r":[^$\n]*\$")
         self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
 
+        if revs and len(revs) > 1:
+            raise util.Abort(_("p4 source does not support specifying "
+                               "multiple revisions"))
         self._parse(ui, path)
 
     def _parse_view(self, path):
@@ -99,7 +102,7 @@
         startrev = self.ui.config('convert', 'p4.startrev', default=0)
         self.p4changes = [x for x in self.p4changes
                           if ((not startrev or int(x) >= int(startrev)) and
-                              (not self.rev or int(x) <= int(self.rev)))]
+                              (not self.revs or int(x) <= int(self.revs[0])))]
 
         # now read the full changelists to get the list of file revisions
         ui.status(_('collecting p4 changelists\n'))
--- a/hgext/convert/subversion.py	Mon Jul 06 01:38:37 2015 +0800
+++ b/hgext/convert/subversion.py	Wed Jul 08 10:27:43 2015 -0700
@@ -268,8 +268,8 @@
 # the parent module. A revision has at most one parent.
 #
 class svn_source(converter_source):
-    def __init__(self, ui, url, rev=None):
-        super(svn_source, self).__init__(ui, url, rev=rev)
+    def __init__(self, ui, url, revs=None):
+        super(svn_source, self).__init__(ui, url, revs=revs)
 
         if not (url.startswith('svn://') or url.startswith('svn+ssh://') or
                 (os.path.exists(url) and
@@ -325,11 +325,15 @@
                            "to libsvn version %s")
                          % (self.url, svnversion))
 
-        if rev:
+        if revs:
+            if len(revs) > 1:
+                raise util.Abort(_('subversion source does not support '
+                                   'specifying multiple revisions'))
             try:
-                latest = int(rev)
+                latest = int(revs[0])
             except ValueError:
-                raise util.Abort(_('svn: revision %s is not an integer') % rev)
+                raise util.Abort(_('svn: revision %s is not an integer') %
+                                 revs[0])
 
         self.trunkname = self.ui.config('convert', 'svn.trunk',
                                         'trunk').strip('/')
--- a/tests/test-convert.t	Mon Jul 06 01:38:37 2015 +0800
+++ b/tests/test-convert.t	Wed Jul 08 10:27:43 2015 -0700
@@ -304,11 +304,11 @@
                     does not convert tags from the source repo to the target
                     repo. The default is False.
   
-  options:
+  options ([+] can be repeated):
   
    -s --source-type TYPE source repository type
    -d --dest-type TYPE   destination repository type
-   -r --rev REV          import up to source revision REV
+   -r --rev REV [+]      import up to source revision REV
    -A --authormap FILE   remap usernames using this file
       --filemap FILE     remap file names using contents of file
       --full             apply filemap changes by converting all files again