Merge lp:~jelmer/bzr/hpss-get-inventories into lp:bzr

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merged at revision: 6372
Proposed branch: lp:~jelmer/bzr/hpss-get-inventories
Merge into: lp:bzr
Prerequisite: lp:~jelmer/bzr/hpss-_get-checkout-format
Diff against target: 728 lines (+372/-52)
17 files modified
bzrlib/remote.py (+171/-5)
bzrlib/smart/repository.py (+58/-1)
bzrlib/smart/request.py (+6/-3)
bzrlib/tests/blackbox/test_annotate.py (+1/-1)
bzrlib/tests/blackbox/test_branch.py (+3/-3)
bzrlib/tests/blackbox/test_cat.py (+2/-3)
bzrlib/tests/blackbox/test_checkout.py (+3/-8)
bzrlib/tests/blackbox/test_export.py (+2/-3)
bzrlib/tests/blackbox/test_log.py (+4/-6)
bzrlib/tests/blackbox/test_ls.py (+2/-3)
bzrlib/tests/blackbox/test_sign_my_commits.py (+4/-12)
bzrlib/tests/per_interbranch/test_push.py (+2/-2)
bzrlib/tests/per_repository_chk/test_supported.py (+11/-1)
bzrlib/tests/per_repository_vf/test_add_inventory_by_delta.py (+1/-1)
bzrlib/tests/test_remote.py (+41/-0)
bzrlib/tests/test_smart.py (+48/-0)
doc/en/release-notes/bzr-2.5.txt (+13/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/hpss-get-inventories
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Approve
Vincent Ladeuil Approve
Andrew Bennetts Pending
Review via email: mp+85252@code.launchpad.net

This proposal supersedes a proposal from 2011-11-29.

Commit message

Add HPSS call for retrieving inventories.

Description of the change

Add a HPSS call for ``Repository.iter_inventories``.

This massively reduces the number of roundtrips for various commands, and causes a fair number to no longer use VFS calls at all when used against a modern remote server.

Repository.get_deltas_for_revisions() now has its own implementation for remote repositories, which means the client won't use the VFS to get at the inventories to generate the deltas from.

Unlike my previous attempt at this, this now uses record streams with inventory deltas. It is still a separate verb though.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

Timings when checking out bzr.dev:

~/src/bzr/hpss-get-inventories/bzr co --lightweight bzr://people.samba.org/bzr.dev 2.25s user 0.46s system 15% cpu 17.277 total
~/src/bzr/hpss-get-inventories/bzr export /tmp/bzr.dev2 bzr://people.samba.org/bzr.dev 1.54s user 0.40s system 12% cpu 15.435 total

versus against an older bzr server:

~/src/bzr/hpss-get-inventories/bzr co --lightweight bzr://people.samba.org/bzr.dev 4.91s user 1.03s system 10% cpu 58.807 total
~/src/bzr/hpss-get-inventories/bzr export /tmp/bzr.dev5 bzr://people.samba.org/bzr.dev 4.35s user 0.98s system 9% cpu 58.357 total

Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

For comparison (though I should it's a different server):

apt-get source bzr 2.79s user 0.54s system 27% cpu 12.338 total

so we're getting closer.

Revision history for this message
Andrew Bennetts (spiv) wrote : Posted in a previous version of this proposal

Jelmer Vernooij wrote:
> Add a HPSS call for ``Repository.iter_inventories``.

I am a bit concerned that by adding verbs like this, with their own ad hoc
record stream-like wire format, that we're growing the maintenance burden
unnecessarily, and not reusing improvements everywhere we could.

I'm glad this is using inventory-deltas. But if it's worth zlib compressing
them here, shouldn't we do that for inventory-deltas from get_record_stream too?

Also, could you implement this via the get_record_stream RPC with a new
parameter that means “just inventories (rather than rev+inv+texts+chks+sigs for
given keys)”?

On one hand it might feel a bit ugly to make one RPC do so many things,
get_record_stream, do so many things, but on the other I think it provides a
useful pressure to keep the interface minimal and consistent (e.g. whether
records are zlib-compressed).

Adding this iter_inventories RPC might be the right thing (although putting
“iter” in the name of an RPC feels weird to me, that's surely a property of the
Python API rather than a property of the RPC), but I'd like hear what you think
about the tradeoffs of doing that vs. extending get_record_stream.

-Andrew.

Revision history for this message
Martin Pool (mbp) wrote : Posted in a previous version of this proposal

On 30 November 2011 09:37, Andrew Bennetts
<email address hidden> wrote:
> Jelmer Vernooij wrote:
>> Add a HPSS call for ``Repository.iter_inventories``.
>
> I am a bit concerned that by adding verbs like this, with their own ad hoc
> record stream-like wire format, that we're growing the maintenance burden
> unnecessarily, and not reusing improvements everywhere we could.

+1

--
Martin

Revision history for this message
Vincent Ladeuil (vila) wrote : Posted in a previous version of this proposal

>> I am a bit concerned that by adding verbs like this, with their own ad hoc
>> record stream-like wire format, that we're growing the maintenance burden
>> unnecessarily, and not reusing improvements everywhere we could.

> +1

I kind of had the same feeling when jelmer started adding a bunch of verbs that were basically replacing a vfs roundtrip by a smart request roundtrip.

BUT

If we stop maintaining these verbs on the server side, the clients will fallback to vfs.

So we introduce different/better verbs, we can remove the old ones in both the server and the client and all but the newest clients can still fallback to vfs.

The net effect is at least to get to a point where the most recent client/server do not use vfs at all.

This sounds like a good incremental step to me.

'reusing improvements everywhere we could' is still valuable but doesn't to block progress.

review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

Hi Andrew,

> Jelmer Vernooij wrote:
> > Add a HPSS call for ``Repository.iter_inventories``.
> I am a bit concerned that by adding verbs like this, with their own ad hoc
> record stream-like wire format, that we're growing the maintenance burden
> unnecessarily, and not reusing improvements everywhere we could.
>
> I'm glad this is using inventory-deltas. But if it's worth zlib compressing
> them here, shouldn't we do that for inventory-deltas from get_record_stream
> too?
It's a pretty big inventory delta in this case - for something like gcc it matters for the delta between null: and the initial revision that was requested. I haven't investigated whether it would help for record streams too, but I imagine it would have less of an effect.

> Also, could you implement this via the get_record_stream RPC with a new
> parameter that means “just inventories (rather than rev+inv+texts+chks+sigs
> for
> given keys)”?
>
> On one hand it might feel a bit ugly to make one RPC do so many things,
> get_record_stream, do so many things, but on the other I think it provides a
> useful pressure to keep the interface minimal and consistent (e.g. whether
> records are zlib-compressed).
>
> Adding this iter_inventories RPC might be the right thing (although putting
> “iter” in the name of an RPC feels weird to me, that's surely a property of
> the Python API rather than a property of the RPC), but I'd like hear what you
> think about the tradeoffs of doing that vs. extending get_record_stream.

With regard to the name: Most of the other calls seem to be named after the equivalent method in the client / server, that's why I went with Repository.iter_inventories. I'm not particularly tied to that name, something like "Repository.get_inventories" or "Repository.stream_inventories" seems reasonable to me too.

I'm not sure whether this should be part of get_record_stream. I have a hard time understanding the get_record_stream code as it is, so I'd rather not make it even more complex by adding more parameters - for example, get_record_streams seem to be dependent on the repository on-disk format to an extent - the inventory stream is not, as it's using inventory deltas. In other words, adding another verb was simpler, while keeping it all understandable for a mere mortal like me. :-)

Either way, I agree we should be reusing more code between the work I've done recently and the existing record stream
 calls. But it seems to me that would best be done by refactoring so they e.g. use the same code for sending a stream of blobs of indeterminate length, rather than by all being a part of the same verb.

What do you think?

Cheers,

Jelmer

Revision history for this message
Andrew Bennetts (spiv) wrote : Posted in a previous version of this proposal
Download full text (4.5 KiB)

Jelmer Vernooij wrote:

> > I'm glad this is using inventory-deltas. But if it's worth zlib compressing
> > them here, shouldn't we do that for inventory-deltas from get_record_stream
> > too?
> It's a pretty big inventory delta in this case - for something like gcc it
> matters for the delta between null: and the initial revision that was
> requested. I haven't investigated whether it would help for record streams
> too, but I imagine it would have less of an effect.

Well, I know that there are already cases that send deltas from null: —
something to do with stacking perhaps? So reusing this improvement globally
would be nice.

> > Also, could you implement this via the get_record_stream RPC with a new
> > parameter that means “just inventories (rather than rev+inv+texts+chks+sigs
> > for
> > given keys)”?
> >
> > On one hand it might feel a bit ugly to make one RPC do so many things,
> > get_record_stream, do so many things, but on the other I think it provides a
> > useful pressure to keep the interface minimal and consistent (e.g. whether
> > records are zlib-compressed).
> >
> > Adding this iter_inventories RPC might be the right thing (although putting
> > “iter” in the name of an RPC feels weird to me, that's surely a property of
> > the Python API rather than a property of the RPC), but I'd like hear what you
> > think about the tradeoffs of doing that vs. extending get_record_stream.
>
> With regard to the name: Most of the other calls seem to be named after the
> equivalent method in the client / server, that's why I went with
> Repository.iter_inventories. I'm not particularly tied to that name, something
> like "Repository.get_inventories" or "Repository.stream_inventories" seems
> reasonable to me too.

I think of using the name of the Python API as a good guide, not a strict rule.
Certainly there's no insert_stream_locked method on Repository…

“Repository.get_inventories” sounds fine to me.

> I'm not sure whether this should be part of get_record_stream. I have a hard
> time understanding the get_record_stream code as it is, so I'd rather not make
> it even more complex by adding more parameters - for example,
> get_record_streams seem to be dependent on the repository on-disk format to an
> extent - the inventory stream is not, as it's using inventory deltas. In other
> words, adding another verb was simpler, while keeping it all understandable
> for a mere mortal like me. :-)
>
> Either way, I agree we should be reusing more code between the work I've done
> recently and the existing record stream calls. But it seems to me that would
> best be done by refactoring so they e.g. use the same code for sending a
> stream of blobs of indeterminate length, rather than by all being a part of
> the same verb.

Well, the path to reuse we've developed *is* record streams. I'm quite willing
to believe that get_record_stream's parameters aren't a convenient way to
express all needs. But I'd really like it the thing that was returned by a new
verb was a true record stream — something you could pass directly to
insert_record_stream. The idea is that record streams should be the One True
Way we have to say “here is a stream of da...

Read more...

Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal
Download full text (6.7 KiB)

Am 05/12/11 11:31, schrieb Andrew Bennetts:
> Jelmer Vernooij wrote:
> …
>>> I'm glad this is using inventory-deltas. But if it's worth zlib compressing
>>> them here, shouldn't we do that for inventory-deltas from get_record_stream
>>> too?
>> It's a pretty big inventory delta in this case - for something like gcc it
>> matters for the delta between null: and the initial revision that was
>> requested. I haven't investigated whether it would help for record streams
>> too, but I imagine it would have less of an effect.
> Well, I know that there are already cases that send deltas from null: —
> something to do with stacking perhaps? So reusing this improvement globally
> would be nice.
I guess the best way to do this would be to add a zlib pack record kind
for network streams? I.e. a new entry in
NetworkRecordStream._kind_factory, and something equivalent on the
server side?
>>> Also, could you implement this via the get_record_stream RPC with a new
>>> parameter that means “just inventories (rather than rev+inv+texts+chks+sigs
>>> for
>>> given keys)”?
>>>
>>> On one hand it might feel a bit ugly to make one RPC do so many things,
>>> get_record_stream, do so many things, but on the other I think it provides a
>>> useful pressure to keep the interface minimal and consistent (e.g. whether
>>> records are zlib-compressed).
>>>
>>> Adding this iter_inventories RPC might be the right thing (although putting
>>> “iter” in the name of an RPC feels weird to me, that's surely a property of
>>> the Python API rather than a property of the RPC), but I'd like hear what you
>>> think about the tradeoffs of doing that vs. extending get_record_stream.
>> With regard to the name: Most of the other calls seem to be named after the
>> equivalent method in the client / server, that's why I went with
>> Repository.iter_inventories. I'm not particularly tied to that name, something
>> like "Repository.get_inventories" or "Repository.stream_inventories" seems
>> reasonable to me too.
> I think of using the name of the Python API as a good guide, not a strict rule.
> Certainly there's no insert_stream_locked method on Repository…
>
> “Repository.get_inventories” sounds fine to me.
I've renamed it.
>
>> I'm not sure whether this should be part of get_record_stream. I have a hard
>> time understanding the get_record_stream code as it is, so I'd rather not make
>> it even more complex by adding more parameters - for example,
>> get_record_streams seem to be dependent on the repository on-disk format to an
>> extent - the inventory stream is not, as it's using inventory deltas. In other
>> words, adding another verb was simpler, while keeping it all understandable
>> for a mere mortal like me. :-)
>>
>> Either way, I agree we should be reusing more code between the work I've done
>> recently and the existing record stream calls. But it seems to me that would
>> best be done by refactoring so they e.g. use the same code for sending a
>> stream of blobs of indeterminate length, rather than by all being a part of
>> the same verb.
> Well, the path to reuse we've developed *is* record streams. I'm quite willing
> to believe that get_record_stream's parameters aren'...

