Release 0.4.13.
[jelmer/subvertpy.git] / __init__.py
index ec244b2a9cdb1bb3f771603aec9902c031aaf334..74d756a1d2081a24e72b6fddc3793438e4061e73 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-"""
-Support for Subversion branches
+"""Support for Subversion branches
+
+Bazaar can be used with Subversion branches through the bzr-svn plugin.
+
+Most Bazaar commands should work fine with Subversion branches. To 
+create new branches in Subversion using push, it is currently necessary
+to use the svn-push command rather than the standard push command.
+
+bzr-svn also adds two new commands to Bazaar:
+
+ - bzr svn-import
+ - bzr dpush
+
+For more information about bzr-svn, see the bzr-svn FAQ.
+
 """
 import bzrlib
+from bzrlib import log
 from bzrlib.bzrdir import BzrDirFormat, format_registry
 from bzrlib.errors import BzrError
 from bzrlib.commands import Command, register_command, display_command, Option
@@ -26,13 +40,12 @@ from bzrlib.revisionspec import SPEC_TYPES
 from bzrlib.trace import warning, mutter
 from bzrlib.transport import register_lazy_transport, register_transport_proto
 
-from bzrlib.plugins.svn import format
-from bzrlib.plugins.svn import revspec
+import os
 
 # versions ending in 'exp' mean experimental mappings
 # versions ending in 'dev' mean development version
 # versions ending in 'final' mean release (well tested, etc)
-version_info = (0, 4, 11, 'exp', 0)
+version_info = (0, 4, 13, 'final', 0)
 
 if version_info[3] == 'final':
     version_string = '%d.%d.%d' % version_info[:3]
@@ -40,7 +53,7 @@ else:
     version_string = '%d.%d.%d%s%d' % version_info
 __version__ = version_string
 
-COMPATIBLE_BZR_VERSIONS = [(1, 6)]
+COMPATIBLE_BZR_VERSIONS = [(1, 6), (1, 7)]
 
 def check_bzrlib_version(desired):
     """Check that bzrlib is compatible.
@@ -49,11 +62,10 @@ def check_bzrlib_version(desired):
     If version is compatible version + 1, assume compatible, with deprecations
     Otherwise, assume incompatible.
     """
-    import bzrlib
     bzrlib_version = bzrlib.version_info[:2]
     if (bzrlib_version in desired or 
         ((bzrlib_version[0], bzrlib_version[1]-1) in desired and 
-         bzrlib.version_info[3] == 'dev')):
+         bzrlib.version_info[3] in ('dev', 'exp'))):
         return
     if bzrlib_version < desired[0]:
         raise BzrError('Installed bzr version %s is too old to be used with bzr-svn, at least %s.%s required' % (bzrlib.__version__, desired[0][0], desired[0][1]))
@@ -68,14 +80,29 @@ def check_subversion_version():
     """Check that Subversion is compatible.
 
     """
+    def check_mtime(m):
+        """Check whether a C extension is out of date."""
+        (base, _) = os.path.splitext(m.__file__)
+        c_file = "%s.c" % base
+        if not os.path.exists(c_file):
+            return True
+        if os.path.getmtime(m.__file__) < os.path.getmtime(c_file):
+            return False
+        return True
     try:
-        from bzrlib.plugins.svn.ra import version
-    except:
+        from bzrlib.plugins.svn import client, ra, repos, wc
+        for x in client, ra, repos, wc:
+            if not check_mtime(x):
+                warning("bzr-svn extensions are outdated and need to be rebuilt")
+                break
+    except ImportError:
         warning("Unable to load bzr-svn extensions - did you build it?")
-        raise BzrError('missing dependency')
-    ra_version = version()
-    if (ra_version[0] >= 5 and getattr(ra, 'SVN_REVISION', None) and 27729 <= ra.SVN_REVISION < 31470):
-        warning('Installed Subversion has buggy svn.ra.get_log() implementation, please install newer.')
+        raise
+    ra_version = ra.version()
+    if (ra_version[0] >= 5 and getattr(ra, 'SVN_REVISION', None) and 
+        27729 <= ra.SVN_REVISION < 31470):
+        warning('Installed Subversion has buggy svn.ra.get_log() '
+                'implementation, please install newer.')
 
     mutter("bzr-svn: using Subversion %d.%d.%d (%s)" % ra_version)
 
@@ -100,6 +127,8 @@ def check_rebase_version(min_version):
 
 check_subversion_version()
 
+from bzrlib.plugins.svn import format, revspec
+
 register_transport_proto('svn+ssh://', 
     help="Access using the Subversion smart server tunneled over SSH.")
 register_transport_proto('svn+file://', 
@@ -128,29 +157,33 @@ format_registry.register("subversion-wc", format.SvnWorkingTreeDirFormat,
                          native=False, hidden=True)
 SPEC_TYPES.append(revspec.RevisionSpec_svn)
 
-versions_checked = False
+log.properties_handler_registry.register_lazy("subversion",
+                                              "bzrlib.plugins.svn.log",
+                                              "show_subversion_properties")
+
+_versions_checked = False
 def lazy_check_versions():
     """Check whether all dependencies have the right versions.
     
     :note: Only checks once, caches the result."""
-    global versions_checked
-    if versions_checked:
+    global _versions_checked
+    if _versions_checked:
         return
-    versions_checked = True
+    _versions_checked = True
     check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
 
-optimizers_registered = False
+_optimizers_registered = False
 def lazy_register_optimizers():
     """Register optimizers for fetching between Subversion and Bazaar 
     repositories.
     
     :note: Only registers on the first call."""
-    global optimizers_registered
-    if optimizers_registered:
+    global _optimizers_registered
+    if _optimizers_registered:
         return
     from bzrlib.repository import InterRepository
     from bzrlib.plugins.svn import commit, fetch
-    optimizers_registered = True
+    _optimizers_registered = True
     InterRepository.register_optimiser(fetch.InterFromSvnRepository)
     InterRepository.register_optimiser(commit.InterToSvnRepository)
 
@@ -174,6 +207,9 @@ def get_scheme(schemename):
 class cmd_svn_import(Command):
     """Convert a Subversion repository to a Bazaar repository.
     
