Rev 3226: Create basic stackable branch facility. in http://people.ubuntu.com/~robertc/baz2.0/shallow-branch

Robert Collins robertc at robertcollins.net
Thu Feb 14 00:40:37 GMT 2008


At http://people.ubuntu.com/~robertc/baz2.0/shallow-branch

------------------------------------------------------------
revno: 3226
revision-id:robertc at robertcollins.net-20080214004032-9vms1rfzv6tabsih
parent: robertc at robertcollins.net-20080213224222-mu1sxr294xoutwdt
committer: Robert Collins <robertc at robertcollins.net>
branch nick: StackableBranch
timestamp: Thu 2008-02-14 11:40:32 +1100
message:
  Create basic stackable branch facility.
modified:
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
  bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/tests/branch_implementations/__init__.py __init__.py-20060123013057-b12a52c3f361daf4
  bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
  bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2008-02-06 04:06:42 +0000
+++ b/bzrlib/branch.py	2008-02-14 00:40:32 +0000
@@ -350,6 +350,15 @@
         """
         raise NotImplementedError(self.get_root_id)
 
+    def get_stacked_on(self):
+        """Get the URL this branch is stacked against.
+
+        :raises NotStacked: If the branch is not stacked.
+        :raises UnstackableBranchFormat: If the branch does not support
+            stacking.
+        """
+        raise NotImplementedError(self.get_stacked_on)
+
     def print_file(self, file, revision_id):
         """Print `file` to stdout."""
         raise NotImplementedError(self.print_file)
@@ -357,6 +366,16 @@
     def set_revision_history(self, rev_history):
         raise NotImplementedError(self.set_revision_history)
 
+    def set_stacked_on(self, url):
+        """set the URL this branch is stacked against.
+
+        :raises UnstackableBranchFormat: If the branch does not support
+            stacking.
+        :raises UnstackableRepositoryFormat: If the repository does not support
+            stacking.
+        """
+        raise NotImplementedError(self.set_stacked_on)
+
     def _cache_revision_history(self, rev_history):
         """Set the cached revision history to rev_history.
 
@@ -1087,7 +1106,42 @@
         return "Bazaar-NG branch format 4"
 
 
-class BzrBranchFormat5(BranchFormat):
+class BranchFormatMetadir(BranchFormat):
+    """Common logic for meta-dir based branch formats."""
+
+    def _branch_class(self):
+        """What class to instantiate on open calls."""
+        raise NotImplementedError(self._branch_class)
+
+    def open(self, a_bzrdir, _found=False):
+        """Return the branch object for a_bzrdir
+
+        _found is a private parameter, do not use it. It is used to indicate
+               if format probing has already be done.
+        """
+        if not _found:
+            format = BranchFormat.find_format(a_bzrdir)
+            assert format.__class__ == self.__class__
+        try:
+            transport = a_bzrdir.get_branch_transport(None)
+            control_files = lockable_files.LockableFiles(transport, 'lock',
+                                                         lockdir.LockDir)
+            return self._branch_class()(_format=self,
+                              _control_files=control_files,
+                              a_bzrdir=a_bzrdir,
+                              _repository=a_bzrdir.find_repository())
+        except NoSuchFile:
+            raise NotBranchError(path=transport.base)
+
+    def __init__(self):
+        super(BranchFormatMetadir, self).__init__()
+        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
+
+    def supports_tags(self):
+        return True
+
+
+class BzrBranchFormat5(BranchFormatMetadir):
     """Bzr branch format 5.
 
     This format has:
@@ -1100,6 +1154,9 @@
     This format is new in bzr 0.8.
     """
 
+    def _branch_class(self):
+        return BzrBranch5
+
     def get_format_string(self):
         """See BranchFormat.get_format_string()."""
         return "Bazaar-NG branch format 5\n"
@@ -1115,32 +1172,11 @@
                       ]
         return self._initialize_helper(a_bzrdir, utf8_files)
 
