# HG changeset patch # User Brendan Cully # Date 1188254491 25200 # Node ID a4547dfbbf206c68a93d40a9788ed5d47d002e7a # Parent be4835ad9a85a3babe444bc5c192bae6b605b013# Parent 65dc707606ed5f4e5882f1a06e11a03f13f0220d Merge with crew diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py --- a/hgext/convert/__init__.py +++ b/hgext/convert/__init__.py @@ -397,6 +397,23 @@ def _convert(ui, src, dest=None, revmapf 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. """ util._encoding = 'UTF-8' diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1645,11 +1645,7 @@ def incoming(ui, repo, source="default", other = hg.repository(ui, source) ui.status(_('comparing with %s\n') % source) if revs: - if 'lookup' in other.capabilities: - revs = [other.lookup(rev) for rev in revs] - else: - error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.") - raise util.Abort(error) + revs = [other.lookup(rev) for rev in revs] incoming = repo.findincoming(other, heads=revs, force=opts["force"]) if not incoming: try: @@ -1667,8 +1663,6 @@ def incoming(ui, repo, source="default", if revs is None: cg = other.changegroup(incoming, "incoming") else: - if 'changegroupsubset' not in other.capabilities: - raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset.")) cg = other.changegroupsubset(incoming, revs, 'incoming') bundletype = other.local() and "HG10BZ" or "HG10UN" fname = cleanup = changegroup.writebundle(cg, fname, bundletype) @@ -2078,10 +2072,11 @@ def pull(ui, repo, source="default", **o other = hg.repository(ui, source) ui.status(_('pulling from %s\n') % (source)) if revs: - if 'lookup' in other.capabilities: + try: revs = [other.lookup(rev) for rev in revs] - else: - error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.") + except repo.NoCapability: + error = _("Other repository doesn't support revision lookup, " + "so a rev cannot be specified.") raise util.Abort(error) modheads = repo.pull(other, heads=revs, force=opts['force']) diff --git a/mercurial/httprepo.py b/mercurial/httprepo.py --- a/mercurial/httprepo.py +++ b/mercurial/httprepo.py @@ -276,9 +276,9 @@ class httprepository(remoterepository): def get_caps(self): if self.caps is None: try: - self.caps = self.do_read('capabilities').split() + self.caps = util.set(self.do_read('capabilities').split()) except repo.RepoError: - self.caps = () + self.caps = util.set() self.ui.debug(_('capabilities: %s\n') % (' '.join(self.caps or ['none']))) return self.caps @@ -354,6 +354,7 @@ class httprepository(remoterepository): fp.close() def lookup(self, key): + self.requirecap('lookup', _('look up remote revision')) d = self.do_cmd("lookup", key = key).read() success, data = d[:-1].split(' ', 1) if int(success): @@ -391,6 +392,7 @@ class httprepository(remoterepository): return util.chunkbuffer(zgenerator(f)) def changegroupsubset(self, bases, heads, source): + self.requirecap('changegroupsubset', _('look up remote changes')) baselst = " ".join([hex(n) for n in bases]) headlst = " ".join([hex(n) for n in heads]) f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -13,7 +13,7 @@ import re, lock, transaction, tempfile, import os, revlog, time, util, extensions, hook class localrepository(repo.repository): - capabilities = ('lookup', 'changegroupsubset') + capabilities = util.set(('lookup', 'changegroupsubset')) supported = ('revlogv1', 'store') def __init__(self, parentui, path=None, create=0): diff --git a/mercurial/repo.py b/mercurial/repo.py --- a/mercurial/repo.py +++ b/mercurial/repo.py @@ -9,16 +9,26 @@ class RepoError(Exception): pass +class NoCapability(RepoError): + pass + class repository(object): def capable(self, name): '''tell whether repo supports named capability. return False if not supported. if boolean capability, return True. if string capability, return string.''' + if name in self.capabilities: + return True name_eq = name + '=' for cap in self.capabilities: - if name == cap: - return True if cap.startswith(name_eq): return cap[len(name_eq):] return False + + def requirecap(self, name, purpose): + '''raise an exception if the given capability is not present''' + if not self.capable(name): + raise NoCapability(_('cannot %s; remote repository does not ' + 'support the %r capability') % + (purpose, name)) diff --git a/mercurial/sshrepo.py b/mercurial/sshrepo.py --- a/mercurial/sshrepo.py +++ b/mercurial/sshrepo.py @@ -71,11 +71,11 @@ class sshrepository(remoterepository): else: self.raise_(repo.RepoError(_("no suitable response from remote hg"))) - self.capabilities = () + self.capabilities = util.set() lines.reverse() for l in lines: if l.startswith("capabilities:"): - self.capabilities = l[:-1].split(":")[1].split() + self.capabilities.update(l[:-1].split(":")[1].split()) break def readerr(self): @@ -131,6 +131,7 @@ class sshrepository(remoterepository): self.call("unlock") def lookup(self, key): + self.requirecap('lookup', _('look up remote revision')) d = self.call("lookup", key=key) success, data = d[:-1].split(" ", 1) if int(success): @@ -168,6 +169,7 @@ class sshrepository(remoterepository): return self.do_cmd("changegroup", roots=n) def changegroupsubset(self, bases, heads, kind): + self.requirecap('changegroupsubset', _('look up remote changes')) bases = " ".join(map(hex, bases)) heads = " ".join(map(hex, heads)) return self.do_cmd("changegroupsubset", bases=bases, heads=heads) diff --git a/tests/hghave b/tests/hghave --- a/tests/hghave +++ b/tests/hghave @@ -5,11 +5,22 @@ prefixed with "no-", the absence of feat """ import optparse import os +import re import sys import tempfile tempprefix = 'hg-hghave-' +def matchoutput(cmd, regexp): + """Return True if cmd executes successfully and its output + is matched by the supplied regular expression. + """ + r = re.compile(regexp) + fh = os.popen(cmd) + s = fh.read() + ret = fh.close() + return ret is None and r.search(s) + def has_symlink(): return hasattr(os, "symlink") @@ -52,10 +63,18 @@ def has_lsprof(): return False def has_git(): - fh = os.popen('git --version 2>&1') - s = fh.read() - ret = fh.close() - return ret is None and s.startswith('git version') + return matchoutput('git --version 2>&1', r'^git version') + +def has_svn(): + return matchoutput('svn --version 2>&1', r'^svn, version') and \ + matchoutput('svnadmin --version 2>&1', r'^svnadmin, version') + +def has_svn_bindings(): + try: + import svn.core + return True + except ImportError: + return False checks = { "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"), @@ -64,6 +83,8 @@ checks = { "fifo": (has_fifo, "named pipes"), "hotshot": (has_hotshot, "python hotshot module"), "lsprof": (has_lsprof, "python lsprof module"), + "svn": (has_svn, "subversion client and admin tools"), + "svn-bindings": (has_svn_bindings, "subversion python bindings"), "symlink": (has_symlink, "symbolic links"), } diff --git a/tests/run-tests.py b/tests/run-tests.py --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -152,7 +152,13 @@ def install_hg(): os.chdir(TESTDIR) os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"]) - os.environ["PYTHONPATH"] = PYTHONDIR + + pythonpath = os.environ.get("PYTHONPATH") + if pythonpath: + pythonpath = PYTHONDIR + os.pathsep + pythonpath + else: + pythonpath = PYTHONDIR + os.environ["PYTHONPATH"] = pythonpath use_correct_python() diff --git a/tests/test-convert-svn b/tests/test-convert-svn new file mode 100755 --- /dev/null +++ b/tests/test-convert-svn @@ -0,0 +1,53 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH + +svnadmin create svn-repo + +echo % initial svn import +mkdir t +cd t +echo a > a +cd .. + +svnpath=`pwd | tr '\\' /` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +svnurl=file://$svnpath/svn-repo/trunk +svn import -m init t $svnurl | fix_path + +echo % update svn repository +svn co $svnurl t2 | fix_path +cd t2 +echo b >> a +echo b > b +svn add b +svn ci -m changea +cd .. + +echo % convert to hg once +hg convert $svnurl + +echo % update svn repository again +cd t2 +echo c >> a +echo c >> b +svn ci -m changeb +cd .. + +echo % test incremental conversion +hg convert $svnurl + diff --git a/tests/test-convert-svn.out b/tests/test-convert-svn.out new file mode 100644 --- /dev/null +++ b/tests/test-convert-svn.out @@ -0,0 +1,32 @@ +% initial svn import +Adding t/a + +Committed revision 1. +% update svn repository +A t2/a +Checked out revision 1. +A b +Sending a +Adding b +Transmitting file data .. +Committed revision 2. +% convert to hg once +assuming destination trunk-hg +initializing destination trunk-hg repository +scanning source... +sorting... +converting... +1 init +0 changea +% update svn repository again +Sending a +Sending b +Transmitting file data .. +Committed revision 3. +% test incremental conversion +assuming destination trunk-hg +destination trunk-hg is a Mercurial repository +scanning source... +sorting... +converting... +0 changeb