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).
This commit is contained in:
Durham Goode 2015-07-08 10:27:43 -07:00
parent 59afbb0eb8
commit 0f795691b8
13 changed files with 64 additions and 43 deletions

View File

@ -29,7 +29,7 @@ testedwith = 'internal'
_('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')),

View File

@ -33,8 +33,8 @@ supportedkinds = ('file', 'symlink')
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 @@ class bzr_source(converter_source):
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:']

View File

@ -59,12 +59,12 @@ class commit(object):
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'

View File

@ -46,14 +46,14 @@ sink_converters = [
('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:

View File

@ -15,8 +15,8 @@ from common import makedatetimestamp
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 @@ class convert_cvs(converter_source):
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:

View File

@ -27,8 +27,8 @@ except ImportError:
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

View File

@ -86,8 +86,12 @@ class convert_git(converter_source):
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 @@ class convert_git(converter_source):
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'))

View File

@ -27,8 +27,8 @@ class gnuarch_source(converter_source, commandline):
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")

View File

@ -372,8 +372,11 @@ class mercurial_sink(converter_sink):
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 @@ class mercurial_source(converter_source):
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()

View File

@ -13,14 +13,17 @@ from common import commandline
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 @@ class monotone_source(converter_source, commandline):
# 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:

View File

@ -24,8 +24,8 @@ def loaditer(f):
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 @@ class p4_source(converter_source):
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 @@ class p4_source(converter_source):
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'))

View File

@ -268,8 +268,8 @@ def issvnurl(ui, url):
# 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 @@ class svn_source(converter_source):
"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('/')

View File

@ -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