-    def __init__(self):
-        super(BzrBranchFormat5, self).__init__()
-        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
-
-    def open(self, a_bzrdir, _found=False):
-        """Return the branch object for a_bzrdir
-
-        _found is a private parameter, do not use it. It is used to indicate
-               if format probing has already be done.
-        """
-        if not _found:
-            format = BranchFormat.find_format(a_bzrdir)
-            assert format.__class__ == self.__class__
-        try:
-            transport = a_bzrdir.get_branch_transport(None)
-            control_files = lockable_files.LockableFiles(transport, 'lock',
-                                                         lockdir.LockDir)
-            return BzrBranch5(_format=self,
-                              _control_files=control_files,
-                              a_bzrdir=a_bzrdir,
-                              _repository=a_bzrdir.find_repository())
-        except NoSuchFile:
-            raise NotBranchError(path=transport.base)
-
-
-class BzrBranchFormat6(BzrBranchFormat5):
+    def supports_tags(self):
+        return False
+
+
+class BzrBranchFormat6(BranchFormatMetadir):
     """Branch format with last-revision and tags.
 
     Unlike previous formats, this has no explicit revision history. Instead,
@@ -1151,6 +1187,9 @@
     and became the default in 0.91.
     """
 
+    def _branch_class(self):
+        return BzrBranch6
+
     def get_format_string(self):
         """See BranchFormat.get_format_string()."""
         return "Bazaar Branch Format 6 (bzr 0.15)\n"
@@ -1167,25 +1206,35 @@
                       ]
         return self._initialize_helper(a_bzrdir, utf8_files)
 
-    def open(self, a_bzrdir, _found=False):
-        """Return the branch object for a_bzrdir
-
-        _found is a private parameter, do not use it. It is used to indicate
-               if format probing has already be done.
-        """
-        if not _found:
-            format = BranchFormat.find_format(a_bzrdir)
-            assert format.__class__ == self.__class__
-        transport = a_bzrdir.get_branch_transport(None)
-        control_files = lockable_files.LockableFiles(transport, 'lock',
-                                                     lockdir.LockDir)
-        return BzrBranch6(_format=self,
-                          _control_files=control_files,
-                          a_bzrdir=a_bzrdir,
-                          _repository=a_bzrdir.find_repository())
-
-    def supports_tags(self):
-        return True
+
+class BzrBranchFormat7(BranchFormatMetadir):
+    """Branch format with last-revision, tags, and a stacked location pointer.
+
+    The stacked location pointer is passed down to the repository and requires
+    a repository format with supports_external_lookups = True.
+
+    This format was introduced in bzr 1.3.
+    """
+
+    def _branch_class(self):
+        return BzrBranch7
+
+    def get_format_string(self):
+        """See BranchFormat.get_format_string()."""
+        return "Bazaar Branch Format 7 (needs bzr 1.3)\n"
+
+    def get_format_description(self):
+        """See BranchFormat.get_format_description()."""
+        return "Branch format 7"
+
+    def initialize(self, a_bzrdir):
+        """Create a branch of this format in a_bzrdir."""
+        utf8_files = [('last-revision', '0 null:\n'),
+                      ('branch.conf', ''),
+                      ('tags', ''),
+                      ('stacked-on', '\n'),
+                      ]
+        return self._initialize_helper(a_bzrdir, utf8_files)
 
 
 class BranchReferenceFormat(BranchFormat):
@@ -1277,9 +1326,11 @@
 # and not independently creatable, so are not registered.
 __format5 = BzrBranchFormat5()
 __format6 = BzrBranchFormat6()
+__format7 = BzrBranchFormat7()
 BranchFormat.register_format(__format5)
 BranchFormat.register_format(BranchReferenceFormat())
 BranchFormat.register_format(__format6)
+BranchFormat.register_format(__format7)
 BranchFormat.set_default_format(__format6)
 _legacy_formats = [BzrBranchFormat4(),
                    ]
@@ -1653,6 +1704,9 @@
         except errors.InvalidURLJoin, e:
             raise errors.InaccessibleParent(parent, self.base)
 
