Rev 6318: (jelmer) Add HPSS call for ``Repository.iter_files_bytes``. (Jelmer Vernooij) in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

Patch Queue Manager pqm at pqm.ubuntu.com
Mon Nov 28 16:29:49 UTC 2011


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 6318 [merge]
revision-id: pqm at pqm.ubuntu.com-20111128162949-cx0obntgfj3rzhgr
parent: pqm at pqm.ubuntu.com-20111128160445-5ndgnqjd0vggze3d
parent: jelmer at samba.org-20111128155939-709ll9ta5aqduig0
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2011-11-28 16:29:49 +0000
message:
  (jelmer) Add HPSS call for ``Repository.iter_files_bytes``. (Jelmer Vernooij)
modified:
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/revisiontree.py         revisiontree.py-20060724012533-bg8xyryhxd0o0i0h-1
  bzrlib/smart/repository.py     repository.py-20061128022038-vr5wy5bubyb8xttk-1
  bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
  bzrlib/tests/blackbox/test_cat.py test_cat.py-20051201162916-f0937e4e19ea24b3
  bzrlib/tests/blackbox/test_export.py test_export.py-20051229024010-e6c26658e460fb1c
  bzrlib/tests/per_bzrdir/test_bzrdir.py test_bzrdir.py-20100829143338-2uachgod1c3liktl-1
  bzrlib/tests/per_repository_vf/test_check_reconcile.py test_broken.py-20070928125406-62236394w0jpbpd6-2
  bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
  bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
  bzrlib/vf_repository.py        vf_repository.py-20110502151858-yh9nnoxpokg86unk-1
  doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2011-11-28 14:18:56 +0000
+++ b/bzrlib/remote.py	2011-11-28 15:59:39 +0000
@@ -29,6 +29,7 @@
     graph,
     lock,
     lockdir,
+    osutils,
     registry,
     repository as _mod_repository,
     revision as _mod_revision,
@@ -1784,8 +1785,7 @@
 
     @needs_read_lock
     def get_inventory(self, revision_id):
-        self._ensure_real()
-        return self._real_repository.get_inventory(revision_id)
+        return list(self.iter_inventories([revision_id]))[0]
 
     def iter_inventories(self, revision_ids, ordering=None):
         self._ensure_real()
@@ -1918,11 +1918,80 @@
         return self._real_repository._get_versioned_file_checker(
             revisions, revision_versions_cache)
 
+    def _iter_files_bytes_rpc(self, desired_files, absent):
+        path = self.bzrdir._path_for_remote_call(self._client)
+        lines = []
+        identifiers = []
+        for (file_id, revid, identifier) in desired_files:
+            lines.append("%s\0%s" % (
+                osutils.safe_file_id(file_id),
+                osutils.safe_revision_id(revid)))
+            identifiers.append(identifier)
+        (response_tuple, response_handler) = (
+            self._call_with_body_bytes_expecting_body(
+            "Repository.iter_files_bytes", (path, ), "\n".join(lines)))
+        if response_tuple != ('ok', ):
+            response_handler.cancel_read_body()
+            raise errors.UnexpectedSmartServerResponse(response_tuple)
+        byte_stream = response_handler.read_streamed_body()
+        def decompress_stream(start, byte_stream, unused):
+            decompressor = zlib.decompressobj()
+            yield decompressor.decompress(start)
+            while decompressor.unused_data == "":
+                try:
+                    data = byte_stream.next()
+                except StopIteration:
+                    break
+                yield decompressor.decompress(data)
+            yield decompressor.flush()
+            unused.append(decompressor.unused_data)
+        unused = ""
+        while True:
+            while not "\n" in unused:
+                unused += byte_stream.next()
+            header, rest = unused.split("\n", 1)
+            args = header.split("\0")
+            if args[0] == "absent":
+                absent[identifiers[int(args[3])]] = (args[1], args[2])
+                unused = rest
+                continue
+            elif args[0] == "ok":
+                idx = int(args[1])
+            else:
+                raise errors.UnexpectedSmartServerResponse(args)
+            unused_chunks = []
+            yield (identifiers[idx],
+                decompress_stream(rest, byte_stream, unused_chunks))
+            unused = "".join(unused_chunks)
+
     def iter_files_bytes(self, desired_files):
         """See Repository.iter_file_bytes.
         """