+    To save disk space, only branches will be created by default 
+    (no working trees). To create a tree for a branch, run "bzr co" in 
+    it.
     """
     takes_args = ['from_location', 'to_location?']
     takes_options = [Option('trees', help='Create working trees.'),
@@ -184,6 +220,10 @@ class cmd_svn_import(Command):
                      Option('scheme', type=get_scheme,
                          help='Branching scheme (none, trunk, etc). '
                               'Default: auto.'),
+                     Option('keep', 
+                         help="Don't delete branches removed in Subversion."),
+                     Option('incremental',
+                         help="Import revisions incrementally."),
                      Option('prefix', type=str, 
                          help='Only consider branches of which path starts '
                               'with prefix.')
@@ -191,14 +231,14 @@ class cmd_svn_import(Command):
 
     @display_command
     def run(self, from_location, to_location=None, trees=False, 
-            standalone=False, scheme=None, all=False, prefix=None):
-        from bzrlib.branch import Branch
+            standalone=False, scheme=None, all=False, prefix=None, keep=False,
+            incremental=False):
         from bzrlib.bzrdir import BzrDir
-        from bzrlib.errors import BzrCommandError, NoRepositoryPresent, NotBranchError
+        from bzrlib.errors import BzrCommandError, NoRepositoryPresent
         from bzrlib import urlutils
         from bzrlib.plugins.svn.convert import convert_repository
+        from bzrlib.plugins.svn.mapping3 import repository_guess_scheme
         from bzrlib.plugins.svn.repository import SvnRepository
-        import os
 
         if to_location is None:
             to_location = os.path.basename(from_location.rstrip("/\\"))
@@ -221,36 +261,48 @@ class cmd_svn_import(Command):
         try:
             from_repos = from_dir.open_repository()
         except NoRepositoryPresent, e:
-            try:
-                Branch.open(from_location)
-                raise BzrCommandError("No Repository found at %s. "
-                    "For individual branches, use 'bzr branch'." % from_location)
-            except NotBranchError:
-                if prefix is not None:
-                    raise BzrCommandError("Path inside repository specified and --prefix specified")
-                from_repos = from_dir.find_repository()
-                prefix = urlutils.relative_url(from_repos.base, from_location)
-                self.outf.write("Importing branches below %s\n" % 
-                        urlutils.unescape_for_display(prefix, self.outf.encoding))
-
-        if prefix is not None:
-            prefix = prefix.strip("/") + "/"
-
-        if not isinstance(from_repos, SvnRepository):
-            raise BzrCommandError(
-                    "Not a Subversion repository: %s" % from_location)
-
-        def filter_branch(branch):
-            if prefix is not None and not branch.get_branch_path().startswith(prefix):
-                return False
-            return True
+            if prefix is not None:
+                raise BzrCommandError("Path inside repository specified "
+                                      "and --prefix specified")
+            from_repos = from_dir.find_repository()
+            prefix = urlutils.relative_url(from_repos.base, from_location)
+            prefix = prefix.encode("utf-8")
+
+        from_repos.lock_read()
+        try:
+            (guessed_scheme, scheme) = repository_guess_scheme(from_repos, 
+                from_repos.get_latest_revnum())
+
+            if prefix is not None:
+                prefix = prefix.strip("/") + "/"
+                if guessed_scheme.is_branch(prefix):
+                    raise BzrCommandError("%s appears to contain a branch. " 
+                            "For individual branches, use 'bzr branch'." % 
+                            from_location)
+
+                self.outf.write("Importing branches with prefix /%s\n" % 
+                    urlutils.unescape_for_display(prefix, self.outf.encoding))
 
-        convert_repository(from_repos, to_location, scheme, None, 
-                           not standalone, trees, all, filter_branch=filter_branch)
+            if not isinstance(from_repos, SvnRepository):
+                raise BzrCommandError(
+                        "Not a Subversion repository: %s" % from_location)
+
+            def filter_branch(branch):
+                if (prefix is not None and 
+                    not branch.get_branch_path().startswith(prefix)):
+                    return False
+                return True
+
+            convert_repository(from_repos, to_location, scheme, None, 
+                               not standalone, trees, all, 
+                               filter_branch=filter_branch,
+                               keep=keep, incremental=incremental)
 
-        if tmp_repos is not None:
-            from bzrlib import osutils
-            osutils.rmtree(tmp_repos)
+            if tmp_repos is not None:
+                from bzrlib import osutils
+                osutils.rmtree(tmp_repos)
+        finally:
+            from_repos.unlock()
 
 
 register_command(cmd_svn_import)
@@ -266,7 +318,8 @@ class cmd_svn_upgrade(Command):
 
     @display_command
     def run(self, from_repository=None, verbose=False):
-        from bzrlib.plugins.svn.upgrade import upgrade_branch, upgrade_workingtree
+        from bzrlib.plugins.svn.upgrade import (upgrade_branch, 
+                                                upgrade_workingtree)
         from bzrlib.branch import Branch
         from bzrlib.errors import NoWorkingTree, BzrCommandError
         from bzrlib.repository import Repository
@@ -323,10 +376,11 @@ class cmd_svn_push(Command):
                  'rather than the one containing the working directory.',
             short_name='d',
             type=unicode,
-            )]
+            ),
+            Option("merged", help="Push merged (right hand side) revisions.")]
 
     def run(self, location=None, revision=None, remember=False, 
-            directory=None):
+            directory=None, merged=None):
         from bzrlib.bzrdir import BzrDir
         from bzrlib.branch import Branch
         from bzrlib.errors import NotBranchError, BzrCommandError
@@ -345,36 +399,40 @@ class cmd_svn_push(Command):
                 self.outf.write("Using saved location: %s\n" % display_url)
                 location = stored_loc
 
-        bzrdir = BzrDir.open(location)
-        if revision is not None:
-            if len(revision) > 1:
-                raise BzrCommandError(
-                    'bzr svn-push --revision takes exactly one revision' 
-                    ' identifier')
-            revision_id = revision[0].as_revision_id(source_branch)
-        else:
-            revision_id = None
+        source_branch.lock_read()
         try:
-            target_branch = bzrdir.open_branch()
-            target_branch.lock_write()
+            bzrdir = BzrDir.open(location)
+            if revision is not None:
+                if len(revision) > 1:
+                    raise BzrCommandError(
+                        'bzr svn-push --revision takes exactly one revision' 
+                        ' identifier')
+                revision_id = revision[0].as_revision_id(source_branch)
+            else:
+                revision_id = None
             try:
-                target_branch.pull(source_branch, revision_id)
-            finally:
-                target_branch.unlock()
-        except NotBranchError:
-            target_branch = bzrdir.import_branch(source_branch, revision_id)
+                target_branch = bzrdir.open_branch()
+                target_branch.lock_write()
+                try:
+                    target_branch.pull(source_branch, stop_revision=revision_id, _push_merged=merged)
+                finally:
+                    target_branch.unlock()
+            except NotBranchError:
+                target_branch = bzrdir.import_branch(source_branch, revision_id, _push_merged=merged)
+        finally:
+            source_branch.unlock()
         # We successfully created the target, remember it
         if source_branch.get_push_location() is None or remember:
             source_branch.set_push_location(target_branch.base)
 
 register_command(cmd_svn_push)
 
-class cmd_svn_dpush(Command):
-    """Push revisions to Subversion without setting any magic Bazaar-specific 
-    properties.
+class cmd_dpush(Command):
+    """Push diffs into Subversion without any Bazaar-specific properties set.
 
     This will afterwards rebase the local Bazaar branch on the Subversion 
-    branch. 
+    branch unless the --no-rebase option is used, in which case 
+    the two branches will be out of sync. 
     """
     takes_args = ['location?']
     takes_options = ['remember', Option('directory',
@@ -385,13 +443,15 @@ class cmd_svn_dpush(Command):
             ),
             Option('no-rebase', help="Don't rebase after push")]
 
