Rev 4729: merge trunk to get test cleanups and resolve conflicts in file:///home/vila/src/bzr/bugs/438158-dpush-strict/

Vincent Ladeuil v.ladeuil+lp at free.fr
Fri Oct 2 15:50:36 BST 2009


At file:///home/vila/src/bzr/bugs/438158-dpush-strict/

------------------------------------------------------------
revno: 4729 [merge]
revision-id: v.ladeuil+lp at free.fr-20091002145035-z2arxiih2zaa5qo5
parent: v.ladeuil+lp at free.fr-20091002143530-v47dydvknxbkcfw8
parent: pqm at pqm.ubuntu.com-20091002101015-hed4j97ksfqyu4mp
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: 438158-dpush-strict
timestamp: Fri 2009-10-02 16:50:35 +0200
message:
  merge trunk to get test cleanups and resolve conflicts
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/_btree_serializer_pyx.pyx _parse_btree_c.pyx-20080703034413-3q25bklkenti3p8p-2
  bzrlib/_dirstate_helpers_pyx.pyx dirstate_helpers.pyx-20070503201057-u425eni465q4idwn-3
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
  bzrlib/mutabletree.py          mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/per_interrepository/test_fetch.py test_fetch.py-20080425213627-j60cjh782ufm83ry-1
  bzrlib/tests/per_intertree/test_compare.py test_compare.py-20060724101752-09ysswo1a92uqyoz-2
  bzrlib/tests/per_workingtree/test_smart_add.py test_smart_add.py-20070215175752-9s5mxoz8aqpd80fm-1
  bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
  bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
  bzrlib/transport/ssh.py        ssh.py-20060824042150-0s9787kng6zv1nwq-1
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2009-10-02 09:11:43 +0000
+++ b/NEWS	2009-10-02 14:50:35 +0000
@@ -49,6 +49,10 @@
   sent a signal to the bzr process specifically, for example by typing
   ``kill -QUIT PID`` in another shell.  (Martin Pool, #341535)
 
+* ``bzr add`` in a tree that has files with ``\r`` or ``\n`` in the
+  filename will issue a warning and skip over those files.
+  (Robert Collins, #3918)
+
 * ``bzr check`` in pack-0.92, 1.6 and 1.9 format repositories will no
   longer report incorrect errors about ``Missing inventory ('TREE_ROOT', ...)``
   (Robert Collins, #416732)
@@ -65,6 +69,10 @@
   merges are present in the working tree.
   (Vincent Ladeuil, #426344)
 
+* bzr will attempt to authenticate with SSH servers that support
+  ``keyboard-interactive`` auth but not ``password`` auth when using
+  Paramiko.   (Andrew Bennetts, #433846)
+
 * Clearer message when Bazaar runs out of memory, instead of a ``MemoryError``
   traceback.  (Martin Pool, #109115)
 
@@ -89,6 +97,10 @@
   is a fairly well packed repository (not as good as 'bzr pack' but
   good-enough.) (Robert Collins, John Arbash Meinel, #402652)
 
+* Fixed fetches from a stacked branch on a smart server that were failing
+  with some combinations of remote and local formats.  This was causing
+  "unknown object type identifier 60" errors.  (Andrew Bennetts, #427736)
+
 * Network streams now decode adjacent records of the same type into a
   single stream, reducing layering churn. (Robert Collins)
 
@@ -96,6 +108,14 @@
   object when doing a merge, and there is limbo, or pending-deletions
   directory.  (Gary van der Merwe, #427773)
 
+* Occasional IndexError on renamed files have been fixed. Operations that
+  set a full inventory in the working tree will now go via the
+  apply_inventory_delta code path which is simpler and easier to
+  understand than dirstates set_state_from_inventory method. This may
+  have a small performance impact on operations built on _write_inventory,
+  but such operations are already doing full tree scans, so no radical
+  performance change should be observed. (Robert Collins, #403322)
+
 * Prevent some kinds of incomplete data from being committed to a 2a
   repository, such as revisions without inventories or inventories without
   chk_bytes root records.
@@ -111,6 +131,9 @@
   domains or user ids embedding '.sig'. Now they can.
   (Matthew Fuller, Vincent Ladeuil, #430868)
 
+* When a file kind becomes unversionable after being added, a sensible
+  error will be shown instead of a traceback. (Robert Collins, #438569)
+
 Improvements
 ************
 
@@ -171,6 +194,9 @@
   documentation in ``developers/testing.txt`` for details.
   (Vincent Ladeuil)
 
+* Stop showing the number of tests due to missing features in the test
+  progress bar.  (Martin Pool)
+
 * Test parameterisation now does a shallow copy, not a deep copy of the test
   to be parameterised. This is not expected to break external use of test
   parameterisation, and is substantially faster. (Robert Collins)
@@ -194,6 +220,18 @@
 Bug Fixes
 *********
 
+* ``bzr add`` in a tree that has files with ``\r`` or ``\n`` in the
+  filename will issue a warning and skip over those files.
+  (Robert Collins, #3918)
+
+* bzr will attempt to authenticate with SSH servers that support
+  ``keyboard-interactive`` auth but not ``password`` auth when using
+  Paramiko.   (Andrew Bennetts, #433846)
+
+* Fixed fetches from a stacked branch on a smart server that were failing
+  with some combinations of remote and local formats.  This was causing
+  "unknown object type identifier 60" errors.  (Andrew Bennetts, #427736)
+
 * Improve the time for ``bzr log DIR`` for 2a format repositories.
   We had been using the same code path as for <2a formats, which required
   iterating over all objects in all revisions.
@@ -203,6 +241,20 @@
   object when doing a merge, and there is limbo, or pending-deletions
   directory.  (Gary van der Merwe, #427773)
 
+* Occasional IndexError on renamed files have been fixed. Operations that
+  set a full inventory in the working tree will now go via the
+  apply_inventory_delta code path which is simpler and easier to
+  understand than dirstates set_state_from_inventory method. This may
+  have a small performance impact on operations built on _write_inventory,
+  but such operations are already doing full tree scans, so no radical
+  performance change should be observed. (Robert Collins, #403322)
+
+* When a file kind becomes unversionable after being added, a sensible
+  error will be shown instead of a traceback. (Robert Collins, #438569)
+
+* Retrieving file text or mtime from a _PreviewTree has good performance when
+  there are many changes.  (Aaron Bentley)
+
 
 bzr 2.0.0
 #########

=== modified file 'bzrlib/_btree_serializer_pyx.pyx'
--- a/bzrlib/_btree_serializer_pyx.pyx	2009-09-04 21:16:14 +0000
+++ b/bzrlib/_btree_serializer_pyx.pyx	2009-10-02 05:43:41 +0000
@@ -188,14 +188,13 @@
             # And the next string is right after it
             self._cur_str = last + 1
             # The last character is right before the '\n'
-            last = last
 
         if last == self._start:
             # parsed it all.
             return 0
         if last < self._start:
             # Unexpected error condition - fail
-            return -1
+            raise AssertionError("last < self._start")
         if 0 == self._header_found:
             # The first line in a leaf node is the header "type=leaf\n"
             if strncmp("type=leaf", self._start, last - self._start) == 0:
@@ -204,14 +203,13 @@
             else:
                 raise AssertionError('Node did not start with "type=leaf": %r'
                     % (safe_string_from_size(self._start, last - self._start)))
-                return -1
 
         key = self.extract_key(last)
         # find the value area
         temp_ptr = <char*>_my_memrchr(self._start, c'\0', last - self._start)
         if temp_ptr == NULL:
             # Invalid line
-            return -1
+            raise AssertionError("Failed to find the value area")
         else:
             # capture the value string
             value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1)
@@ -225,15 +223,15 @@
                 # extract a reference list
                 loop_counter = loop_counter + 1
                 if last < self._start:
-                    return -1
+                    raise AssertionError("last < self._start")
                 # find the next reference list end point:
                 temp_ptr = <char*>memchr(self._start, c'\t', last - self._start)
                 if temp_ptr == NULL:
                     # Only valid for the last list
                     if loop_counter != self.ref_list_length:
                         # Invalid line
-                        return -1
-                        raise AssertionError("invalid key")
+                        raise AssertionError(
+                            "invalid key, loop_counter != self.ref_list_length")
                     else:
                         # scan to the end of the ref list area
                         ref_ptr = last
@@ -259,7 +257,7 @@
         else:
             if last != self._start:
                 # unexpected reference data present
-                return -1
+                raise AssertionError("unexpected reference data present")
             node_value = (value, ())
         PyList_Append(self.keys, (key, node_value))
         return 0

=== modified file 'bzrlib/_dirstate_helpers_pyx.pyx'
--- a/bzrlib/_dirstate_helpers_pyx.pyx	2009-08-28 05:00:33 +0000
+++ b/bzrlib/_dirstate_helpers_pyx.pyx	2009-10-02 05:43:41 +0000
@@ -1202,7 +1202,9 @@
                         content_change = 0
                     target_exec = False
                 else:
-                    raise Exception, "unknown kind %s" % path_info[2]
+                    if path is None:
+                        path = self.pathjoin(old_dirname, old_basename)
+                    raise errors.BadFileKindError(path, path_info[2])
             if source_minikind == c'd':
                 if path is None:
                     old_path = path = self.pathjoin(old_dirname, old_basename)

=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2009-09-14 01:48:28 +0000
+++ b/bzrlib/dirstate.py	2009-10-02 05:43:41 +0000
@@ -1324,7 +1324,7 @@
                 key = (dirname_utf8, basename, file_id)
                 minikind = DirState._kind_to_minikind[inv_entry.kind]
                 if minikind == 't':
-                    fingerprint = inv_entry.reference_revision
+                    fingerprint = inv_entry.reference_revision or ''
                 else:
                     fingerprint = ''
                 insertions[file_id] = (key, minikind, inv_entry.executable,
@@ -3328,7 +3328,9 @@
                         content_change = False
                     target_exec = False
                 else:
-                    raise Exception, "unknown kind %s" % path_info[2]
+                    if path is None:
+                        path = pathjoin(old_dirname, old_basename)
+                    raise errors.BadFileKindError(path, path_info[2])
             if source_minikind == 'd':
                 if path is None:
                     old_path = path = pathjoin(old_dirname, old_basename)

=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py	2009-09-25 21:24:21 +0000
+++ b/bzrlib/inventory.py	2009-10-02 05:43:41 +0000
@@ -2305,7 +2305,7 @@
     try:
         factory = entry_factory[kind]
     except KeyError:
-        raise BzrError("unknown kind %r" % kind)
+        raise errors.BadFileKindError(name, kind)
     return factory(file_id, name, parent_id)
 
 

=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py	2009-09-07 23:14:05 +0000
+++ b/bzrlib/mutabletree.py	2009-09-28 02:02:30 +0000
@@ -23,6 +23,7 @@
 from bzrlib.lazy_import import lazy_import
 lazy_import(globals(), """
 import os
+import re
 
 from bzrlib import (
     add,
@@ -427,6 +428,7 @@
                 dirs_to_add.append((path, None))
             prev_dir = path.raw_path
 
+        illegalpath_re = re.compile(r'[\r\n]')
         # dirs_to_add is initialised to a list of directories, but as we scan
         # directories we append files to it.
         # XXX: We should determine kind of files when we scan them rather than
@@ -443,6 +445,9 @@
             if not InventoryEntry.versionable_kind(kind):
                 warning("skipping %s (can't add file of kind '%s')", abspath, kind)
                 continue
+            if illegalpath_re.search(directory.raw_path):
+                warning("skipping %r (contains \\n or \\r)" % abspath)
+                continue
 
             if parent_ie is not None:
                 versioned = directory.base_path in parent_ie.children

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2009-09-24 05:31:23 +0000
+++ b/bzrlib/remote.py	2009-10-02 05:43:41 +0000
@@ -1923,7 +1923,7 @@
         :param search: The overall search to satisfy with streams.
         :param sources: A list of Repository objects to query.
         """
-        self.serialiser = self.to_format._serializer
+        self.from_serialiser = self.from_repository._format._serializer
         self.seen_revs = set()
         self.referenced_revs = set()
         # If there are heads in the search, or the key count is > 0, we are not
@@ -1946,7 +1946,8 @@
     def missing_parents_rev_handler(self, substream):
         for content in substream:
             revision_bytes = content.get_bytes_as('fulltext')
-            revision = self.serialiser.read_revision_from_string(revision_bytes)
+            revision = self.from_serialiser.read_revision_from_string(
+                revision_bytes)
             self.seen_revs.add(content.key[-1])
             self.referenced_revs.update(revision.parent_ids)
             yield content

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-09-25 05:19:17 +0000
+++ b/bzrlib/tests/__init__.py	2009-10-01 14:44:43 +0000
@@ -496,8 +496,8 @@
             a += ', %d err' % self.error_count
         if self.failure_count:
             a += ', %d fail' % self.failure_count
-        if self.unsupported:
-            a += ', %d missing' % len(self.unsupported)
+        # if self.unsupported:
+        #     a += ', %d missing' % len(self.unsupported)
         a += ']'
         return a
 

=== modified file 'bzrlib/tests/per_interrepository/test_fetch.py'
--- a/bzrlib/tests/per_interrepository/test_fetch.py	2009-09-09 02:14:12 +0000
+++ b/bzrlib/tests/per_interrepository/test_fetch.py	2009-10-02 02:40:37 +0000
@@ -134,6 +134,44 @@
             to_repo.texts.get_record_stream([('foo', revid)],
             'unordered', True).next().get_bytes_as('fulltext'))
 
+    def test_fetch_from_stacked_smart(self):
+        self.setup_smart_server_with_call_log()
+        self.test_fetch_from_stacked()
+
+    def test_fetch_from_stacked_smart_old(self):
+        self.setup_smart_server_with_call_log()
+        self.disable_verb('Repository.get_stream_1.19')
+        self.test_fetch_from_stacked()
+
+    def test_fetch_from_stacked(self):
+        """Fetch from a stacked branch succeeds."""
+        if not self.repository_format.supports_external_lookups:
+            raise TestNotApplicable("Need stacking support in the source.")
+        builder = self.make_branch_builder('full-branch')
+        builder.start_series()
+        builder.build_snapshot('first', None, [
+            ('add', ('', 'root-id', 'directory', '')),
+            ('add', ('file', 'file-id', 'file', 'content\n'))])
+        builder.build_snapshot('second', ['first'], [
+            ('modify', ('file-id', 'second content\n'))])
+        builder.build_snapshot('third', ['second'], [
+            ('modify', ('file-id', 'third content\n'))])
+        builder.finish_series()
+        branch = builder.get_branch()
+        repo = self.make_repository('stacking-base')
+        trunk = repo.bzrdir.create_branch()
+        trunk.repository.fetch(branch.repository, 'second')
+        repo = self.make_repository('stacked')
+        stacked_branch = repo.bzrdir.create_branch()
+        stacked_branch.set_stacked_on_url(trunk.base)
+        stacked_branch.repository.fetch(branch.repository, 'third')
+        target = self.make_to_repository('target')
+        target.fetch(stacked_branch.repository, 'third')
+        target.lock_read()
+        self.addCleanup(target.unlock)
+        all_revs = set(['first', 'second', 'third'])
+        self.assertEqual(all_revs, set(target.get_parent_map(all_revs)))
+
     def test_fetch_parent_inventories_at_stacking_boundary_smart(self):
         self.setup_smart_server_with_call_log()
         self.test_fetch_parent_inventories_at_stacking_boundary()

=== modified file 'bzrlib/tests/per_intertree/test_compare.py'
--- a/bzrlib/tests/per_intertree/test_compare.py	2009-08-28 05:00:33 +0000
+++ b/bzrlib/tests/per_intertree/test_compare.py	2009-10-02 05:43:41 +0000
@@ -948,6 +948,39 @@
                            (False, True))],
                          self.do_iter_changes(tree1, tree2))
 
+    def test_file_becomes_unversionable_bug_438569(self):
+        # This isn't strictly a intertree problem, but its the intertree code
+        # path that triggers all stat cache updates on both xml and dirstate
+        # trees.
+        # In bug 438569, a file becoming a fifo causes an assert. Fifo's are
+        # not versionable or diffable. For now, we simply stop cold when they
+        # are detected (because we don't know how far through the code the 
+        # assumption 'fifo's do not exist' goes). In future we could report 
+        # the kind change and have commit refuse to go futher, or something
+        # similar. One particular reason for choosing this approach is that
+        # there is no minikind for 'fifo' in dirstate today, so we can't 
+        # actually update records that way.
+        # To add confusion, the totally generic code path works - but it
+        # doesn't update persistent metadata. So this test permits InterTrees
+        # to either work, or fail with BadFileKindError.
+        self.requireFeature(tests.OsFifoFeature)
+        tree1 = self.make_branch_and_tree('1')
+        self.build_tree(['1/a'])
+        tree1.set_root_id('root-id')
+        tree1.add(['a'], ['a-id'])
+        tree2 = self.make_branch_and_tree('2')
+        os.mkfifo('2/a')
+        tree2.add(['a'], ['a-id'], ['file'])
+        try:
+            tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
+        except (KeyError,):
+            raise tests.TestNotApplicable(
+                "Cannot represent a FIFO in this case %s" % self.id())
+        try:
+            self.do_iter_changes(tree1, tree2)
+        except errors.BadFileKindError:
+            pass
+
     def test_missing_in_target(self):
         """Test with the target files versioned but absent from disk."""
         tree1 = self.make_branch_and_tree('1')

=== modified file 'bzrlib/tests/per_workingtree/test_smart_add.py'
--- a/bzrlib/tests/per_workingtree/test_smart_add.py	2009-07-10 07:14:02 +0000
+++ b/bzrlib/tests/per_workingtree/test_smart_add.py	2009-09-28 02:02:30 +0000
@@ -51,6 +51,18 @@
         self.assertEqual([('', 'V', 'directory'), ('a', 'V', 'file')],
                          files)
 
+    def assertFilenameSkipped(self, filename):
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree(['tree/'+filename])
+        tree.smart_add(['tree'])
+        self.assertEqual(None, tree.path2id(filename))
+
+    def test_path_containing_newline_skips(self):
+        self.assertFilenameSkipped('a\nb')
+
+    def test_path_containing_carriagereturn_skips(self):
+        self.assertFilenameSkipped('a\rb')
+
     def test_save_false(self):
         """Dry-run add doesn't permanently affect the tree."""
         wt = self.make_branch_and_tree('.')

=== modified file 'bzrlib/tests/test_transform.py'
--- a/bzrlib/tests/test_transform.py	2009-09-29 04:40:55 +0000
+++ b/bzrlib/tests/test_transform.py	2009-10-02 05:43:41 +0000
@@ -2629,7 +2629,9 @@
 
     def test_walkdirs(self):
         preview = self.get_empty_preview()
-        preview.version_file('tree-root', preview.root)
+        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
+        # FIXME: new_directory should mark root.
+        preview.adjust_path('', ROOT_PARENT, root)
         preview_tree = preview.get_preview_tree()
         file_trans_id = preview.new_file('a', preview.root, 'contents',
                                          'a-id')
@@ -2666,11 +2668,11 @@
         self.addCleanup(work_tree.unlock)
         preview = TransformPreview(work_tree)
         self.addCleanup(preview.finalize)
-        preview_tree = preview.get_preview_tree()
         file_trans_id = preview.trans_id_file_id('file-id')
         preview.delete_contents(file_trans_id)
         preview.create_file('a\nb\n', file_trans_id)
         pb = progress.DummyProgress()
+        preview_tree = preview.get_preview_tree()
         merger = Merger.from_revision_ids(pb, preview_tree,
                                           child_tree.branch.last_revision(),
                                           other_branch=child_tree.branch,

=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py	2009-09-29 04:40:55 +0000
+++ b/bzrlib/transform.py	2009-10-02 05:43:41 +0000
@@ -859,8 +859,8 @@
     def get_preview_tree(self):
         """Return a tree representing the result of the transform.
 
-        This tree only supports the subset of Tree functionality required
-        by show_diff_trees.  It must only be compared to tt._tree.
+        The tree is a snapshot, and altering the TreeTransform will invalidate
+        it.
         """
         return _PreviewTree(self)
 
@@ -1635,15 +1635,12 @@
         self._all_children_cache = {}
         self._path2trans_id_cache = {}
         self._final_name_cache = {}
-
-    def _changes(self, file_id):
-        for changes in self._transform.iter_changes():
-            if changes[0] == file_id:
-                return changes
+        self._iter_changes_cache = dict((c[0], c) for c in
+                                        self._transform.iter_changes())
 
     def _content_change(self, file_id):
         """Return True if the content of this file changed"""
-        changes = self._changes(file_id)
+        changes = self._iter_changes_cache.get(file_id)
         # changes[2] is true if the file content changed.  See
         # InterTree.iter_changes.
         return (changes is not None and changes[2])
@@ -1990,7 +1987,7 @@
 
     def annotate_iter(self, file_id,
                       default_revision=_mod_revision.CURRENT_REVISION):
-        changes = self._changes(file_id)
+        changes = self._iter_changes_cache.get(file_id)
         if changes is None:
             get_old = True
         else:

=== modified file 'bzrlib/transport/ssh.py'
--- a/bzrlib/transport/ssh.py	2009-08-28 05:00:33 +0000
+++ b/bzrlib/transport/ssh.py	2009-10-02 05:43:41 +0000
@@ -504,7 +504,16 @@
     except paramiko.SSHException, e:
         # Don't know what happened, but just ignore it
         pass
-    if 'password' not in supported_auth_types:
+    # We treat 'keyboard-interactive' and 'password' auth methods identically,
+    # because Paramiko's auth_password method will automatically try
+    # 'keyboard-interactive' auth (using the password as the response) if
+    # 'password' auth is not available.  Apparently some Debian and Gentoo
+    # OpenSSH servers require this.
+    # XXX: It's possible for a server to require keyboard-interactive auth that
+    # requires something other than a single password, but we currently don't
+    # support that.
+    if ('password' not in supported_auth_types and
+        'keyboard-interactive' not in supported_auth_types):
         raise errors.ConnectionError('Unable to authenticate to SSH host as'
             '\n  %s@%s\nsupported auth types: %s'
             % (username, host, supported_auth_types))

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2009-08-25 04:43:21 +0000
+++ b/bzrlib/workingtree_4.py	2009-09-30 21:38:49 +0000
@@ -1267,9 +1267,17 @@
         if self._dirty:
             raise AssertionError("attempting to write an inventory when the "
                 "dirstate is dirty will lose pending changes")
-        self.current_dirstate().set_state_from_inventory(inv)
-        self._make_dirty(reset_inventory=False)
-        if self._inventory is not None:
+        had_inventory = self._inventory is not None
+        # Setting self._inventory = None forces the dirstate to regenerate the
+        # working inventory. We do this because self.inventory may be inv, or
+        # may have been modified, and either case would prevent a clean delta
+        # being created.
+        self._inventory = None
+        # generate a delta,
+        delta = inv._make_delta(self.inventory)
+        # and apply it.
+        self.apply_inventory_delta(delta)
+        if had_inventory:
             self._inventory = inv
         self.flush()
 



More information about the bazaar-commits mailing list