-        self._ensure_real()
-        return self._real_repository.iter_files_bytes(desired_files)
+        try:
+            absent = {}
+            for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
+                    desired_files, absent):
+                yield identifier, bytes_iterator
+            for fallback in self._fallback_repositories:
+                if not absent:
+                    break
+                desired_files = [(key[0], key[1], identifier) for
+                    (identifier, key) in absent.iteritems()]
+                for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
+                    del absent[identifier]
+                    yield identifier, bytes_iterator
+            if absent:
+                # There may be more missing items, but raise an exception
+                # for just one.
+                missing_identifier = absent.keys()[0]
+                missing_key = absent[missing_identifier]
+                raise errors.RevisionNotPresent(revision_id=missing_key[1],
+                    file_id=missing_key[0])
+        except errors.UnknownSmartMethod:
+            self._ensure_real()
+            for (identifier, bytes_iterator) in (
+                self._real_repository.iter_files_bytes(desired_files)):
+                yield identifier, bytes_iterator
 
     def get_cached_parent_map(self, revision_ids):
         """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
@@ -2111,8 +2180,9 @@
 
     @needs_read_lock
     def revision_trees(self, revision_ids):
-        self._ensure_real()
-        return self._real_repository.revision_trees(revision_ids)
+        inventories = self.iter_inventories(revision_ids)
+        for inv in inventories:
+            yield InventoryRevisionTree(self, inv, inv.revision_id)
 
     @needs_read_lock
     def get_revision_reconcile(self, revision_id):
@@ -2137,7 +2207,6 @@
     def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
         # get a tarball of the remote repository, and copy from that into the
         # destination
-        from bzrlib import osutils
         import tarfile
         # TODO: Maybe a progress bar while streaming the tarball?
         note(gettext("Copying repository content as tarball..."))
@@ -2197,9 +2266,6 @@
     def revisions(self):
         """Decorate the real repository for now.
 
-        In the short term this should become a real object to intercept graph
-        lookups.
-
         In the long term a full blown network facility is needed.
         """
         self._ensure_real()
@@ -2336,12 +2402,14 @@
             return self._real_repository.add_signature_text(
                 revision_id, signature)
         path = self.bzrdir._path_for_remote_call(self._client)
-        response, response_handler = self._call_with_body_bytes(
-            'Repository.add_signature_text', (path, revision_id),
-            signature)
+        response, handler = self._call_with_body_bytes_expecting_body(
+            'Repository.add_signature_text', (path, self._lock_token,
+                revision_id) + tuple(self._write_group_tokens), signature)
+        handler.cancel_read_body()
         self.refresh_data()
         if response[0] != 'ok':
             raise errors.UnexpectedSmartServerResponse(response)
+        self._write_group_tokens = response[1:]
 
     def has_signature_for_revision_id(self, revision_id):
         path = self.bzrdir._path_for_remote_call(self._client)
@@ -3855,6 +3923,13 @@
     lambda err, find, get_path: errors.ReadError(get_path()))
 error_translators.register('NoSuchFile',
     lambda err, find, get_path: errors.NoSuchFile(get_path()))
+error_translators.register('UnsuspendableWriteGroup',
+    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
+        repository=find('repository')))
+error_translators.register('UnresumableWriteGroup',
+    lambda err, find, get_path: errors.UnresumableWriteGroup(
+        repository=find('repository'), write_groups=err.error_args[0],
+        reason=err.error_args[1]))
 no_context_error_translators.register('IncompatibleRepositories',
     lambda err: errors.IncompatibleRepositories(
         err.error_args[0], err.error_args[1], err.error_args[2]))
@@ -3905,14 +3980,9 @@
 no_context_error_translators.register('MemoryError',
     lambda err: errors.BzrError("remote server out of memory\n"
         "Retry non-remotely, or contact the server admin for details."))
+no_context_error_translators.register('RevisionNotPresent',
+    lambda err: errors.RevisionNotPresent(err.error_args[0], err.error_args[1]))
 
 no_context_error_translators.register('BzrCheckError',
     lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
-error_translators.register('UnsuspendableWriteGroup',
-    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
-        repository=find('repository')))
-error_translators.register('UnresumableWriteGroup',
-    lambda err, find, get_path: errors.UnresumableWriteGroup(
-        repository=find('repository'), write_groups=err.error_args[0],
-        reason=err.error_args[1]))

=== modified file 'bzrlib/revisiontree.py'
--- a/bzrlib/revisiontree.py	2011-08-29 19:06:57 +0000
+++ b/bzrlib/revisiontree.py	2011-11-21 17:07:19 +0000
@@ -65,8 +65,9 @@
         raise NotImplementedError(self.get_file_revision)
 
     def get_file_text(self, file_id, path=None):
-        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
-        return ''.join(content)
+        for (identifier, content) in self.iter_files_bytes([(file_id, None)]):
+            ret = "".join(content)
+        return ret
 
     def get_file(self, file_id, path=None):
         return StringIO(self.get_file_text(file_id))
@@ -215,14 +216,14 @@
     def iter_files_bytes(self, desired_files):
         """See Tree.iter_files_bytes.
 