-    def run(self, location=None, remember=False, 
-            directory=None, no_rebase=False):
+    def run(self, location=None, remember=False, directory=None, 
+            no_rebase=False):
+        from bzrlib import urlutils
         from bzrlib.bzrdir import BzrDir
         from bzrlib.branch import Branch
-        from bzrlib.errors import NotBranchError, BzrCommandError, NoWorkingTree
-        from bzrlib.commit import dpush
-        from bzrlib import urlutils
+        from bzrlib.errors import BzrCommandError, NoWorkingTree
+        from bzrlib.workingtree import WorkingTree
+
+        from bzrlib.plugins.svn.commit import dpush
 
         if directory is None:
             directory = "."
@@ -412,14 +472,6 @@ class cmd_svn_dpush(Command):
                 location = stored_loc
 
         bzrdir = BzrDir.open(location)
-        if revision is not None:
-            if len(revision) > 1:
-                raise BzrCommandError(
-                    'bzr svn-dpush --revision takes exactly one revision' 
-                    ' identifier')
-            revision_id = revision[0].as_revision_id(source_branch)
-        else:
-            revision_id = None
         target_branch = bzrdir.open_branch()
         target_branch.lock_write()
         revid_map = dpush(target_branch, source_branch)
@@ -427,13 +479,17 @@ class cmd_svn_dpush(Command):
         if source_branch.get_push_location() is None or remember:
             source_branch.set_push_location(target_branch.base)
         if not no_rebase:
