Merge with crew
Those crew folks are getting lazy about pulling from upstream before
committing.
--- a/hgext/convert/__init__.py Tue Oct 09 17:44:44 2007 -0500
+++ b/hgext/convert/__init__.py Thu Oct 11 00:46:40 2007 -0500
@@ -10,7 +10,7 @@
from darcs import darcs_source
from git import convert_git
from hg import mercurial_source, mercurial_sink
-from subversion import convert_svn, debugsvnlog
+from subversion import svn_source, debugsvnlog
import filemap
import os, shutil
@@ -19,27 +19,32 @@
commands.norepo += " convert debugsvnlog"
-sink_converters = [mercurial_sink]
-source_converters = [convert_cvs, convert_git, convert_svn,
- mercurial_source, darcs_source]
-def convertsource(ui, path, **opts):
- for c in source_converters:
+source_converters = [
+ ('cvs', convert_cvs),
+ ('git', convert_git),
+ ('svn', svn_source),
+ ('hg', mercurial_source),
+ ('darcs', darcs_source),
+ ]
+
+sink_converters = [
+ ('hg', mercurial_sink),
+ ]
+
+def convertsource(ui, path, type, rev):
+ for name, source in source_converters:
try:
- return c.getcommit and c(ui, path, **opts)
- except AttributeError:
- pass
+ if not type or name == type:
+ return source(ui, path, rev)
except NoRepo, inst:
ui.note(_("convert: %s\n") % inst)
raise util.Abort('%s: unknown repository type' % path)
-def convertsink(ui, path):
- if not os.path.isdir(path):
- raise util.Abort("%s: not a directory" % path)
- for c in sink_converters:
+def convertsink(ui, path, type):
+ for name, sink in sink_converters:
try:
- return c.putcommit and c(ui, path)
- except AttributeError:
- pass
+ if not type or name == type:
+ return sink(ui, path)
except NoRepo, inst:
ui.note(_("convert: %s\n") % inst)
raise util.Abort('%s: unknown repository type' % path)
@@ -350,37 +355,14 @@
dest = hg.defaultdest(src) + "-hg"
ui.status("assuming destination %s\n" % dest)
- # Try to be smart and initalize things when required
- created = False
- if os.path.isdir(dest):
- if len(os.listdir(dest)) > 0:
- try:
- hg.repository(ui, dest)
- ui.status("destination %s is a Mercurial repository\n" % dest)
- except hg.RepoError:
- raise util.Abort(
- "destination directory %s is not empty.\n"
- "Please specify an empty directory to be initialized\n"
- "or an already initialized mercurial repository"
- % dest)
- else:
- ui.status("initializing destination %s repository\n" % dest)
- hg.repository(ui, dest, create=True)
- created = True
- elif os.path.exists(dest):
- raise util.Abort("destination %s exists and is not a directory" % dest)
- else:
- ui.status("initializing destination %s repository\n" % dest)
- hg.repository(ui, dest, create=True)
- created = True
-
- destc = convertsink(ui, dest)
+ destc = convertsink(ui, dest, opts.get('dest_type'))
try:
- srcc = convertsource(ui, src, rev=opts.get('rev'))
+ srcc = convertsource(ui, src, opts.get('source_type'),
+ opts.get('rev'))
except Exception:
- if created:
- shutil.rmtree(dest, True)
+ for path in destc.created:
+ shutil.rmtree(path, True)
raise
fmap = opts.get('filemap')
@@ -402,8 +384,10 @@
"convert":
(convert,
[('A', 'authors', '', 'username mapping filename'),
+ ('d', 'dest-type', '', 'destination repository type'),
('', 'filemap', '', 'remap file names using contents of file'),
('r', 'rev', '', 'import up to target revision REV'),
+ ('s', 'source-type', '', 'source repository type'),
('', 'datesort', None, 'try to sort changesets by date')],
'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
"debugsvnlog":
--- a/hgext/convert/common.py Tue Oct 09 17:44:44 2007 -0500
+++ b/hgext/convert/common.py Thu Oct 11 00:46:40 2007 -0500
@@ -20,13 +20,15 @@
SKIPREV = 'SKIP'
class commit(object):
- def __init__(self, author, date, desc, parents, branch=None, rev=None):
+ def __init__(self, author, date, desc, parents, branch=None, rev=None,
+ extra={}):
self.author = author
self.date = date
self.desc = desc
self.parents = parents
self.branch = branch
self.rev = rev
+ self.extra = extra
class converter_source(object):
"""Conversion source interface"""
@@ -114,8 +116,13 @@
def __init__(self, ui, path):
"""Initialize conversion sink (or raise NoRepo("message")
- exception if path is not a valid repository)"""
- raise NotImplementedError()
+ exception if path is not a valid repository)
+
+ created is a list of paths to remove if a fatal error occurs
+ later"""
+ self.ui = ui
+ self.path = path
+ self.created = []
def getheads(self):
"""Return a list of this repository's heads"""
--- a/hgext/convert/hg.py Tue Oct 09 17:44:44 2007 -0500
+++ b/hgext/convert/hg.py Thu Oct 11 00:46:40 2007 -0500
@@ -16,16 +16,27 @@
class mercurial_sink(converter_sink):
def __init__(self, ui, path):
- self.path = path
- self.ui = ui
+ converter_sink.__init__(self, ui, path)
self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
self.lastbranch = None
- try:
- self.repo = hg.repository(self.ui, path)
- except:
- raise NoRepo("could not open hg repo %s as sink" % path)
+ if os.path.isdir(path) and len(os.listdir(path)) > 0:
+ try:
+ self.repo = hg.repository(self.ui, path)
+ ui.status(_('destination %s is a Mercurial repository\n') %
+ path)
+ except hg.RepoError, err:
+ ui.print_exc()
+ raise NoRepo(err.args[0])
+ else:
+ try:
+ ui.status(_('initializing destination %s repository\n') % path)
+ self.repo = hg.repository(self.ui, path, create=True)
+ self.created.append(path)
+ except hg.RepoError, err:
+ ui.print_exc()
+ raise NoRepo("could not create hg repo %s as sink" % path)
self.lock = None
self.wlock = None
self.filemapmode = False
@@ -108,7 +119,7 @@
p2 = parents.pop(0)
text = commit.desc
- extra = {}
+ extra = commit.extra.copy()
if self.branchnames and commit.branch:
extra['branch'] = commit.branch
if commit.rev:
@@ -174,7 +185,11 @@
converter_source.__init__(self, ui, path, rev)
try:
self.repo = hg.repository(self.ui, path)
- except:
+ # try to provoke an exception if this isn't really a hg
+ # repo, but some other bogus compatible-looking url
+ self.repo.heads()
+ except hg.RepoError:
+ ui.print_exc()
raise NoRepo("could not open hg repo %s as source" % path)
self.lastrev = None
self.lastctx = None
@@ -226,7 +241,7 @@
parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
return commit(author=ctx.user(), date=util.datestr(ctx.date()),
desc=ctx.description(), parents=parents,
- branch=ctx.branch())
+ branch=ctx.branch(), extra=ctx.extra())
def gettags(self):
tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
--- a/hgext/convert/subversion.py Tue Oct 09 17:44:44 2007 -0500
+++ b/hgext/convert/subversion.py Thu Oct 11 00:46:40 2007 -0500
@@ -93,9 +93,9 @@
get_log_child(sys.stdout, *args)
# SVN conversion code stolen from bzr-svn and tailor
-class convert_svn(converter_source):
+class svn_source(converter_source):
def __init__(self, ui, url, rev=None):
- super(convert_svn, self).__init__(ui, url, rev=rev)
+ super(svn_source, self).__init__(ui, url, rev=rev)
try:
SubversionException
@@ -128,6 +128,7 @@
self.paths = {}
self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
except SubversionException, e:
+ ui.print_exc()
raise NoRepo("couldn't open SVN repo %s" % self.url)
if rev:
--- a/mercurial/context.py Tue Oct 09 17:44:44 2007 -0500
+++ b/mercurial/context.py Thu Oct 11 00:46:40 2007 -0500
@@ -82,6 +82,7 @@
def files(self): return self._changeset[3]
def description(self): return self._changeset[4]
def branch(self): return self._changeset[5].get("branch")
+ def extra(self): return self._changeset[5]
def tags(self): return self._repo.nodetags(self._node)
def parents(self):
--- a/tests/test-convert Tue Oct 09 17:44:44 2007 -0500
+++ b/tests/test-convert Thu Oct 11 00:46:40 2007 -0500
@@ -3,6 +3,8 @@
echo "[extensions]" >> $HGRCPATH
echo "convert=" >> $HGRCPATH
+hg help convert
+
hg init a
cd a
echo a > a
@@ -19,3 +21,17 @@
cd ..
hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
hg --cwd a-hg pull ../a
+
+touch bogusfile
+echo % should fail
+hg convert a bogusfile
+
+mkdir bogusdir
+chmod 000 bogusdir
+
+echo % should fail
+hg convert a bogusdir
+
+echo % should succeed
+chmod 700 bogusdir
+hg convert a bogusdir
--- a/tests/test-convert-darcs Tue Oct 09 17:44:44 2007 -0500
+++ b/tests/test-convert-darcs Thu Oct 11 00:46:40 2007 -0500
@@ -7,6 +7,7 @@
echo 'hgext.graphlog =' >> $HGRCPATH
DARCS_EMAIL='test@example.org'; export DARCS_EMAIL
+HOME=do_not_use_HOME_darcs; export HOME
echo % initialize darcs repo
mkdir darcs-repo
--- a/tests/test-convert.out Tue Oct 09 17:44:44 2007 -0500
+++ b/tests/test-convert.out Thu Oct 11 00:46:40 2007 -0500
@@ -1,3 +1,67 @@
+hg convert [OPTION]... SOURCE [DEST [MAPFILE]]
+
+Convert a foreign SCM repository to a Mercurial one.
+
+ Accepted source formats:
+ - CVS
+ - Darcs
+ - git
+ - Subversion
+
+ Accepted destination formats:
+ - Mercurial
+
+ If no revision is given, all revisions will be converted. Otherwise,
+ convert will only import up to the named revision (given in a format
+ understood by the source).
+
+ If no destination directory name is specified, it defaults to the
+ basename of the source with '-hg' appended. If the destination
+ repository doesn't exist, it will be created.
+
+ If <revmapfile> isn't given, it will be put in a default location
+ (<dest>/.hg/shamap by default). The <revmapfile> is a simple text
+ file that maps each source commit ID to the destination ID for
+ that revision, like so:
+ <source ID> <destination ID>
+
+ If the file doesn't exist, it's automatically created. It's updated
+ on each commit copied, so convert-repo can be interrupted and can
+ be run repeatedly to copy new commits.
+
+ The [username mapping] file is a simple text file that maps each source
+ commit author to a destination commit author. It is handy for source SCMs
+ that use unix logins to identify authors (eg: CVS). One line per author
+ mapping and the line format is:
+ srcauthor=whatever string you want
+
+ The filemap is a file that allows filtering and remapping of files
+ and directories. Comment lines start with '#'. Each line can
+ contain one of the following directives:
+
+ include path/to/file
+
+ exclude path/to/file
+
+ rename from/file to/file
+
+ The 'include' directive causes a file, or all files under a
+ directory, to be included in the destination repository. The
+ 'exclude' directive causes files or directories to be omitted.
+ The 'rename' directive renames a file or directory. To rename
+ from a subdirectory into the root of the repository, use '.' as
+ the path to rename to.
+
+options:
+
+ -A --authors username mapping filename
+ -d --dest-type destination repository type
+ --filemap remap file names using contents of file
+ -r --rev import up to target revision REV
+ -s --source-type source repository type
+ --datesort try to sort changesets by date
+
+use "hg -v help convert" to show global options
adding a
assuming destination a-hg
initializing destination a-hg repository
@@ -12,3 +76,18 @@
pulling from ../a
searching for changes
no changes found
+% should fail
+initializing destination bogusfile repository
+abort: cannot create new bundle repository
+% should fail
+abort: Permission denied: bogusdir
+% should succeed
+initializing destination bogusdir repository
+scanning source...
+sorting...
+converting...
+4 a
+3 b
+2 c
+1 d
+0 e