-        This version is implemented on top of Repository.extract_files_bytes"""
+        This version is implemented on top of Repository.iter_files_bytes"""
         repo_desired_files = [(f, self.get_file_revision(f), i)
                               for f, i in desired_files]
         try:
             for result in self._repository.iter_files_bytes(repo_desired_files):
                 yield result
         except errors.RevisionNotPresent, e:
-            raise errors.NoSuchFile(e.revision_id)
+            raise errors.NoSuchFile(e.file_id)
 
     def annotate_iter(self, file_id,
                       default_revision=revision.CURRENT_REVISION):

=== modified file 'bzrlib/smart/repository.py'
--- a/bzrlib/smart/repository.py	2011-11-28 14:18:56 +0000
+++ b/bzrlib/smart/repository.py	2011-11-28 15:59:39 +0000
@@ -963,18 +963,18 @@
     New in 2.5.
     """
 
-    def do_repository_request(self, repository, lock_token, write_group_tokens,
-            revision_id):
+    def do_repository_request(self, repository, lock_token, revision_id,
+            *write_group_tokens):
         """Add a revision signature text.
 
         :param repository: Repository to operate on
         :param lock_token: Lock token
+        :param revision_id: Revision for which to add signature
         :param write_group_tokens: Write group tokens
-        :param revision_id: Revision for which to add signature
         """
         self._lock_token = lock_token
+        self._revision_id = revision_id
         self._write_group_tokens = write_group_tokens
-        self._revision_id = revision_id
         return None
 
     def do_body(self, body_bytes):
@@ -988,12 +988,14 @@
         try:
             self._repository.resume_write_group(self._write_group_tokens)
             try:
-                self._repository.add_signature_text(self._revision_id, body_bytes)
+                self._repository.add_signature_text(self._revision_id,
+                    body_bytes)
             finally:
                 new_write_group_tokens = self._repository.suspend_write_group()
         finally:
             self._repository.unlock()
-        return SuccessfulSmartServerResponse(('ok', ) + tuple(new_write_group_tokens))
+        return SuccessfulSmartServerResponse(
+            ('ok', ) + tuple(new_write_group_tokens))
 
 
 class SmartServerRepositoryStartWriteGroup(SmartServerRepositoryRequest):
@@ -1126,6 +1128,63 @@
         return SuccessfulSmartServerResponse(("ok", ), )
 
 
+class SmartServerRepositoryIterFilesBytes(SmartServerRepositoryRequest):
+    """Iterate over the contents of files.
+
+    The client sends a list of desired files to stream, one
+    per line, and as tuples of file id and revision, separated by
+    \0.
+
+    The server replies with a stream. Each entry is preceded by a header,
+    which can either be:
+
+    * "ok\x00IDX\n" where IDX is the index of the entry in the desired files
+        list sent by the client. This header is followed by the contents of
+        the file, bzip2-compressed.
+    * "absent\x00FILEID\x00REVISION\x00IDX" to indicate a text is missing.
+        The client can then raise an appropriate RevisionNotPresent error
+        or check its fallback repositories.
+
+    New in 2.5.
+    """
+
+    def body_stream(self, repository, desired_files):
+        self._repository.lock_read()
+        try:
+            text_keys = {}
+            for i, key in enumerate(desired_files):
+                text_keys[key] = i
+            for record in repository.texts.get_record_stream(text_keys,
+                    'unordered', True):
+                identifier = text_keys[record.key]
+                if record.storage_kind == 'absent':
+                    yield "absent\0%s\0%s\0%d\n" % (record.key[0],
+                        record.key[1], identifier)
+                    # FIXME: Way to abort early?
+                    continue
+                yield "ok\0%d\n" % identifier
+                compressor = zlib.compressobj()
+                for bytes in record.get_bytes_as('chunked'):
+                    data = compressor.compress(bytes)
+                    if data:
+                        yield data
+                data = compressor.flush()
+                if data:
+                    yield data
+        finally:
+            self._repository.unlock()
+
+    def do_body(self, body_bytes):
+        desired_files = [
+            tuple(l.split("\0")) for l in body_bytes.splitlines()]
+        return SuccessfulSmartServerResponse(('ok', ),
+            body_stream=self.body_stream(self._repository, desired_files))
+
+    def do_repository_request(self, repository):
+        # Signal that we want a body
+        return None
+
+
 class SmartServerRepositoryIterRevisions(SmartServerRepositoryRequest):
     """Stream a list of revisions.
 