-            new_last_revid = revid_map[source_branch.last_revision()]
-            source_branch.set_last_revision(new_last_revid)
+            _, old_last_revid = source_branch.last_revision_info()
+            new_last_revid = revid_map[old_last_revid]
             if source_wt is not None:
-                source_wt.set_last_revision(new_last_revid)
+                source_wt.pull(target_branch, overwrite=True, 
+                               stop_revision=new_last_revid)
+            else:
+                source_branch.pull(target_branch, overwrite=True, 
+                                   stop_revision=new_last_revid)
 
 
-register_command(cmd_svn_dpush)
+register_command(cmd_dpush)
 
 
 class cmd_svn_branching_scheme(Command):
@@ -452,11 +508,11 @@ class cmd_svn_branching_scheme(Command):
         from bzrlib.bzrdir import BzrDir
         from bzrlib.errors import BzrCommandError
         from bzrlib.msgeditor import edit_commit_message
-        from bzrlib.repository import Repository
         from bzrlib.trace import info
         from bzrlib.plugins.svn.repository import SvnRepository
         from bzrlib.plugins.svn.mapping3.scheme import scheme_from_branch_list
-        from bzrlib.plugins.svn.mapping3 import config_set_scheme, get_property_scheme, set_property_scheme
+        from bzrlib.plugins.svn.mapping3 import (config_set_scheme, 
+            get_property_scheme, set_property_scheme)
         def scheme_str(scheme):
             if scheme is None:
                 return ""
@@ -477,7 +533,7 @@ class cmd_svn_branching_scheme(Command):
             if repository_wide:
                 set_property_scheme(repos, scheme)
             else:
-                config_set_scheme(repos, scheme, mandatory=True)
+                config_set_scheme(repos, scheme, None, mandatory=True)
         elif scheme is not None:
             info(scheme_str(scheme))