Read more...

Revision history for this message
Vincent Ladeuil (vila) wrote :

Based on the tests and the decrease of roundtrips: approved !

16 + def _vfs_checkout_metadir(self):
17 + self._ensure_real()
18 + return self._real_bzrdir.checkout_metadir()
19 +
20 + def checkout_metadir(self):
21 + medium = self._client._medium
22 + if medium._is_remote_before((2, 5)):
23 + return self._vfs_checkout_metadir()

_vfs_checkout_metadir -> checkout_metadir -> _vfs_checkout_metadir ?

Is that a loop or do I miss something obvious ?

Up to you to wait for spiv's review.

review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Am 13/12/11 11:14, schrieb Vincent Ladeuil:
> Review: Approve
>
> Based on the tests and the decrease of roundtrips: approved !
>
> 16 + def _vfs_checkout_metadir(self):
> 17 + self._ensure_real()
> 18 + return self._real_bzrdir.checkout_metadir()
> 19 +
> 20 + def checkout_metadir(self):
> 21 + medium = self._client._medium
> 22 + if medium._is_remote_before((2, 5)):
> 23 + return self._vfs_checkout_metadir()
>
> _vfs_checkout_metadir -> checkout_metadir -> _vfs_checkout_metadir ?
It's not checkout_metadir, but _real_bzrdir.checkout_metadir. :-)
>
> Is that a loop or do I miss something obvious ?
>
> Up to you to wait for spiv's review.
Cheers,