=== modified file 'bzrlib/smart/request.py'
--- a/bzrlib/smart/request.py	2011-11-28 14:18:56 +0000
+++ b/bzrlib/smart/request.py	2011-11-28 15:59:39 +0000
@@ -415,6 +415,8 @@
     elif isinstance(err, errors.ShortReadvError):
         return ('ShortReadvError', err.path, str(err.offset), str(err.length),
                 str(err.actual))
+    elif isinstance(err, errors.RevisionNotPresent):
+        return ('RevisionNotPresent', err.revision_id, err.file_id)
     elif isinstance(err, errors.UnstackableRepositoryFormat):
         return (('UnstackableRepositoryFormat', str(err.format), err.url))
     elif isinstance(err, errors.UnstackableBranchFormat):
@@ -658,6 +660,9 @@
 request_handlers.register_lazy(
     'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
 request_handlers.register_lazy(
+    'Repository.iter_files_bytes', 'bzrlib.smart.repository',
+    'SmartServerRepositoryIterFilesBytes')
+request_handlers.register_lazy(
     'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
 request_handlers.register_lazy(
     'Repository.make_working_trees', 'bzrlib.smart.repository', 'SmartServerRepositoryMakeWorkingTrees')

=== modified file 'bzrlib/tests/blackbox/test_cat.py'
--- a/bzrlib/tests/blackbox/test_cat.py	2011-11-22 23:15:35 +0000
+++ b/bzrlib/tests/blackbox/test_cat.py	2011-11-23 12:02:26 +0000
@@ -238,4 +238,4 @@
         # being too low. If rpc_count increases, more network roundtrips have
         # become necessary for this use case. Please do not adjust this number
         # upwards without agreement from bzr's network support maintainers.
-        self.assertLength(17, self.hpss_calls)
+        self.assertLength(16, self.hpss_calls)

=== modified file 'bzrlib/tests/blackbox/test_export.py'
--- a/bzrlib/tests/blackbox/test_export.py	2011-11-22 23:34:54 +0000
+++ b/bzrlib/tests/blackbox/test_export.py	2011-11-23 12:02:26 +0000
@@ -430,4 +430,4 @@
         # being too low. If rpc_count increases, more network roundtrips have
         # become necessary for this use case. Please do not adjust this number
         # upwards without agreement from bzr's network support maintainers.
-        self.assertLength(17, self.hpss_calls)
+        self.assertLength(16, self.hpss_calls)

=== modified file 'bzrlib/tests/per_bzrdir/test_bzrdir.py'
--- a/bzrlib/tests/per_bzrdir/test_bzrdir.py	2011-11-04 16:32:00 +0000
+++ b/bzrlib/tests/per_bzrdir/test_bzrdir.py	2011-11-21 18:59:51 +0000
@@ -162,8 +162,12 @@
                 for file_id, revision_id in text_index.iterkeys():
                     desired_files.append(
                         (file_id, revision_id, (file_id, revision_id)))
-                left_texts = list(left_repo.iter_files_bytes(desired_files))
-                right_texts = list(right_repo.iter_files_bytes(desired_files))
+                left_texts = [(identifier, "".join(bytes_iterator)) for
+                        (identifier, bytes_iterator) in
+                        left_repo.iter_files_bytes(desired_files)]
+                right_texts = [(identifier, "".join(bytes_iterator)) for
+                        (identifier, bytes_iterator) in
+                        right_repo.iter_files_bytes(desired_files)]
                 left_texts.sort()
                 right_texts.sort()
                 self.assertEqual(left_texts, right_texts)

=== modified file 'bzrlib/tests/per_repository_vf/test_check_reconcile.py'
--- a/bzrlib/tests/per_repository_vf/test_check_reconcile.py	2011-05-11 14:26:41 +0000
+++ b/bzrlib/tests/per_repository_vf/test_check_reconcile.py	2011-11-28 13:33:08 +0000
@@ -461,7 +461,8 @@
 
         # make rev1b: A well-formed revision, containing 'a-file'
         # rev1b of a-file has the exact same contents as rev1a.
-        file_contents = repo.revision_tree('rev1a').get_file_text('a-file-id')
+        file_contents = repo.texts.get_record_stream([('a-file-id', 'rev1a')],
+            "unordered", False).next().get_bytes_as('fulltext')
         inv = self.make_one_file_inventory(
             repo, 'rev1b', [], root_revision='rev1b',
             file_contents=file_contents)

=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py	2011-11-28 14:18:56 +0000
+++ b/bzrlib/tests/test_remote.py	2011-11-28 15:59:39 +0000
@@ -2413,14 +2413,26 @@
     def test_add_signature_text(self):
         transport_path = 'quack'
         repo, client = self.setup_fake_client_and_repository(transport_path)
-        client.add_success_response('ok')
+        client.add_expected_call(
+            'Repository.lock_write', ('quack/', ''),
+            'success', ('ok', 'a token'))
+        client.add_expected_call(
+            'Repository.start_write_group', ('quack/', 'a token'),
+            'success', ('ok', ('token1', )))
+        client.add_expected_call(
+            'Repository.add_signature_text', ('quack/', 'a token', 'rev1',
+                'token1'),
+            'success', ('ok', ), None)
+        repo.lock_write()
+        repo.start_write_group()
         self.assertIs(None,
             repo.add_signature_text("rev1", "every bloody emperor"))
         self.assertEqual(
-            [('call_with_body_bytes',
-              'Repository.add_signature_text', ('quack/', 'rev1', ),
-              'every bloody emperor')],
-            client._calls)
+            ('call_with_body_bytes_expecting_body',
+              'Repository.add_signature_text',
+                ('quack/', 'a token', 'rev1', 'token1'),
+              'every bloody emperor'),
+            client._calls[-1])
 
 
 class TestRepositoryGetParentMap(TestRemoteRepository):
@@ -2987,7 +2999,7 @@
             'success', ('ok', 'a token'))
         client.add_expected_call(
             'Repository.start_write_group', ('quack/', 'a token'),
-            'success', ('ok', 'token1'))
+            'success', ('ok', ('token1', )))
         repo.lock_write()
         repo.start_write_group()
 