+    def get_stacked_on(self):
+        raise errors.UnstackableBranchFormat(self._format, self.base)
+
     def set_push_location(self, location):
         """See Branch.set_push_location."""
         self.get_config().set_user_option(
@@ -1685,6 +1739,9 @@
             assert isinstance(url, str)
             self.control_files.put_bytes('parent', url + '\n')
 
+    def set_stacked_on(self, url):
+        raise errors.UnstackableBranchFormat(self._format, self.base)
+
 
 class BzrBranch5(BzrBranch):
     """A format 5 branch. This supports new features over plain branches.
@@ -1820,7 +1877,12 @@
         return None
 
 
-class BzrBranch6(BzrBranch5):
+class BzrBranch7(BzrBranch5):
+
+    def _check_stackable_repo(self):
+        if not self.repository._format.supports_external_lookups:
+            raise errors.UnstackableRepositoryFormat(self.repository._format,
+                self.repository.base)
 
     @needs_read_lock
     def last_revision_info(self):
@@ -1930,6 +1992,9 @@
         """See Branch.get_old_bound_location"""
         return self._get_bound_location(False)
 
+    def get_stacked_on(self):
+        self._check_stackable_repo()
+
     def set_append_revisions_only(self, enabled):
         if enabled:
             value = 'True'
@@ -1938,6 +2003,9 @@
         self.get_config().set_user_option('append_revisions_only', value,
             warn_masked=True)
 
+    def set_stacked_on(self, url):
+        self._check_stackable_repo()
+
     def _get_append_revisions_only(self):
         value = self.get_config().get_user_option('append_revisions_only')
         return value == 'True'
@@ -1975,6 +2043,19 @@
         return BasicTags(self)
 
 
+class BzrBranch6(BzrBranch7):
+    """See BzrBranchFormat6 for the capabilities of this branch.
+
+    This subclass of BzrBranch7 disables the new features BzrBranch7 added.
+    """
+
+    def get_stacked_on(self):
+        raise errors.UnstackableBranchFormat(self._format, self.base)
+
+    def set_stacked_on(self, url):
+        raise errors.UnstackableBranchFormat(self._format, self.base)
+
+
 ######################################################################
 # results of operations
 

=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py	2008-02-13 22:11:40 +0000
+++ b/bzrlib/bzrdir.py	2008-02-14 00:40:32 +0000
@@ -2686,27 +2686,27 @@
     )
 # The following two formats should always just be aliases.
 format_registry.register_metadir('development',
-    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
+    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
     help='Current development format. Can convert data to and from pack-0.92 '
         '(and anything compatible with pack-0.92) format repositories. '
-        'Repositories in this format can only be read by bzr.dev. '
+        'Repositories and branches in this format can only be read by bzr.dev. '
         'Please read '
         'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
         'before use.',
-    branch_format='bzrlib.branch.BzrBranchFormat6',
+    branch_format='bzrlib.branch.BzrBranchFormat7',
     tree_format='bzrlib.workingtree.WorkingTreeFormat4',
     experimental=True,
     alias=True,
     )
 format_registry.register_metadir('development-subtree',
-    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
+    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
     help='Current development format, subtree variant. Can convert data to and '
         'from pack-0.92 (and anything compatible with pack-0.92) format '
-        'repositories. Repositories in this format can only be read by '
-        'bzr.dev. Please read '
+        'repositories. Repositories and branches in this format can only be '
+        'read by bzr.dev. Please read '
         'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
         'before use.',
-    branch_format='bzrlib.branch.BzrBranchFormat6',
+    branch_format='bzrlib.branch.BzrBranchFormat7',
     tree_format='bzrlib.workingtree.WorkingTreeFormat4',
     experimental=True,
     alias=True,
@@ -2734,4 +2734,27 @@
     hidden=True,
     experimental=True,
     )
+format_registry.register_metadir('development1',
+    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
+    help='pack-0.92 with a branch that supports stacking. '
+        'Please read '
+        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
+        'before use.',
+    branch_format='bzrlib.branch.BzrBranchFormat7',
+    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
+    hidden=True,
+    experimental=True,
+    )
+format_registry.register_metadir('development1-subtree',
+    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
+    help='pack-0.92-subtree with a branch that supports stacking. '
+        'Please read '
+        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
+        'before use.',
+    branch_format='bzrlib.branch.BzrBranchFormat7',
+    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
+    hidden=True,
+    experimental=True,
+    )
+# The current format that is made on 'bzr init'.
 format_registry.set_default('pack-0.92')

=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py	2008-02-12 02:13:43 +0000
+++ b/bzrlib/errors.py	2008-02-14 00:40:32 +0000
@@ -185,6 +185,13 @@
     _fmt = "The tree builder is already building a tree."
 
 
+class BranchError(BzrError):
+    """Base class for concrete 'errors about a branch'."""
+
+    def __init__(self, branch):
+        BzrError.__init__(self, branch=branch)
+
+
 class BzrCheckError(InternalBzrError):
     
     _fmt = "Internal check failed: %(message)s"
@@ -305,6 +312,11 @@
         BzrError.__init__(self, repository=repository, file_id=file_id)
 
 
+class NotStacked(BranchError):
+
+    _fmt = "The branch '%(branch)s' is not stacked."
+
+
 class InventoryModified(InternalBzrError):
 
     _fmt = ("The current inventory for the tree %(tree)r has been modified,"
@@ -561,6 +573,28 @@
         PathError.__init__(self, url, extra=extra)
 
 
+class UnstackableBranchFormat(BzrError):
+
+    _fmt = ("The branch '%(url)s'(%(format)s) is not a stackable format. "
+        "You will need to upgrade the branch to permit branch stacking.")
+
+    def __init__(self, format, url):
+        BzrError.__init__(self)
+        self.format = format
+        self.url = url
+
+
+class UnstackableRepositoryFormat(BzrError):
+
+    _fmt = ("The repository '%(url)s'(%(format)s) is not a stackable format. "
+        "You will need to upgrade the repository to permit branch stacking.")
+
+    def __init__(self, format, url):
+        BzrError.__init__(self)
+        self.format = format
+        self.url = url
+
+
 class ReadError(PathError):
     
     _fmt = """Error reading from %(path)r."""
@@ -1164,13 +1198,10 @@
         self.bases = bases
 
 
-class NoCommits(BzrError):
+class NoCommits(BranchError):
 
     _fmt = "Branch %(branch)s has no commits."
 
-    def __init__(self, branch):
-        BzrError.__init__(self, branch=branch)
-
 
 class UnlistableStore(BzrError):
 

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2008-02-12 05:17:25 +0000
+++ b/bzrlib/remote.py	2008-02-14 00:40:32 +0000
@@ -1289,6 +1289,18 @@
         self._ensure_real()
         return self._real_branch.get_physical_lock_status()
 
+    def get_stacked_on(self):
+        """Get the URL this branch is stacked against.
+
+        :raises NotStacked: If the branch is not stacked.
+        :raises UnstackableBranchFormat: If the branch does not support
+            stacking.
+        :raises UnstackableRepositoryFormat: If the repository does not support
+            stacking.
+        """
+        self._ensure_real()
+        return self._real_branch.get_stacked_on()
+
     def lock_read(self):
         if not self._lock_mode:
             self._lock_mode = 'r'
@@ -1464,6 +1476,17 @@
         self._ensure_real()
         return self._real_branch.set_parent(url)
         
+    def set_stacked_on(self, stacked_location):
+        """set the URL this branch is stacked against.
+
+        :raises UnstackableBranchFormat: If the branch does not support
+            stacking.
+        :raises UnstackableRepositoryFormat: If the repository does not support
+            stacking.
+        """
+        self._ensure_real()
+        return self._real_branch.set_stacked_on(stacked_location)
+
     def get_config(self):
         return RemoteBranchConfig(self)
 

=== modified file 'bzrlib/tests/branch_implementations/__init__.py'
--- a/bzrlib/tests/branch_implementations/__init__.py	2007-06-28 05:19:04 +0000
+++ b/bzrlib/tests/branch_implementations/__init__.py	2008-02-14 00:40:32 +0000
@@ -155,6 +155,7 @@
         'bzrlib.tests.branch_implementations.test_revision_history',
         'bzrlib.tests.branch_implementations.test_revision_id_to_revno',
         'bzrlib.tests.branch_implementations.test_sprout',
+        'bzrlib.tests.branch_implementations.test_stacking',
         'bzrlib.tests.branch_implementations.test_tags',
         'bzrlib.tests.branch_implementations.test_uncommit',
         'bzrlib.tests.branch_implementations.test_update',

=== modified file 'bzrlib/tests/test_branch.py'
--- a/bzrlib/tests/test_branch.py	2007-10-04 05:50:44 +0000
+++ b/bzrlib/tests/test_branch.py	2008-02-14 00:40:32 +0000
@@ -200,26 +200,36 @@
         self.make_branch_and_tree('bar')
 
 
-class TestBranch6(TestCaseWithTransport):
+class TestBranch67(object):
+    """Common tests for both branch 6 and 7 which are mostly the same."""
+
+    def get_format_name(self):
+        raise NotImplementedError(self.get_format_name)
+
+    def get_format_name_subtree(self):
+        raise NotImplementedError(self.get_format_name)
+
+    def get_class(self):
+        raise NotImplementedError(self.get_class)
 
     def test_creation(self):
         format = BzrDirMetaFormat1()
         format.set_branch_format(_mod_branch.BzrBranchFormat6())
         branch = self.make_branch('a', format=format)
-        self.assertIsInstance(branch, _mod_branch.BzrBranch6)
-        branch = self.make_branch('b', format='dirstate-tags')
-        self.assertIsInstance(branch, _mod_branch.BzrBranch6)
+        self.assertIsInstance(branch, self.get_class())
+        branch = self.make_branch('b', format=self.get_format_name())
+        self.assertIsInstance(branch, self.get_class())
         branch = _mod_branch.Branch.open('a')
-        self.assertIsInstance(branch, _mod_branch.BzrBranch6)
+        self.assertIsInstance(branch, self.get_class())
 
     def test_layout(self):
-        branch = self.make_branch('a', format='dirstate-tags')
+        branch = self.make_branch('a', format=self.get_format_name())
         self.failUnlessExists('a/.bzr/branch/last-revision')
         self.failIfExists('a/.bzr/branch/revision-history')
 
     def test_config(self):
         """Ensure that all configuration data is stored in the branch"""
-        branch = self.make_branch('a', format='dirstate-tags')
+        branch = self.make_branch('a', format=self.get_format_name())
         branch.set_parent('http://bazaar-vcs.org')
         self.failIfExists('a/.bzr/branch/parent')
         self.assertEqual('http://bazaar-vcs.org', branch.get_parent())
@@ -233,7 +243,7 @@
 
     def test_set_revision_history(self):
         tree = self.make_branch_and_memory_tree('.',
-            format='dirstate-tags')
+            format=self.get_format_name())
         tree.lock_write()
         try:
             tree.add('.')
@@ -247,11 +257,12 @@
             tree.unlock()
 
     def do_checkout_test(self, lightweight=False):
-        tree = self.make_branch_and_tree('source', format='dirstate-with-subtree')
+        tree = self.make_branch_and_tree('source',
+            format=self.get_format_name_subtree())
         subtree = self.make_branch_and_tree('source/subtree',
-            format='dirstate-with-subtree')
+            format=self.get_format_name_subtree())
         subsubtree = self.make_branch_and_tree('source/subtree/subsubtree',
-            format='dirstate-with-subtree')
+            format=self.get_format_name_subtree())
         self.build_tree(['source/subtree/file',
                          'source/subtree/subsubtree/file'])
         subsubtree.add('file')
@@ -279,7 +290,7 @@
         self.do_checkout_test(lightweight=True)
 
     def test_set_push(self):
-        branch = self.make_branch('source', format='dirstate-tags')
+        branch = self.make_branch('source', format=self.get_format_name())
         branch.get_config().set_user_option('push_location', 'old',
             store=config.STORE_LOCATION)
         warnings = []
@@ -294,6 +305,54 @@
         self.assertEqual(warnings[0], 'Value "new" is masked by "old" from '
                          'locations.conf')
 
+
+class TestBranch6(TestBranch67, TestCaseWithTransport):
+
+    def get_class(self):
+        return _mod_branch.BzrBranch6
+
+    def get_format_name(self):
+        return "dirstate-tags"
+
+    def get_format_name_subtree(self):
+        return "dirstate-with-subtree"
+
+    def test_set_stacked_on_errors(self):
+        branch = self.make_branch('a', format=self.get_format_name())
+        self.assertRaises(errors.UnstackableBranchFormat,
+            branch.set_stacked_on, None)
+
+    def test_default_stacked_location(self):
+        branch = self.make_branch('a', format=self.get_format_name())
+        self.assertRaises(errors.UnstackableBranchFormat, branch.get_stacked_on)
+
+
+class TestBranch7(TestBranch67, TestCaseWithTransport):
+
+    def get_class(self):
+        return _mod_branch.BzrBranch7
+
+    def get_format_name(self):
+        return "development"
+
+    def get_format_name_subtree(self):
+        return "development-subtree"
+
+    def test_set_stacked_on_unstackable_repo(self):
+        branch = self.make_branch('a', format=self.get_format_name())
+        target = self.make_branch('b')
+        self.assertRaises(errors.UnstackableRepositoryFormat,
+            branch.set_stacked_on, target.base)
+
+    def _test_default_stacked_location(self):
+        branch = self.make_branch('a', format=self.get_format_name())
+        self.assertRaises(errors.NotStacked, branch.get_stacked_on)
+
+    def test_stacked_location_file(self):
+        branch = self.make_branch('a', format=self.get_format_name())
+        self.assertFileEqual('\n', 'a/.bzr/branch/stacked-on')
+
+
 class TestBranchReference(TestCaseWithTransport):
     """Tests for the branch reference facility."""
 

=== modified file 'bzrlib/tests/test_errors.py'
--- a/bzrlib/tests/test_errors.py	2008-02-13 01:42:47 +0000
+++ b/bzrlib/tests/test_errors.py	2008-02-14 00:40:32 +0000
@@ -174,6 +174,11 @@
                              " tree atree.", str(error))
         self.assertIsInstance(error, errors.NoSuchRevision)
 
+    def test_not_stacked(self):
+        error = errors.NotStacked('a branch')
+        self.assertEqualDiff("The branch 'a branch' is not stacked.",
+            str(error))
+
     def test_not_write_locked(self):
         error = errors.NotWriteLocked('a thing to repr')
         self.assertEqualDiff("'a thing to repr' is not write locked but needs "
@@ -209,6 +214,24 @@
             " of bzrlib.",
             str(error))
 
+    def test_unstackable_branch_format(self):
+        format = u'foo'
+        url = "/foo"
+        error = errors.UnstackableBranchFormat(format, url)
+        self.assertEqualDiff(
+            "The branch '/foo'(foo) is not a stackable format. "
+            "You will need to upgrade the branch to permit branch stacking.",
+            str(error))
+
+    def test_unstackable_repository_format(self):
+        format = u'foo'
+        url = "/foo"
+        error = errors.UnstackableRepositoryFormat(format, url)
+        self.assertEqualDiff(
+            "The repository '/foo'(foo) is not a stackable format. "
+            "You will need to upgrade the repository to permit branch stacking.",
+            str(error))
+
     def test_up_to_date(self):
         error = errors.UpToDateFormat(bzrdir.BzrDirFormat4())
         self.assertEqualDiff("The branch format Bazaar-NG branch, "



More information about the bazaar-commits mailing list