Jelmer

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

> Up to you to wait for spiv's review.
Thanks.

I haven't heard from spiv in the last week, and I'd really like to get this landed so it gets some decent testing for 2.5.0. My recent changes should have addressed his concerns regarding the use of record streams, and I'm happy to tweak whatever is necessary later based on post-commit reviews.

review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/remote.py'
--- bzrlib/remote.py 2011-12-14 12:33:04 +0000
+++ bzrlib/remote.py 2011-12-14 21:28:31 +0000
@@ -27,6 +27,7 @@
27 errors,27 errors,
28 gpg,28 gpg,
29 graph,29 graph,
30 inventory_delta,
30 lock,31 lock,
31 lockdir,32 lockdir,
32 osutils,33 osutils,
@@ -1830,9 +1831,125 @@
1830 def get_inventory(self, revision_id):1831 def get_inventory(self, revision_id):
1831 return list(self.iter_inventories([revision_id]))[0]1832 return list(self.iter_inventories([revision_id]))[0]
18321833
1834 def _iter_inventories_rpc(self, revision_ids, ordering):
1835 if ordering is None:
1836 ordering = 'unordered'
1837 path = self.bzrdir._path_for_remote_call(self._client)
1838 body = "\n".join(revision_ids)
1839 response_tuple, response_handler = (
1840 self._call_with_body_bytes_expecting_body(
1841 "VersionedFileRepository.get_inventories",
1842 (path, ordering), body))
1843 if response_tuple[0] != "ok":
1844 raise errors.UnexpectedSmartServerResponse(response_tuple)
1845 deserializer = inventory_delta.InventoryDeltaDeserializer()
1846 byte_stream = response_handler.read_streamed_body()
1847 decoded = smart_repo._byte_stream_to_stream(byte_stream)
1848 if decoded is None:
1849 # no results whatsoever
1850 return
1851 src_format, stream = decoded
1852 if src_format.network_name() != self._format.network_name():
1853 raise AssertionError(
1854 "Mismatched RemoteRepository and stream src %r, %r" % (
1855 src_format.network_name(), self._format.network_name()))
1856 # ignore the src format, it's not really relevant
1857 prev_inv = Inventory(root_id=None,
1858 revision_id=_mod_revision.NULL_REVISION)
1859 # there should be just one substream, with inventory deltas
1860 substream_kind, substream = stream.next()
1861 if substream_kind != "inventory-deltas":
1862 raise AssertionError(
1863 "Unexpected stream %r received" % substream_kind)
1864 for record in substream:
1865 (parent_id, new_id, versioned_root, tree_references, invdelta) = (
1866 deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1867 if parent_id != prev_inv.revision_id:
1868 raise AssertionError("invalid base %r != %r" % (parent_id,
1869 prev_inv.revision_id))
1870 inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1871 yield inv, inv.revision_id
1872 prev_inv = inv
1873
1874 def _iter_inventories_vfs(self, revision_ids, ordering=None):
1875 self._ensure_real()
1876 return self._real_repository._iter_inventories(revision_ids, ordering)
1877
1833 def iter_inventories(self, revision_ids, ordering=None):1878 def iter_inventories(self, revision_ids, ordering=None):
1834 self._ensure_real()1879 """Get many inventories by revision_ids.
1835 return self._real_repository.iter_inventories(revision_ids, ordering)1880
1881 This will buffer some or all of the texts used in constructing the
1882 inventories in memory, but will only parse a single inventory at a
1883 time.
1884
1885 :param revision_ids: The expected revision ids of the inventories.
1886 :param ordering: optional ordering, e.g. 'topological'. If not
1887 specified, the order of revision_ids will be preserved (by
1888 buffering if necessary).
1889 :return: An iterator of inventories.
1890 """
1891 if ((None in revision_ids)
1892 or (_mod_revision.NULL_REVISION in revision_ids)):
1893 raise ValueError('cannot get null revision inventory')
1894 for inv, revid in self._iter_inventories(revision_ids, ordering):
1895 if inv is None:
1896 raise errors.NoSuchRevision(self, revid)
1897 yield inv
1898
1899 def _iter_inventories(self, revision_ids, ordering=None):
1900 if len(revision_ids) == 0:
1901 return
1902 missing = set(revision_ids)
1903 if ordering is None:
1904 order_as_requested = True
1905 invs = {}
1906 order = list(revision_ids)
1907 order.reverse()
1908 next_revid = order.pop()
1909 else:
1910 order_as_requested = False
1911 if ordering != 'unordered' and self._fallback_repositories:
1912 raise ValueError('unsupported ordering %r' % ordering)
1913 iter_inv_fns = [self._iter_inventories_rpc] + [
1914 fallback._iter_inventories for fallback in
1915 self._fallback_repositories]
1916 try:
1917 for iter_inv in iter_inv_fns:
1918 request = [revid for revid in revision_ids if revid in missing]
1919 for inv, revid in iter_inv(request, ordering):
1920 if inv is None:
1921 continue
1922 missing.remove(inv.revision_id)
1923 if ordering != 'unordered':
1924 invs[revid] = inv
1925 else:
1926 yield inv, revid
1927 if order_as_requested:
1928 # Yield as many results as we can while preserving order.
1929 while next_revid in invs:
1930 inv = invs.pop(next_revid)
1931 yield inv, inv.revision_id
1932 try:
1933 next_revid = order.pop()
1934 except IndexError:
1935 # We still want to fully consume the stream, just
1936 # in case it is not actually finished at this point
1937 next_revid = None
1938 break
1939 except errors.UnknownSmartMethod:
1940 for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
1941 yield inv, revid
1942 return
1943 # Report missing
1944 if order_as_requested:
1945 if next_revid is not None:
1946 yield None, next_revid
1947 while order:
1948 revid = order.pop()
1949 yield invs.get(revid), revid
1950 else:
1951 while missing:
1952 yield None, missing.pop()
18361953
1837 @needs_read_lock1954 @needs_read_lock
1838 def get_revision(self, revision_id):1955 def get_revision(self, revision_id):
@@ -2191,6 +2308,8 @@
21912308
2192 @needs_read_lock2309 @needs_read_lock
2193 def _get_inventory_xml(self, revision_id):2310 def _get_inventory_xml(self, revision_id):
2311 # This call is used by older working tree formats,
2312 # which stored a serialized basis inventory.
2194 self._ensure_real()2313 self._ensure_real()
2195 return self._real_repository._get_inventory_xml(revision_id)2314 return self._real_repository._get_inventory_xml(revision_id)
21962315
@@ -2235,11 +2354,58 @@
2235 revids.update(set(fallback.all_revision_ids()))2354 revids.update(set(fallback.all_revision_ids()))
2236 return list(revids)2355 return list(revids)
22372356
2357 def _filtered_revision_trees(self, revision_ids, file_ids):
2358 """Return Tree for a revision on this branch with only some files.
2359
2360 :param revision_ids: a sequence of revision-ids;
2361 a revision-id may not be None or 'null:'
2362 :param file_ids: if not None, the result is filtered
2363 so that only those file-ids, their parents and their
2364 children are included.
2365 """
2366 inventories = self.iter_inventories(revision_ids)
2367 for inv in inventories:
2368 # Should we introduce a FilteredRevisionTree class rather
2369 # than pre-filter the inventory here?
2370 filtered_inv = inv.filter(file_ids)
2371 yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2372
2238 @needs_read_lock2373 @needs_read_lock
2239 def get_deltas_for_revisions(self, revisions, specific_fileids=None):2374 def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2240 self._ensure_real()2375 medium = self._client._medium
2241 return self._real_repository.get_deltas_for_revisions(revisions,2376 if medium._is_remote_before((1, 2)):
2242 specific_fileids=specific_fileids)2377 self._ensure_real()
2378 for delta in self._real_repository.get_deltas_for_revisions(
2379 revisions, specific_fileids):
2380 yield delta
2381 return
2382 # Get the revision-ids of interest
2383 required_trees = set()
2384 for revision in revisions:
2385 required_trees.add(revision.revision_id)
2386 required_trees.update(revision.parent_ids[:1])
2387
2388 # Get the matching filtered trees. Note that it's more
2389 # efficient to pass filtered trees to changes_from() rather
2390 # than doing the filtering afterwards. changes_from() could
2391 # arguably do the filtering itself but it's path-based, not
2392 # file-id based, so filtering before or afterwards is
2393 # currently easier.
2394 if specific_fileids is None:
2395 trees = dict((t.get_revision_id(), t) for
2396 t in self.revision_trees(required_trees))
2397 else:
2398 trees = dict((t.get_revision_id(), t) for
2399 t in self._filtered_revision_trees(required_trees,
2400 specific_fileids))
2401
2402 # Calculate the deltas
2403 for revision in revisions:
2404 if not revision.parent_ids:
2405 old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2406 else:
2407 old_tree = trees[revision.parent_ids[0]]
2408 yield trees[revision.revision_id].changes_from(old_tree)
22432409
2244 @needs_read_lock2410 @needs_read_lock
2245 def get_revision_delta(self, revision_id, specific_fileids=None):2411 def get_revision_delta(self, revision_id, specific_fileids=None):
22462412
=== modified file 'bzrlib/smart/repository.py'
--- bzrlib/smart/repository.py 2011-12-11 13:30:10 +0000
+++ bzrlib/smart/repository.py 2011-12-14 21:28:31 +0000
@@ -14,7 +14,7 @@
14# along with this program; if not, write to the Free Software14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1616
17"""Server-side repository related request implmentations."""17"""Server-side repository related request implementations."""
1818
19import bz219import bz2
20import os20import os
@@ -28,6 +28,8 @@
28 bencode,28 bencode,
29 errors,29 errors,
30 estimate_compressed_size,30 estimate_compressed_size,
31 inventory as _mod_inventory,
32 inventory_delta,
31 osutils,33 osutils,
32 pack,34 pack,
33 trace,35 trace,
@@ -43,6 +45,7 @@
43from bzrlib.repository import _strip_NULL_ghosts, network_format_registry45from bzrlib.repository import _strip_NULL_ghosts, network_format_registry
44from bzrlib import revision as _mod_revision46from bzrlib import revision as _mod_revision
45from bzrlib.versionedfile import (47from bzrlib.versionedfile import (
48 ChunkedContentFactory,
46 NetworkRecordStream,49 NetworkRecordStream,
47 record_to_fulltext_bytes,50 record_to_fulltext_bytes,
48 )51 )
@@ -1243,3 +1246,57 @@
1243 yield zlib.compress(record.get_bytes_as('fulltext'))1246 yield zlib.compress(record.get_bytes_as('fulltext'))
1244 finally:1247 finally:
1245 self._repository.unlock()1248 self._repository.unlock()
1249
1250
1251class SmartServerRepositoryGetInventories(SmartServerRepositoryRequest):
1252 """Get the inventory deltas for a set of revision ids.
1253
1254 This accepts a list of revision ids, and then sends a chain
1255 of deltas for the inventories of those revisions. The first
1256 revision will be empty.
1257
1258 The server writes back zlibbed serialized inventory deltas,
1259 in the ordering specified. The base for each delta is the
1260 inventory generated by the previous delta.
1261
1262 New in 2.5.
1263 """
1264
1265 def _inventory_delta_stream(self, repository, ordering, revids):
1266 prev_inv = _mod_inventory.Inventory(root_id=None,
1267 revision_id=_mod_revision.NULL_REVISION)
1268 serializer = inventory_delta.InventoryDeltaSerializer(
1269 repository.supports_rich_root(),
1270 repository._format.supports_tree_reference)
1271 repository.lock_read()
1272 try:
1273 for inv, revid in repository._iter_inventories(revids, ordering):
1274 if inv is None:
1275 continue
1276 inv_delta = inv._make_delta(prev_inv)
1277 lines = serializer.delta_to_lines(
1278 prev_inv.revision_id, inv.revision_id, inv_delta)
1279 yield ChunkedContentFactory(inv.revision_id, None, None, lines)
1280 prev_inv = inv
1281 finally:
1282 repository.unlock()
1283
1284 def body_stream(self, repository, ordering, revids):
1285 substream = self._inventory_delta_stream(repository,
1286 ordering, revids)
1287 return _stream_to_byte_stream([('inventory-deltas', substream)],
1288 repository._format)
1289
1290 def do_body(self, body_bytes):
1291 return SuccessfulSmartServerResponse(('ok', ),
1292 body_stream=self.body_stream(self._repository, self._ordering,
1293 body_bytes.splitlines()))
1294
1295 def do_repository_request(self, repository, ordering):
1296 if ordering == 'unordered':
1297 # inventory deltas for a topologically sorted stream
1298 # are likely to be smaller
1299 ordering = 'topological'
1300 self._ordering = ordering
1301 # Signal that we want a body
1302 return None
12461303
=== modified file 'bzrlib/smart/request.py'
--- bzrlib/smart/request.py 2011-12-14 12:20:36 +0000
+++ bzrlib/smart/request.py 2011-12-14 21:28:31 +0000
@@ -751,15 +751,18 @@
751 'Repository.check_write_group', 'bzrlib.smart.repository',751 'Repository.check_write_group', 'bzrlib.smart.repository',
752 'SmartServerRepositoryCheckWriteGroup', info='read')752 'SmartServerRepositoryCheckWriteGroup', info='read')
753request_handlers.register_lazy(753request_handlers.register_lazy(
754 'VersionedFileRepository.get_serializer_format', 'bzrlib.smart.repository',
755 'SmartServerRepositoryGetSerializerFormat', info='read')
756request_handlers.register_lazy(
757 'Repository.reconcile', 'bzrlib.smart.repository',754 'Repository.reconcile', 'bzrlib.smart.repository',
758 'SmartServerRepositoryReconcile', info='idem')755 'SmartServerRepositoryReconcile', info='idem')
759request_handlers.register_lazy(756request_handlers.register_lazy(
760 'Repository.tarball', 'bzrlib.smart.repository',757 'Repository.tarball', 'bzrlib.smart.repository',
761 'SmartServerRepositoryTarball', info='read')758 'SmartServerRepositoryTarball', info='read')
762request_handlers.register_lazy(759request_handlers.register_lazy(
760 'VersionedFileRepository.get_serializer_format', 'bzrlib.smart.repository',
761 'SmartServerRepositoryGetSerializerFormat', info='read')
762request_handlers.register_lazy(
763 'VersionedFileRepository.get_inventories', 'bzrlib.smart.repository',
764 'SmartServerRepositoryGetInventories', info='read')
765request_handlers.register_lazy(
763 'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest', info='semivfs')766 'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest', info='semivfs')
764request_handlers.register_lazy(767request_handlers.register_lazy(
765 'stat', 'bzrlib.smart.vfs', 'StatRequest', info='read')768 'stat', 'bzrlib.smart.vfs', 'StatRequest', info='read')
766769
=== modified file 'bzrlib/tests/blackbox/test_annotate.py'
--- bzrlib/tests/blackbox/test_annotate.py 2011-12-14 12:15:44 +0000
+++ bzrlib/tests/blackbox/test_annotate.py 2011-12-14 21:28:31 +0000
@@ -326,6 +326,6 @@
326 # being too low. If rpc_count increases, more network roundtrips have326 # being too low. If rpc_count increases, more network roundtrips have
327 # become necessary for this use case. Please do not adjust this number327 # become necessary for this use case. Please do not adjust this number
328 # upwards without agreement from bzr's network support maintainers.328 # upwards without agreement from bzr's network support maintainers.
329 self.assertLength(19, self.hpss_calls)329 self.assertLength(15, self.hpss_calls)
330 self.expectFailure("annotate accesses inventories, which require VFS access",330 self.expectFailure("annotate accesses inventories, which require VFS access",
331 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)331 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
332332
=== modified file 'bzrlib/tests/blackbox/test_branch.py'
--- bzrlib/tests/blackbox/test_branch.py 2011-12-14 18:17:43 +0000
+++ bzrlib/tests/blackbox/test_branch.py 2011-12-14 21:28:31 +0000
@@ -483,7 +483,7 @@
483 # being too low. If rpc_count increases, more network roundtrips have483 # being too low. If rpc_count increases, more network roundtrips have
484 # become necessary for this use case. Please do not adjust this number484 # become necessary for this use case. Please do not adjust this number
485 # upwards without agreement from bzr's network support maintainers.485 # upwards without agreement from bzr's network support maintainers.
486 self.assertLength(40, self.hpss_calls)486 self.assertLength(33, self.hpss_calls)
487 self.expectFailure("branching to the same branch requires VFS access",487 self.expectFailure("branching to the same branch requires VFS access",
488 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)488 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
489489
@@ -521,8 +521,8 @@
521 # being too low. If rpc_count increases, more network roundtrips have521 # being too low. If rpc_count increases, more network roundtrips have
522 # become necessary for this use case. Please do not adjust this number522 # become necessary for this use case. Please do not adjust this number
523 # upwards without agreement from bzr's network support maintainers.523 # upwards without agreement from bzr's network support maintainers.
524 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
525 self.assertLength(15, self.hpss_calls)524 self.assertLength(15, self.hpss_calls)
525 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
526526
527 def test_branch_from_branch_with_tags(self):527 def test_branch_from_branch_with_tags(self):
528 self.setup_smart_server_with_call_log()528 self.setup_smart_server_with_call_log()
@@ -539,8 +539,8 @@
539 # being too low. If rpc_count increases, more network roundtrips have539 # being too low. If rpc_count increases, more network roundtrips have
540 # become necessary for this use case. Please do not adjust this number540 # become necessary for this use case. Please do not adjust this number
541 # upwards without agreement from bzr's network support maintainers.541 # upwards without agreement from bzr's network support maintainers.
542 self.assertLength(10, self.hpss_calls)
542 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)543 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
543 self.assertLength(10, self.hpss_calls)
544544
545 def test_branch_to_stacked_from_trivial_branch_streaming_acceptance(self):545 def test_branch_to_stacked_from_trivial_branch_streaming_acceptance(self):
546 self.setup_smart_server_with_call_log()546 self.setup_smart_server_with_call_log()
547547
=== modified file 'bzrlib/tests/blackbox/test_cat.py'
--- bzrlib/tests/blackbox/test_cat.py 2011-12-14 12:15:44 +0000
+++ bzrlib/tests/blackbox/test_cat.py 2011-12-14 21:28:31 +0000
@@ -239,6 +239,5 @@
239 # being too low. If rpc_count increases, more network roundtrips have239 # being too low. If rpc_count increases, more network roundtrips have
240 # become necessary for this use case. Please do not adjust this number240 # become necessary for this use case. Please do not adjust this number
241 # upwards without agreement from bzr's network support maintainers.241 # upwards without agreement from bzr's network support maintainers.
242 self.assertLength(16, self.hpss_calls)242 self.assertLength(9, self.hpss_calls)
243 self.expectFailure("cat still uses VFS calls",243 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
244 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
245244
=== modified file 'bzrlib/tests/blackbox/test_checkout.py'
--- bzrlib/tests/blackbox/test_checkout.py 2011-12-14 18:17:43 +0000
+++ bzrlib/tests/blackbox/test_checkout.py 2011-12-14 21:28:31 +0000
@@ -179,8 +179,7 @@
179 for count in range(9):179 for count in range(9):
180 t.commit(message='commit %d' % count)180 t.commit(message='commit %d' % count)
181 self.reset_smart_call_log()181 self.reset_smart_call_log()
182 out, err = self.run_bzr(['checkout', self.get_url('from'),182 out, err = self.run_bzr(['checkout', self.get_url('from'), 'target'])
183 'target'])
184 # This figure represent the amount of work to perform this use case. It183 # This figure represent the amount of work to perform this use case. It
185 # is entirely ok to reduce this number if a test fails due to rpc_count184 # is entirely ok to reduce this number if a test fails due to rpc_count
186 # being too low. If rpc_count increases, more network roundtrips have185 # being too low. If rpc_count increases, more network roundtrips have
@@ -202,9 +201,5 @@
202 # being too low. If rpc_count increases, more network roundtrips have201 # being too low. If rpc_count increases, more network roundtrips have
203 # become necessary for this use case. Please do not adjust this number202 # become necessary for this use case. Please do not adjust this number
204 # upwards without agreement from bzr's network support maintainers.203 # upwards without agreement from bzr's network support maintainers.
205 if len(self.hpss_calls) < 28 or len(self.hpss_calls) > 40:204 self.assertLength(15, self.hpss_calls)
206 self.fail(205 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
207 "Incorrect length: wanted between 28 and 40, got %d for %r" % (
208 len(self.hpss_calls), self.hpss_calls))
209 self.expectFailure("lightweight checkouts require VFS calls",
210 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
211206
=== modified file 'bzrlib/tests/blackbox/test_export.py'
--- bzrlib/tests/blackbox/test_export.py 2011-12-14 12:15:44 +0000
+++ bzrlib/tests/blackbox/test_export.py 2011-12-14 21:28:31 +0000
@@ -448,6 +448,5 @@
448 # being too low. If rpc_count increases, more network roundtrips have448 # being too low. If rpc_count increases, more network roundtrips have
449 # become necessary for this use case. Please do not adjust this number449 # become necessary for this use case. Please do not adjust this number
450 # upwards without agreement from bzr's network support maintainers.450 # upwards without agreement from bzr's network support maintainers.
451 self.assertLength(16, self.hpss_calls)451 self.assertLength(7, self.hpss_calls)
452 self.expectFailure("export requires inventory access which requires VFS",452 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
453 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
454453
=== modified file 'bzrlib/tests/blackbox/test_log.py'
--- bzrlib/tests/blackbox/test_log.py 2011-12-14 12:15:44 +0000
+++ bzrlib/tests/blackbox/test_log.py 2011-12-14 21:28:31 +0000
@@ -1086,9 +1086,8 @@
1086 # being too low. If rpc_count increases, more network roundtrips have1086 # being too low. If rpc_count increases, more network roundtrips have
1087 # become necessary for this use case. Please do not adjust this number1087 # become necessary for this use case. Please do not adjust this number
1088 # upwards without agreement from bzr's network support maintainers.1088 # upwards without agreement from bzr's network support maintainers.
1089 self.assertLength(19, self.hpss_calls)1089 self.assertLength(11, self.hpss_calls)
1090 self.expectFailure("verbose log accesses inventories, which require VFS",1090 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
1091 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
10921091
1093 def test_per_file(self):1092 def test_per_file(self):
1094 self.setup_smart_server_with_call_log()1093 self.setup_smart_server_with_call_log()
@@ -1103,6 +1102,5 @@
1103 # being too low. If rpc_count increases, more network roundtrips have1102 # being too low. If rpc_count increases, more network roundtrips have
1104 # become necessary for this use case. Please do not adjust this number1103 # become necessary for this use case. Please do not adjust this number
1105 # upwards without agreement from bzr's network support maintainers.1104 # upwards without agreement from bzr's network support maintainers.
1106 self.assertLength(21, self.hpss_calls)1105 self.assertLength(15, self.hpss_calls)
1107 self.expectFailure("per-file graph access requires VFS",1106 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
1108 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
11091107
=== modified file 'bzrlib/tests/blackbox/test_ls.py'
--- bzrlib/tests/blackbox/test_ls.py 2011-12-14 12:15:44 +0000
+++ bzrlib/tests/blackbox/test_ls.py 2011-12-14 21:28:31 +0000
@@ -262,6 +262,5 @@
262 # being too low. If rpc_count increases, more network roundtrips have262 # being too low. If rpc_count increases, more network roundtrips have
263 # become necessary for this use case. Please do not adjust this number263 # become necessary for this use case. Please do not adjust this number
264 # upwards without agreement from bzr's network support maintainers.264 # upwards without agreement from bzr's network support maintainers.
265 self.assertLength(15, self.hpss_calls)265 self.assertLength(6, self.hpss_calls)
266 self.expectFailure("inventories can only be accessed over VFS",266 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
267 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
268267
=== modified file 'bzrlib/tests/blackbox/test_sign_my_commits.py'
--- bzrlib/tests/blackbox/test_sign_my_commits.py 2011-12-14 18:17:43 +0000
+++ bzrlib/tests/blackbox/test_sign_my_commits.py 2011-12-14 21:28:31 +0000
@@ -165,9 +165,8 @@
165 # being too low. If rpc_count increases, more network roundtrips have165 # being too low. If rpc_count increases, more network roundtrips have
166 # become necessary for this use case. Please do not adjust this number166 # become necessary for this use case. Please do not adjust this number
167 # upwards without agreement from bzr's network support maintainers.167 # upwards without agreement from bzr's network support maintainers.
168 self.assertLength(51, self.hpss_calls)168 self.assertLength(15, self.hpss_calls)
169 self.expectFailure("signing commits requires VFS access",169 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
170 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
171170
172 def test_verify_commits(self):171 def test_verify_commits(self):
173 self.setup_smart_server_with_call_log()172 self.setup_smart_server_with_call_log()
@@ -185,12 +184,5 @@
185 # being too low. If rpc_count increases, more network roundtrips have184 # being too low. If rpc_count increases, more network roundtrips have
186 # become necessary for this use case. Please do not adjust this number185 # become necessary for this use case. Please do not adjust this number
187 # upwards without agreement from bzr's network support maintainers.186 # upwards without agreement from bzr's network support maintainers.
188187 self.assertLength(10, self.hpss_calls)
189 # The number of readv requests seems to vary depending on the generated188 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
190 # repository and how well it compresses, so allow for a bit of
191 # variation:
192 if len(self.hpss_calls) not in (18, 19):
193 self.fail("Incorrect length: wanted 18 or 19, got %d for %r" % (
194 len(self.hpss_calls), self.hpss_calls))
195 self.expectFailure("verifying commits requires VFS access",
196 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
197189
=== modified file 'bzrlib/tests/per_interbranch/test_push.py'
--- bzrlib/tests/per_interbranch/test_push.py 2011-10-15 01:09:01 +0000
+++ bzrlib/tests/per_interbranch/test_push.py 2011-12-14 21:28:31 +0000
@@ -279,10 +279,10 @@
279 # remote graph any further.279 # remote graph any further.
280 bzr_core_trace = Equals(280 bzr_core_trace = Equals(
281 ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',281 ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',
282 'get', 'Branch.set_last_revision_info', 'Branch.unlock'])282 'Branch.set_last_revision_info', 'Branch.unlock'])
283 bzr_loom_trace = Equals(283 bzr_loom_trace = Equals(
284 ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',284 ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',
285 'get', 'Branch.set_last_revision_info', 'get', 'Branch.unlock'])285 'Branch.set_last_revision_info', 'get', 'Branch.unlock'])
286 self.assertThat(calls_after_insert_stream,286 self.assertThat(calls_after_insert_stream,
287 MatchesAny(bzr_core_trace, bzr_loom_trace))287 MatchesAny(bzr_core_trace, bzr_loom_trace))
288288
289289
=== modified file 'bzrlib/tests/per_repository_chk/test_supported.py'
--- bzrlib/tests/per_repository_chk/test_supported.py 2009-09-09 18:52:56 +0000
+++ bzrlib/tests/per_repository_chk/test_supported.py 2011-12-14 21:28:31 +0000
@@ -22,7 +22,9 @@
22 osutils,22 osutils,
23 repository,23 repository,
24 )24 )
25from bzrlib.remote import RemoteRepository
25from bzrlib.versionedfile import VersionedFiles26from bzrlib.versionedfile import VersionedFiles
27from bzrlib.tests import TestNotApplicable
26from bzrlib.tests.per_repository_chk import TestCaseWithRepositoryCHK28from bzrlib.tests.per_repository_chk import TestCaseWithRepositoryCHK
2729
2830
@@ -221,7 +223,9 @@
221 # check our setup: B-id and C-id should have identical chk root keys.223 # check our setup: B-id and C-id should have identical chk root keys.
222 inv_b = b.repository.get_inventory('B-id')224 inv_b = b.repository.get_inventory('B-id')
223 inv_c = b.repository.get_inventory('C-id')225 inv_c = b.repository.get_inventory('C-id')
224 self.assertEqual(inv_b.id_to_entry.key(), inv_c.id_to_entry.key())226 if not isinstance(repo, RemoteRepository):
227 # Remote repositories always return plain inventories
228 self.assertEqual(inv_b.id_to_entry.key(), inv_c.id_to_entry.key())
225 # Now, manually insert objects for a stacked repo with only revision229 # Now, manually insert objects for a stacked repo with only revision
226 # C-id:230 # C-id:
227 # We need ('revisions', 'C-id'), ('inventories', 'C-id'),231 # We need ('revisions', 'C-id'), ('inventories', 'C-id'),
@@ -250,6 +254,9 @@
250 for a parent inventory of a new revision is missing.254 for a parent inventory of a new revision is missing.
251 """255 """
252 repo = self.make_repository('damaged-repo')256 repo = self.make_repository('damaged-repo')
257 if isinstance(repo, RemoteRepository):
258 raise TestNotApplicable(
259 "Unable to obtain CHKInventory from remote repo")
253 b = self.make_branch_with_multiple_chk_nodes()260 b = self.make_branch_with_multiple_chk_nodes()
254 src_repo = b.repository261 src_repo = b.repository
255 src_repo.lock_read()262 src_repo.lock_read()
@@ -293,6 +300,9 @@
293 for a parent inventory of a new revision is missing.300 for a parent inventory of a new revision is missing.
294 """301 """
295 repo = self.make_repository('damaged-repo')302 repo = self.make_repository('damaged-repo')
303 if isinstance(repo, RemoteRepository):
304 raise TestNotApplicable(
305 "Unable to obtain CHKInventory from remote repo")
296 b = self.make_branch_with_multiple_chk_nodes()306 b = self.make_branch_with_multiple_chk_nodes()
297 b.lock_read()307 b.lock_read()
298 self.addCleanup(b.unlock)308 self.addCleanup(b.unlock)
299309
=== modified file 'bzrlib/tests/per_repository_vf/test_add_inventory_by_delta.py'
--- bzrlib/tests/per_repository_vf/test_add_inventory_by_delta.py 2011-05-02 22:39:15 +0000
+++ bzrlib/tests/per_repository_vf/test_add_inventory_by_delta.py 2011-12-14 21:28:31 +0000
@@ -109,4 +109,4 @@
109 else:109 else:
110 repo_delta.commit_write_group()110 repo_delta.commit_write_group()
111 self.assertEqual(add_validator, delta_validator)111 self.assertEqual(add_validator, delta_validator)
112 self.assertEqual(new_inv, inv)112 self.assertEqual(list(new_inv.iter_entries()), list(inv.iter_entries()))
113113
=== modified file 'bzrlib/tests/test_remote.py'
--- bzrlib/tests/test_remote.py 2011-12-14 12:20:36 +0000
+++ bzrlib/tests/test_remote.py 2011-12-14 21:28:31 +0000
@@ -69,6 +69,7 @@
69from bzrlib.smart.repository import (69from bzrlib.smart.repository import (
70 SmartServerRepositoryGetParentMap,70 SmartServerRepositoryGetParentMap,
71 SmartServerRepositoryGetStream_1_19,71 SmartServerRepositoryGetStream_1_19,
72 _stream_to_byte_stream,
72 )73 )
73from bzrlib.symbol_versioning import deprecated_in74from bzrlib.symbol_versioning import deprecated_in
74from bzrlib.tests import (75from bzrlib.tests import (
@@ -4233,3 +4234,43 @@
4233 'Repository.unlock', ('quack/', 'token', 'False'),4234 'Repository.unlock', ('quack/', 'token', 'False'),
4234 'success', ('ok', ))4235 'success', ('ok', ))
4235 repo.pack(['hinta', 'hintb'])4236 repo.pack(['hinta', 'hintb'])
4237
4238
4239class TestRepositoryIterInventories(TestRemoteRepository):
4240 """Test Repository.iter_inventories."""
4241
4242 def _serialize_inv_delta(self, old_name, new_name, delta):
4243 serializer = inventory_delta.InventoryDeltaSerializer(True, False)
4244 return "".join(serializer.delta_to_lines(old_name, new_name, delta))
4245
4246 def test_single_empty(self):
4247 transport_path = 'quack'
4248 repo, client = self.setup_fake_client_and_repository(transport_path)
4249 fmt = bzrdir.format_registry.get('2a')().repository_format
4250 repo._format = fmt
4251 stream = [('inventory-deltas', [
4252 versionedfile.FulltextContentFactory('somerevid', None, None,
4253 self._serialize_inv_delta('null:', 'somerevid', []))])]
4254 client.add_expected_call(
4255 'VersionedFileRepository.get_inventories', ('quack/', 'unordered'),
4256 'success', ('ok', ),
4257 _stream_to_byte_stream(stream, fmt))
4258 ret = list(repo.iter_inventories(["somerevid"]))
4259 self.assertLength(1, ret)
4260 inv = ret[0]
4261 self.assertEquals("somerevid", inv.revision_id)
4262
4263 def test_empty(self):
4264 transport_path = 'quack'
4265 repo, client = self.setup_fake_client_and_repository(transport_path)
4266 ret = list(repo.iter_inventories([]))
4267 self.assertEquals(ret, [])
4268
4269 def test_missing(self):
4270 transport_path = 'quack'
4271 repo, client = self.setup_fake_client_and_repository(transport_path)
4272 client.add_expected_call(
4273 'VersionedFileRepository.get_inventories', ('quack/', 'unordered'),
4274 'success', ('ok', ), iter([]))
4275 self.assertRaises(errors.NoSuchRevision, list, repo.iter_inventories(
4276 ["somerevid"]))
42364277
=== modified file 'bzrlib/tests/test_smart.py'
--- bzrlib/tests/test_smart.py 2011-12-14 12:20:36 +0000
+++ bzrlib/tests/test_smart.py 2011-12-14 21:28:31 +0000
@@ -32,6 +32,7 @@
32 bzrdir,32 bzrdir,
33 errors,33 errors,
34 gpg,34 gpg,
35 inventory_delta,
35 tests,36 tests,
36 transport,37 transport,
37 urlutils,38 urlutils,
@@ -2565,6 +2566,8 @@
2565 smart_repo.SmartServerRepositoryAbortWriteGroup)2566 smart_repo.SmartServerRepositoryAbortWriteGroup)
2566 self.assertHandlerEqual('VersionedFileRepository.get_serializer_format',2567 self.assertHandlerEqual('VersionedFileRepository.get_serializer_format',
2567 smart_repo.SmartServerRepositoryGetSerializerFormat)2568 smart_repo.SmartServerRepositoryGetSerializerFormat)
2569 self.assertHandlerEqual('VersionedFileRepository.get_inventories',
2570 smart_repo.SmartServerRepositoryGetInventories)
2568 self.assertHandlerEqual('Transport.is_readonly',2571 self.assertHandlerEqual('Transport.is_readonly',
2569 smart_req.SmartServerIsReadonly)2572 smart_req.SmartServerIsReadonly)
25702573
@@ -2630,3 +2633,48 @@
2630 smart_req.SuccessfulSmartServerResponse(('ok', ), ),2633 smart_req.SuccessfulSmartServerResponse(('ok', ), ),
2631 request.do_body(''))2634 request.do_body(''))
26322635
2636
2637class TestSmartServerRepositoryGetInventories(tests.TestCaseWithTransport):
2638
2639 def _get_serialized_inventory_delta(self, repository, base_revid, revid):
2640 base_inv = repository.revision_tree(base_revid).inventory
2641 inv = repository.revision_tree(revid).inventory
2642 inv_delta = inv._make_delta(base_inv)
2643 serializer = inventory_delta.InventoryDeltaSerializer(True, False)
2644 return "".join(serializer.delta_to_lines(base_revid, revid, inv_delta))
2645
2646 def test_single(self):
2647 backing = self.get_transport()
2648 request = smart_repo.SmartServerRepositoryGetInventories(backing)
2649 t = self.make_branch_and_tree('.', format='2a')
2650 self.addCleanup(t.lock_write().unlock)
2651 self.build_tree_contents([("file", "somecontents")])
2652 t.add(["file"], ["thefileid"])
2653 t.commit(rev_id='somerev', message="add file")
2654 self.assertIs(None, request.execute('', 'unordered'))
2655 response = request.do_body("somerev\n")
2656 self.assertTrue(response.is_successful())
2657 self.assertEquals(response.args, ("ok", ))
2658 stream = [('inventory-deltas', [
2659 versionedfile.FulltextContentFactory('somerev', None, None,
2660 self._get_serialized_inventory_delta(
2661 t.branch.repository, 'null:', 'somerev'))])]
2662 fmt = bzrdir.format_registry.get('2a')().repository_format
2663 self.assertEquals(
2664 "".join(response.body_stream),
2665 "".join(smart_repo._stream_to_byte_stream(stream, fmt)))
2666
2667 def test_empty(self):
2668 backing = self.get_transport()
2669 request = smart_repo.SmartServerRepositoryGetInventories(backing)
2670 t = self.make_branch_and_tree('.', format='2a')
2671 self.addCleanup(t.lock_write().unlock)
2672 self.build_tree_contents([("file", "somecontents")])
2673 t.add(["file"], ["thefileid"])
2674 t.commit(rev_id='somerev', message="add file")
2675 self.assertIs(None, request.execute('', 'unordered'))
2676 response = request.do_body("")
2677 self.assertTrue(response.is_successful())
2678 self.assertEquals(response.args, ("ok", ))
2679 self.assertEquals("".join(response.body_stream),
2680 "Bazaar pack format 1 (introduced in 0.18)\nB54\n\nBazaar repository format 2a (needs bzr 1.16 or later)\nE")
26332681
=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- doc/en/release-notes/bzr-2.5.txt 2011-12-14 20:39:58 +0000
+++ doc/en/release-notes/bzr-2.5.txt 2011-12-14 21:28:31 +0000
@@ -95,6 +95,10 @@
9595
96* New HPSS call ``BzrDir.checkout_metadir``. (Jelmer Vernooij, #894459)96* New HPSS call ``BzrDir.checkout_metadir``. (Jelmer Vernooij, #894459)
9797
98* New HPSS call ``VersionedFileRepository.get_inventories``,
99 speeding up various commands including ``bzr export``,
100 ``bzr checkout`` and ``bzr cat``. (Jelmer Vernooij, #608640)
101
98Testing102Testing
99*******103*******
100104
@@ -156,6 +160,9 @@
156* Plugins can now register additional "location aliases".160* Plugins can now register additional "location aliases".
157 (Jelmer Vernooij)161 (Jelmer Vernooij)
158162
163* ``bzr status`` no longer shows shelves if files are specified.
164 (Francis Devereux)
165
159* Revision specifiers will now only browse as much history as they166* Revision specifiers will now only browse as much history as they
160 need to, rather than grabbing the whole history unnecessarily in some167 need to, rather than grabbing the whole history unnecessarily in some
161 cases. (Jelmer Vernooij)168 cases. (Jelmer Vernooij)
@@ -281,8 +288,14 @@
281 ``Repository.get_revision_signature_text``.288 ``Repository.get_revision_signature_text``.
282 (Jelmer Vernooij)289 (Jelmer Vernooij)
283290
291* Add HPSS call for ``Branch.get_checkout_format``. (Jelmer Vernooij, #894459)
292
284* Add HPSS call for ``Repository.pack``. (Jelmer Vernooij, #894461)293* Add HPSS call for ``Repository.pack``. (Jelmer Vernooij, #894461)
285294
295* Add HPSS calls for ``Repository.iter_files_bytes``, speeding up
296 several commands including ``bzr export`` and ``bzr co --lightweight``.
297 (Jelmer Vernooij, #608640)
298
286* Custom HPSS error handlers can now be installed in the smart server client299* Custom HPSS error handlers can now be installed in the smart server client
287 using the ``error_translators`` and ``no_context_error_translators``300 using the ``error_translators`` and ``no_context_error_translators``
288 registries. (Jelmer Vernooij)301 registries. (Jelmer Vernooij)