changeset 5257:a4547dfbbf20

Merge with crew
author Brendan Cully <brendan@kublai.com>
date Mon, 27 Aug 2007 15:41:31 -0700
parents be4835ad9a85 (current diff) 65dc707606ed (diff)
children a35756389ef4
files
diffstat 10 files changed, 160 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- 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'
--- 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'])
--- 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)
--- 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):
--- 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))
--- 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)
--- 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"),
 }
 
--- 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()
 
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
+
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