@@ -3126,6 +3138,33 @@
         self.assertEqual([], client._calls)
 
 
+class TestRepositoryIterFilesBytes(TestRemoteRepository):
+    """Test Repository.iter_file_bytes."""
+
+    def test_single(self):
+        transport_path = 'quack'
+        repo, client = self.setup_fake_client_and_repository(transport_path)
+        client.add_expected_call(
+            'Repository.iter_files_bytes', ('quack/', ),
+            'success', ('ok',), iter(["ok\x000", "\n", zlib.compress("mydata" * 10)]))
+        for (identifier, byte_stream) in repo.iter_files_bytes([("somefile",
+                "somerev", "myid")]):
+            self.assertEquals("myid", identifier)
+            self.assertEquals("".join(byte_stream), "mydata" * 10)
+
+    def test_missing(self):
+        transport_path = 'quack'
+        repo, client = self.setup_fake_client_and_repository(transport_path)
+        client.add_expected_call(
+            'Repository.iter_files_bytes',
+                ('quack/', ),
+            'error', ('RevisionNotPresent', 'somefile', 'somerev'),
+            iter(["absent\0somefile\0somerev\n"]))
+        self.assertRaises(errors.RevisionNotPresent, list,
+                repo.iter_files_bytes(
+                [("somefile", "somerev", "myid")]))
+
+
 class TestRepositoryInsertStreamBase(TestRemoteRepository):
     """Base class for Repository.insert_stream and .insert_stream_1.19
     tests.

=== modified file 'bzrlib/tests/test_smart.py'
--- a/bzrlib/tests/test_smart.py	2011-11-28 14:18:56 +0000
+++ b/bzrlib/tests/test_smart.py	2011-11-28 15:59:39 +0000
@@ -1541,7 +1541,7 @@
         tree.branch.repository.start_write_group()
         write_group_tokens = tree.branch.repository.suspend_write_group()
         self.assertEqual(None, request.execute('', write_token,
-            write_group_tokens, 'rev1'))
+            'rev1', *write_group_tokens))
         response = request.do_body('somesignature')
         self.assertTrue(response.is_successful())
         self.assertEqual(response.args[0], 'ok')
@@ -1849,6 +1849,36 @@
             request.execute('', rev_id_utf8))
 
 
+class TestSmartServerRepositoryIterFilesBytes(tests.TestCaseWithTransport):
+
+    def test_single(self):
+        backing = self.get_transport()
+        request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
+        t = self.make_branch_and_tree('.')
+        self.addCleanup(t.lock_write().unlock)
+        self.build_tree_contents([("file", "somecontents")])
+        t.add(["file"], ["thefileid"])
+        t.commit(rev_id='somerev', message="add file")
+        self.assertIs(None, request.execute(''))
+        response = request.do_body("thefileid\0somerev\n")
+        self.assertTrue(response.is_successful())
+        self.assertEquals(response.args, ("ok", ))
+        self.assertEquals("".join(response.body_stream),
+            "ok\x000\n" + zlib.compress("somecontents"))
+
+    def test_missing(self):
+        backing = self.get_transport()
+        request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
+        t = self.make_branch_and_tree('.')
+        self.addCleanup(t.lock_write().unlock)
+        self.assertIs(None, request.execute(''))
+        response = request.do_body("thefileid\0revision\n")
+        self.assertTrue(response.is_successful())
+        self.assertEquals(response.args, ("ok", ))
+        self.assertEquals("".join(response.body_stream),
+            "absent\x00thefileid\x00revision\x000\n")
+
+
 class TestSmartServerRequestHasSignatureForRevisionId(
         tests.TestCaseWithMemoryTransport):
 
@@ -2472,6 +2502,8 @@
             smart_repo.SmartServerRepositoryInsertStreamLocked)
         self.assertHandlerEqual('Repository.is_shared',
             smart_repo.SmartServerRepositoryIsShared)
+        self.assertHandlerEqual('Repository.iter_files_bytes',
+            smart_repo.SmartServerRepositoryIterFilesBytes)
         self.assertHandlerEqual('Repository.lock_write',
             smart_repo.SmartServerRepositoryLockWrite)
         self.assertHandlerEqual('Repository.make_working_trees',

=== modified file 'bzrlib/vf_repository.py'
--- a/bzrlib/vf_repository.py	2011-10-17 08:45:09 +0000
+++ b/bzrlib/vf_repository.py	2011-11-20 02:16:25 +0000
@@ -1516,7 +1516,7 @@
             text_keys[(file_id, revision_id)] = callable_data
         for record in self.texts.get_record_stream(text_keys, 'unordered', True):
             if record.storage_kind == 'absent':
-                raise errors.RevisionNotPresent(record.key, self)
+                raise errors.RevisionNotPresent(record.key[1], record.key[0])
             yield text_keys[record.key], record.get_bytes_as('chunked')
 
     def _generate_text_key_index(self, text_key_references=None,

=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt	2011-11-28 14:18:56 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt	2011-11-28 15:59:39 +0000
@@ -53,6 +53,11 @@
 * Add HPSS call for looking up revision numbers from revision ids on
   remote repositories. (Jelmer Vernooij, #640253)
 
+* Add HPSS call for retrieving file contents from remote repositories.
+  Should improve performance for lightweight checkouts and exports of
+  from remote repositories.  (Jelmer Vernooij, #368717, #762330,
+  #608640)
+
 * Cope with missing revision ids being specified to
   ``Repository.gather_stats`` HPSS call. (Jelmer Vernooij, #411290)
 




More information about the bazaar-commits mailing list