Merge lp:~jelmer/bzr/hpss-get-inventories into lp:bzr
- hpss-get-inventories
- Merge into bzr.dev
Status: | Superseded |
---|---|
Proposed branch: | lp:~jelmer/bzr/hpss-get-inventories |
Merge into: | lp:bzr |
Prerequisite: | lp:~jelmer/bzr/hpss-_get-checkout-format |
Diff against target: |
969 lines (+466/-182) 17 files modified
bzrlib/remote.py (+207/-50) bzrlib/smart/branch.py (+0/-19) bzrlib/smart/bzrdir.py (+19/-0) 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 (+1/-1) 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 (+3/-10) bzrlib/tests/per_interbranch/test_push.py (+2/-2) bzrlib/tests/test_remote.py (+78/-41) bzrlib/tests/test_smart.py (+68/-31) doc/en/release-notes/bzr-2.5.txt (+10/-0) |
To merge this branch: | bzr merge lp:~jelmer/bzr/hpss-get-inventories |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vincent Ladeuil | Approve | ||
Review via email: mp+83837@code.launchpad.net |
This proposal has been superseded by a proposal from 2011-12-11.
Commit message
Add HPSS call for ``Repository.
Description of the change
Add a HPSS call for ``Repository.
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.
In the process I removed the HistoryMissing exception that was thrown in the private Repository.
Repository.
Jelmer Vernooij (jelmer) wrote : | # |
Jelmer Vernooij (jelmer) wrote : | # |
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.
Andrew Bennetts (spiv) wrote : | # |
Jelmer Vernooij wrote:
> Add a HPSS call for ``Repository.
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+
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.
Martin Pool (mbp) wrote : | # |
On 30 November 2011 09:37, Andrew Bennetts
<email address hidden> wrote:
> Jelmer Vernooij wrote:
>> Add a HPSS call for ``Repository.
>
> 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
Vincent Ladeuil (vila) wrote : | # |
>> 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.
Jelmer Vernooij (jelmer) wrote : | # |
Hi Andrew,
> Jelmer Vernooij wrote:
> > Add a HPSS call for ``Repository.
> 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+
> 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.
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
Andrew Bennetts (spiv) wrote : | # |
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+
> > 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.
> like "Repository.
> 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_
“Repository.
> 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_
Way we have to say “here is a stream of da...
Jelmer Vernooij (jelmer) wrote : | # |
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
NetworkRecordSt
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+
>>> 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.
>> like "Repository.
>> 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_
>
> “Repository.
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'...
Preview Diff
1 | === modified file 'bzrlib/remote.py' |
2 | --- bzrlib/remote.py 2011-12-11 16:36:30 +0000 |
3 | +++ bzrlib/remote.py 2011-12-11 16:36:30 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | errors, |
6 | gpg, |
7 | graph, |
8 | + inventory_delta, |
9 | lock, |
10 | lockdir, |
11 | osutils, |
12 | @@ -490,6 +491,45 @@ |
13 | self._next_open_branch_result = None |
14 | return _mod_bzrdir.BzrDir.break_lock(self) |
15 | |
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() |
24 | + path = self._path_for_remote_call(self._client) |
25 | + try: |
26 | + response = self._client.call('BzrDir.checkout_metadir', |
27 | + path) |
28 | + except errors.UnknownSmartMethod: |
29 | + medium._remember_remote_is_before((2, 5)) |
30 | + return self._vfs_checkout_metadir() |
31 | + if len(response) != 3: |
32 | + raise errors.UnexpectedSmartServerResponse(response) |
33 | + control_name, repo_name, branch_name = response |
34 | + try: |
35 | + format = controldir.network_format_registry.get(control_name) |
36 | + except KeyError: |
37 | + raise errors.UnknownFormatError(kind='control', format=control_name) |
38 | + if repo_name: |
39 | + try: |
40 | + repo_format = _mod_repository.network_format_registry.get( |
41 | + repo_name) |
42 | + except KeyError: |
43 | + raise errors.UnknownFormatError(kind='repository', |
44 | + format=repo_name) |
45 | + format.repository_format = repo_format |
46 | + if branch_name: |
47 | + try: |
48 | + format.set_branch_format( |
49 | + branch.network_format_registry.get(branch_name)) |
50 | + except KeyError: |
51 | + raise errors.UnknownFormatError(kind='branch', |
52 | + format=branch_name) |
53 | + return format |
54 | + |
55 | def _vfs_cloning_metadir(self, require_stacking=False): |
56 | self._ensure_real() |
57 | return self._real_bzrdir.cloning_metadir( |
58 | @@ -1788,9 +1828,122 @@ |
59 | def get_inventory(self, revision_id): |
60 | return list(self.iter_inventories([revision_id]))[0] |
61 | |
62 | + def _iter_inventories_rpc(self, revision_ids, ordering): |
63 | + if ordering is None: |
64 | + ordering = 'unordered' |
65 | + path = self.bzrdir._path_for_remote_call(self._client) |
66 | + body = "\n".join(revision_ids) |
67 | + response_tuple, response_handler = ( |
68 | + self._call_with_body_bytes_expecting_body( |
69 | + "VersionedFileRepository.get_inventories", |
70 | + (path, ordering), body)) |
71 | + if response_tuple[0] != "ok": |
72 | + raise errors.UnexpectedSmartServerResponse(response_tuple) |
73 | + deserializer = inventory_delta.InventoryDeltaDeserializer() |
74 | + byte_stream = response_handler.read_streamed_body() |
75 | + decoded = smart_repo._byte_stream_to_stream(byte_stream) |
76 | + if decoded is None: |
77 | + # no results whatsoever |
78 | + return |
79 | + src_format, stream = decoded |
80 | + if src_format.network_name() != self._format.network_name(): |
81 | + raise AssertionError( |
82 | + "Mismatched RemoteRepository and stream src %r, %r" % ( |
83 | + src_format.network_name(), self._format.network_name())) |
84 | + # ignore the src format, it's not really relevant |
85 | + prev_inv = Inventory(root_id=None, |
86 | + revision_id=_mod_revision.NULL_REVISION) |
87 | + # there should be just one substream, with inventory deltas |
88 | + substream_kind, substream = stream.next() |
89 | + for record in substream: |
90 | + (parent_id, new_id, versioned_root, tree_references, invdelta) = ( |
91 | + deserializer.parse_text_bytes(record.get_bytes_as("fulltext"))) |
92 | + if parent_id != prev_inv.revision_id: |
93 | + raise AssertionError("invalid base %r != %r" % (parent_id, |
94 | + prev_inv.revision_id)) |
95 | + inv = prev_inv.create_by_apply_delta(invdelta, new_id) |
96 | + yield inv, inv.revision_id |
97 | + prev_inv = inv |
98 | + |
99 | + def _iter_inventories_vfs(self, revision_ids, ordering=None): |
100 | + self._ensure_real() |
101 | + return self._real_repository._iter_inventories(revision_ids, ordering) |
102 | + |
103 | def iter_inventories(self, revision_ids, ordering=None): |
104 | - self._ensure_real() |
105 | - return self._real_repository.iter_inventories(revision_ids, ordering) |
106 | + """Get many inventories by revision_ids. |
107 | + |
108 | + This will buffer some or all of the texts used in constructing the |
109 | + inventories in memory, but will only parse a single inventory at a |
110 | + time. |
111 | + |
112 | + :param revision_ids: The expected revision ids of the inventories. |
113 | + :param ordering: optional ordering, e.g. 'topological'. If not |
114 | + specified, the order of revision_ids will be preserved (by |
115 | + buffering if necessary). |
116 | + :return: An iterator of inventories. |
117 | + """ |
118 | + if ((None in revision_ids) |
119 | + or (_mod_revision.NULL_REVISION in revision_ids)): |
120 | + raise ValueError('cannot get null revision inventory') |
121 | + for inv, revid in self._iter_inventories(revision_ids, ordering): |
122 | + if inv is None: |
123 | + raise errors.NoSuchRevision(self, revid) |
124 | + yield inv |
125 | + |
126 | + def _iter_inventories(self, revision_ids, ordering=None): |
127 | + if len(revision_ids) == 0: |
128 | + return |
129 | + missing = set(revision_ids) |
130 | + if ordering is None: |
131 | + order_as_requested = True |
132 | + invs = {} |
133 | + order = list(revision_ids) |
134 | + order.reverse() |
135 | + next_revid = order.pop() |
136 | + else: |
137 | + order_as_requested = False |
138 | + if ordering != 'unordered' and self._fallback_repositories: |
139 | + raise ValueError('unsupported ordering %r' % ordering) |
140 | + iter_inv_fns = [self._iter_inventories_rpc] + [ |
141 | + fallback._iter_inventories for fallback in |
142 | + self._fallback_repositories] |
143 | + try: |
144 | + for iter_inv in iter_inv_fns: |
145 | + request = [revid for revid in revision_ids if revid in missing] |
146 | + for inv, revid in iter_inv(request, ordering): |
147 | + if inv is None: |
148 | + continue |
149 | + missing.remove(inv.revision_id) |
150 | + if ordering != 'unordered': |
151 | + invs[revid] = inv |
152 | + else: |
153 | + yield inv, revid |
154 | + if order_as_requested: |
155 | + # Yield as many results as we can while preserving order. |
156 | + while next_revid in invs: |
157 | + inv = invs.pop(next_revid) |
158 | + yield inv, inv.revision_id |
159 | + try: |
160 | + next_revid = order.pop() |
161 | + except IndexError: |
162 | + # We still want to fully consume the stream, just |
163 | + # in case it is not actually finished at this point |
164 | + next_revid = None |
165 | + break |
166 | + except errors.UnknownSmartMethod: |
167 | + for inv, revid in self._iter_inventories_vfs(revision_ids, ordering): |
168 | + yield inv, revid |
169 | + return |
170 | + # Report missing |
171 | + if order_as_requested: |
172 | + if next_revid is not None: |
173 | + yield None, next_revid |
174 | + while order: |
175 | + revid = order.pop() |
176 | + yield invs.get(revid), revid |
177 | + else: |
178 | + while missing: |
179 | + yield None, missing.pop() |
180 | |
181 | @needs_read_lock |
182 | def get_revision(self, revision_id): |
183 | @@ -2149,6 +2302,8 @@ |
184 | |
185 | @needs_read_lock |
186 | def _get_inventory_xml(self, revision_id): |
187 | + # This call is used by older working tree formats, |
188 | + # which stored a serialized basis inventory. |
189 | self._ensure_real() |
190 | return self._real_repository._get_inventory_xml(revision_id) |
191 | |
192 | @@ -2171,11 +2326,58 @@ |
193 | revids.update(set(fallback.all_revision_ids())) |
194 | return list(revids) |
195 | |
196 | + def _filtered_revision_trees(self, revision_ids, file_ids): |
197 | + """Return Tree for a revision on this branch with only some files. |
198 | + |
199 | + :param revision_ids: a sequence of revision-ids; |
200 | + a revision-id may not be None or 'null:' |
201 | + :param file_ids: if not None, the result is filtered |
202 | + so that only those file-ids, their parents and their |
203 | + children are included. |
204 | + """ |
205 | + inventories = self.iter_inventories(revision_ids) |
206 | + for inv in inventories: |
207 | + # Should we introduce a FilteredRevisionTree class rather |
208 | + # than pre-filter the inventory here? |
209 | + filtered_inv = inv.filter(file_ids) |
210 | + yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id) |
211 | + |
212 | @needs_read_lock |
213 | def get_deltas_for_revisions(self, revisions, specific_fileids=None): |
214 | - self._ensure_real() |
215 | - return self._real_repository.get_deltas_for_revisions(revisions, |
216 | - specific_fileids=specific_fileids) |
217 | + medium = self._client._medium |
218 | + if medium._is_remote_before((1, 2)): |
219 | + self._ensure_real() |
220 | + for delta in self._real_repository.get_deltas_for_revisions( |
221 | + revisions, specific_fileids): |
222 | + yield delta |
223 | + return |
224 | + # Get the revision-ids of interest |
225 | + required_trees = set() |
226 | + for revision in revisions: |
227 | + required_trees.add(revision.revision_id) |
228 | + required_trees.update(revision.parent_ids[:1]) |
229 | + |
230 | + # Get the matching filtered trees. Note that it's more |
231 | + # efficient to pass filtered trees to changes_from() rather |
232 | + # than doing the filtering afterwards. changes_from() could |
233 | + # arguably do the filtering itself but it's path-based, not |
234 | + # file-id based, so filtering before or afterwards is |
235 | + # currently easier. |
236 | + if specific_fileids is None: |
237 | + trees = dict((t.get_revision_id(), t) for |
238 | + t in self.revision_trees(required_trees)) |
239 | + else: |
240 | + trees = dict((t.get_revision_id(), t) for |
241 | + t in self._filtered_revision_trees(required_trees, |
242 | + specific_fileids)) |
243 | + |
244 | + # Calculate the deltas |
245 | + for revision in revisions: |
246 | + if not revision.parent_ids: |
247 | + old_tree = self.revision_tree(_mod_revision.NULL_REVISION) |
248 | + else: |
249 | + old_tree = trees[revision.parent_ids[0]] |
250 | + yield trees[revision.revision_id].changes_from(old_tree) |
251 | |
252 | @needs_read_lock |
253 | def get_revision_delta(self, revision_id, specific_fileids=None): |
254 | @@ -3157,51 +3359,6 @@ |
255 | self.bzrdir, self._client) |
256 | return self._control_files |
257 | |
258 | - def _get_checkout_format_vfs(self, lightweight=False): |
259 | - self._ensure_real() |
260 | - if lightweight: |
261 | - format = RemoteBzrDirFormat() |
262 | - self.bzrdir._format._supply_sub_formats_to(format) |
263 | - format.workingtree_format = self._real_branch._get_checkout_format( |
264 | - lightweight=lightweight).workingtree_format |
265 | - return format |
266 | - else: |
267 | - return self._real_branch._get_checkout_format(lightweight=False) |
268 | - |
269 | - def _get_checkout_format(self, lightweight=False): |
270 | - medium = self._client._medium |
271 | - if medium._is_remote_before((2, 5)): |
272 | - return self._get_checkout_format_vfs(lightweight) |
273 | - try: |
274 | - response = self._client.call('Branch.get_checkout_format', |
275 | - self._remote_path(), lightweight) |
276 | - except errors.UnknownSmartMethod: |
277 | - medium._remember_remote_is_before((2, 5)) |
278 | - return self._get_checkout_format_vfs(lightweight) |
279 | - if len(response) != 3: |
280 | - raise errors.UnexpectedSmartServerResponse(response) |
281 | - control_name, repo_name, branch_name = response |
282 | - try: |
283 | - format = controldir.network_format_registry.get(control_name) |
284 | - except KeyError: |
285 | - raise errors.UnknownFormatError(kind='control', format=control_name) |
286 | - if repo_name: |
287 | - try: |
288 | - repo_format = _mod_repository.network_format_registry.get( |
289 | - repo_name) |
290 | - except KeyError: |
291 | - raise errors.UnknownFormatError(kind='repository', |
292 | - format=repo_name) |
293 | - format.repository_format = repo_format |
294 | - if branch_name: |
295 | - try: |
296 | - format.set_branch_format( |
297 | - branch.network_format_registry.get(branch_name)) |
298 | - except KeyError: |
299 | - raise errors.UnknownFormatError(kind='branch', |
300 | - format=branch_name) |
301 | - return format |
302 | - |
303 | def get_physical_lock_status(self): |
304 | """See Branch.get_physical_lock_status().""" |
305 | try: |
306 | |
307 | === modified file 'bzrlib/smart/branch.py' |
308 | --- bzrlib/smart/branch.py 2011-12-11 16:36:30 +0000 |
309 | +++ bzrlib/smart/branch.py 2011-11-25 14:04:12 +0000 |
310 | @@ -446,22 +446,3 @@ |
311 | return SuccessfulSmartServerResponse(('yes',)) |
312 | else: |
313 | return SuccessfulSmartServerResponse(('no',)) |
314 | - |
315 | - |
316 | -class SmartServerBranchRequestGetCheckoutFormat(SmartServerBranchRequest): |
317 | - """Get the format to use for checkouts of a branch. |
318 | - |
319 | - New in 2.5. |
320 | - """ |
321 | - |
322 | - def do_with_branch(self, branch, lightweight): |
323 | - control_format = branch._get_checkout_format(lightweight) |
324 | - control_name = control_format.network_name() |
325 | - if not control_format.fixed_components: |
326 | - branch_name = control_format.get_branch_format().network_name() |
327 | - repo_name = control_format.repository_format.network_name() |
328 | - else: |
329 | - branch_name = '' |
330 | - repo_name = '' |
331 | - return SuccessfulSmartServerResponse( |
332 | - (control_name, repo_name, branch_name)) |
333 | |
334 | === modified file 'bzrlib/smart/bzrdir.py' |
335 | --- bzrlib/smart/bzrdir.py 2011-11-21 14:44:03 +0000 |
336 | +++ bzrlib/smart/bzrdir.py 2011-12-11 16:36:30 +0000 |
337 | @@ -208,6 +208,25 @@ |
338 | branch_name)) |
339 | |
340 | |
341 | +class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir): |
342 | + """Get the format to use for checkouts. |
343 | + |
344 | + New in 2.5. |
345 | + """ |
346 | + |
347 | + def do_bzrdir_request(self): |
348 | + control_format = self._bzrdir.checkout_metadir() |
349 | + control_name = control_format.network_name() |
350 | + if not control_format.fixed_components: |
351 | + branch_name = control_format.get_branch_format().network_name() |
352 | + repo_name = control_format.repository_format.network_name() |
353 | + else: |
354 | + branch_name = '' |
355 | + repo_name = '' |
356 | + return SuccessfulSmartServerResponse( |
357 | + (control_name, repo_name, branch_name)) |
358 | + |
359 | + |
360 | class SmartServerRequestCreateBranch(SmartServerRequestBzrDir): |
361 | |
362 | def do(self, path, network_name): |
363 | |
364 | === modified file 'bzrlib/smart/repository.py' |
365 | --- bzrlib/smart/repository.py 2011-12-05 15:16:52 +0000 |
366 | +++ bzrlib/smart/repository.py 2011-12-11 16:36:30 +0000 |
367 | @@ -14,7 +14,7 @@ |
368 | # along with this program; if not, write to the Free Software |
369 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
370 | |
371 | -"""Server-side repository related request implmentations.""" |
372 | +"""Server-side repository related request implementations.""" |
373 | |
374 | import bz2 |
375 | import os |
376 | @@ -28,6 +28,8 @@ |
377 | bencode, |
378 | errors, |
379 | estimate_compressed_size, |
380 | + inventory as _mod_inventory, |
381 | + inventory_delta, |
382 | osutils, |
383 | pack, |
384 | trace, |
385 | @@ -43,6 +45,7 @@ |
386 | from bzrlib.repository import _strip_NULL_ghosts, network_format_registry |
387 | from bzrlib import revision as _mod_revision |
388 | from bzrlib.versionedfile import ( |
389 | + ChunkedContentFactory, |
390 | NetworkRecordStream, |
391 | record_to_fulltext_bytes, |
392 | ) |
393 | @@ -1220,3 +1223,57 @@ |
394 | yield zlib.compress(record.get_bytes_as('fulltext')) |
395 | finally: |
396 | self._repository.unlock() |
397 | + |
398 | + |
399 | +class SmartServerRepositoryGetInventories(SmartServerRepositoryRequest): |
400 | + """Get the inventory deltas for a set of revision ids. |
401 | + |
402 | + This accepts a list of revision ids, and then sends a chain |
403 | + of deltas for the inventories of those revisions. The first |
404 | + revision will be empty. |
405 | + |
406 | + The server writes back zlibbed serialized inventory deltas, |
407 | + in the ordering specified. The base for each delta is the |
408 | + inventory generated by the previous delta. |
409 | + |
410 | + New in 2.5. |
411 | + """ |
412 | + |
413 | + def _inventory_delta_stream(self, repository, ordering, revids): |
414 | + prev_inv = _mod_inventory.Inventory(root_id=None, |
415 | + revision_id=_mod_revision.NULL_REVISION) |
416 | + serializer = inventory_delta.InventoryDeltaSerializer( |
417 | + repository.supports_rich_root(), |
418 | + repository._format.supports_tree_reference) |
419 | + repository.lock_read() |
420 | + try: |
421 | + for inv, revid in repository._iter_inventories(revids, ordering): |
422 | + if inv is None: |
423 | + continue |
424 | + inv_delta = inv._make_delta(prev_inv) |
425 | + lines = serializer.delta_to_lines( |
426 | + prev_inv.revision_id, inv.revision_id, inv_delta) |
427 | + yield ChunkedContentFactory(inv.revision_id, None, None, lines) |
428 | + prev_inv = inv |
429 | + finally: |
430 | + repository.unlock() |
431 | + |
432 | + def body_stream(self, repository, ordering, revids): |
433 | + substream = self._inventory_delta_stream(repository, |
434 | + ordering, revids) |
435 | + return _stream_to_byte_stream([('inventory-delta', substream)], |
436 | + repository._format) |
437 | + |
438 | + def do_body(self, body_bytes): |
439 | + return SuccessfulSmartServerResponse(('ok', ), |
440 | + body_stream=self.body_stream(self._repository, self._ordering, |
441 | + body_bytes.splitlines())) |
442 | + |
443 | + def do_repository_request(self, repository, ordering): |
444 | + if ordering == 'unordered': |
445 | + # inventory deltas for a topologically sorted stream |
446 | + # are likely to be smaller |
447 | + ordering = 'topological' |
448 | + self._ordering = ordering |
449 | + # Signal that we want a body |
450 | + return None |
451 | |
452 | === modified file 'bzrlib/smart/request.py' |
453 | --- bzrlib/smart/request.py 2011-12-11 16:36:30 +0000 |
454 | +++ bzrlib/smart/request.py 2011-12-11 16:36:30 +0000 |
455 | @@ -547,9 +547,6 @@ |
456 | 'Branch.get_stacked_on_url', 'bzrlib.smart.branch', |
457 | 'SmartServerBranchRequestGetStackedOnURL', info='read') |
458 | request_handlers.register_lazy( |
459 | - 'Branch.get_checkout_format', 'bzrlib.smart.branch', |
460 | - 'SmartServerBranchRequestGetCheckoutFormat', info='read') |
461 | -request_handlers.register_lazy( |
462 | 'Branch.get_physical_lock_status', 'bzrlib.smart.branch', |
463 | 'SmartServerBranchRequestGetPhysicalLockStatus', info='read') |
464 | request_handlers.register_lazy( |
465 | @@ -586,6 +583,9 @@ |
466 | 'Branch.revision_id_to_revno', 'bzrlib.smart.branch', |
467 | 'SmartServerBranchRequestRevisionIdToRevno', info='read') |
468 | request_handlers.register_lazy( |
469 | + 'BzrDir.checkout_metadir', 'bzrlib.smart.bzrdir', |
470 | + 'SmartServerBzrDirRequestCheckoutMetaDir', info='read') |
471 | +request_handlers.register_lazy( |
472 | 'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir', |
473 | 'SmartServerBzrDirRequestCloningMetaDir', info='read') |
474 | request_handlers.register_lazy( |
475 | @@ -754,6 +754,9 @@ |
476 | 'VersionedFileRepository.get_serializer_format', 'bzrlib.smart.repository', |
477 | 'SmartServerRepositoryGetSerializerFormat', info='read') |
478 | request_handlers.register_lazy( |
479 | + 'VersionedFileRepository.get_inventories', 'bzrlib.smart.repository', |
480 | + 'SmartServerRepositoryGetInventories', info='read') |
481 | +request_handlers.register_lazy( |
482 | 'Repository.tarball', 'bzrlib.smart.repository', |
483 | 'SmartServerRepositoryTarball', info='read') |
484 | request_handlers.register_lazy( |
485 | |
486 | === modified file 'bzrlib/tests/blackbox/test_annotate.py' |
487 | --- bzrlib/tests/blackbox/test_annotate.py 2011-12-11 16:36:30 +0000 |
488 | +++ bzrlib/tests/blackbox/test_annotate.py 2011-12-11 16:36:30 +0000 |
489 | @@ -326,6 +326,6 @@ |
490 | # being too low. If rpc_count increases, more network roundtrips have |
491 | # become necessary for this use case. Please do not adjust this number |
492 | # upwards without agreement from bzr's network support maintainers. |
493 | - self.assertLength(19, self.hpss_calls) |
494 | + self.assertLength(15, self.hpss_calls) |
495 | self.expectFailure("annotate accesses inventories, which require VFS access", |
496 | self.assertThat, self.hpss_calls, NoVfsCalls) |
497 | |
498 | === modified file 'bzrlib/tests/blackbox/test_branch.py' |
499 | --- bzrlib/tests/blackbox/test_branch.py 2011-12-11 16:36:30 +0000 |
500 | +++ bzrlib/tests/blackbox/test_branch.py 2011-12-11 16:36:30 +0000 |
501 | @@ -483,7 +483,7 @@ |
502 | # being too low. If rpc_count increases, more network roundtrips have |
503 | # become necessary for this use case. Please do not adjust this number |
504 | # upwards without agreement from bzr's network support maintainers. |
505 | - self.assertLength(40, self.hpss_calls) |
506 | + self.assertLength(33, self.hpss_calls) |
507 | self.expectFailure("branching to the same branch requires VFS access", |
508 | self.assertThat, self.hpss_calls, NoVfsCalls) |
509 | |
510 | |
511 | === modified file 'bzrlib/tests/blackbox/test_cat.py' |
512 | --- bzrlib/tests/blackbox/test_cat.py 2011-12-11 16:36:30 +0000 |
513 | +++ bzrlib/tests/blackbox/test_cat.py 2011-12-11 16:36:30 +0000 |
514 | @@ -239,6 +239,5 @@ |
515 | # being too low. If rpc_count increases, more network roundtrips have |
516 | # become necessary for this use case. Please do not adjust this number |
517 | # upwards without agreement from bzr's network support maintainers. |
518 | - self.assertLength(16, self.hpss_calls) |
519 | - self.expectFailure("cat still uses VFS calls", |
520 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
521 | + self.assertLength(9, self.hpss_calls) |
522 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
523 | |
524 | === modified file 'bzrlib/tests/blackbox/test_checkout.py' |
525 | --- bzrlib/tests/blackbox/test_checkout.py 2011-12-11 16:36:30 +0000 |
526 | +++ bzrlib/tests/blackbox/test_checkout.py 2011-12-11 16:36:30 +0000 |
527 | @@ -179,8 +179,7 @@ |
528 | for count in range(9): |
529 | t.commit(message='commit %d' % count) |
530 | self.reset_smart_call_log() |
531 | - out, err = self.run_bzr(['checkout', self.get_url('from'), |
532 | - 'target']) |
533 | + out, err = self.run_bzr(['checkout', self.get_url('from'), 'target']) |
534 | # This figure represent the amount of work to perform this use case. It |
535 | # is entirely ok to reduce this number if a test fails due to rpc_count |
536 | # being too low. If rpc_count increases, more network roundtrips have |
537 | @@ -202,9 +201,5 @@ |
538 | # being too low. If rpc_count increases, more network roundtrips have |
539 | # become necessary for this use case. Please do not adjust this number |
540 | # upwards without agreement from bzr's network support maintainers. |
541 | - if len(self.hpss_calls) < 28 or len(self.hpss_calls) > 40: |
542 | - self.fail( |
543 | - "Incorrect length: wanted between 28 and 40, got %d for %r" % ( |
544 | - len(self.hpss_calls), self.hpss_calls)) |
545 | - self.expectFailure("lightweight checkouts require VFS calls", |
546 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
547 | + self.assertLength(15, self.hpss_calls) |
548 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
549 | |
550 | === modified file 'bzrlib/tests/blackbox/test_export.py' |
551 | --- bzrlib/tests/blackbox/test_export.py 2011-12-11 16:36:30 +0000 |
552 | +++ bzrlib/tests/blackbox/test_export.py 2011-12-11 16:36:30 +0000 |
553 | @@ -448,6 +448,5 @@ |
554 | # being too low. If rpc_count increases, more network roundtrips have |
555 | # become necessary for this use case. Please do not adjust this number |
556 | # upwards without agreement from bzr's network support maintainers. |
557 | - self.assertLength(16, self.hpss_calls) |
558 | - self.expectFailure("export requires inventory access which requires VFS", |
559 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
560 | + self.assertLength(7, self.hpss_calls) |
561 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
562 | |
563 | === modified file 'bzrlib/tests/blackbox/test_log.py' |
564 | --- bzrlib/tests/blackbox/test_log.py 2011-12-11 16:36:30 +0000 |
565 | +++ bzrlib/tests/blackbox/test_log.py 2011-12-11 16:36:30 +0000 |
566 | @@ -1086,9 +1086,8 @@ |
567 | # being too low. If rpc_count increases, more network roundtrips have |
568 | # become necessary for this use case. Please do not adjust this number |
569 | # upwards without agreement from bzr's network support maintainers. |
570 | - self.assertLength(19, self.hpss_calls) |
571 | - self.expectFailure("verbose log accesses inventories, which require VFS", |
572 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
573 | + self.assertLength(11, self.hpss_calls) |
574 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
575 | |
576 | def test_per_file(self): |
577 | self.setup_smart_server_with_call_log() |
578 | @@ -1103,6 +1102,5 @@ |
579 | # being too low. If rpc_count increases, more network roundtrips have |
580 | # become necessary for this use case. Please do not adjust this number |
581 | # upwards without agreement from bzr's network support maintainers. |
582 | - self.assertLength(21, self.hpss_calls) |
583 | - self.expectFailure("per-file graph access requires VFS", |
584 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
585 | + self.assertLength(15, self.hpss_calls) |
586 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
587 | |
588 | === modified file 'bzrlib/tests/blackbox/test_ls.py' |
589 | --- bzrlib/tests/blackbox/test_ls.py 2011-12-11 16:36:30 +0000 |
590 | +++ bzrlib/tests/blackbox/test_ls.py 2011-12-11 16:36:30 +0000 |
591 | @@ -262,6 +262,5 @@ |
592 | # being too low. If rpc_count increases, more network roundtrips have |
593 | # become necessary for this use case. Please do not adjust this number |
594 | # upwards without agreement from bzr's network support maintainers. |
595 | - self.assertLength(15, self.hpss_calls) |
596 | - self.expectFailure("inventories can only be accessed over VFS", |
597 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
598 | + self.assertLength(6, self.hpss_calls) |
599 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
600 | |
601 | === modified file 'bzrlib/tests/blackbox/test_sign_my_commits.py' |
602 | --- bzrlib/tests/blackbox/test_sign_my_commits.py 2011-12-11 16:36:30 +0000 |
603 | +++ bzrlib/tests/blackbox/test_sign_my_commits.py 2011-12-11 16:36:30 +0000 |
604 | @@ -165,7 +165,7 @@ |
605 | # being too low. If rpc_count increases, more network roundtrips have |
606 | # become necessary for this use case. Please do not adjust this number |
607 | # upwards without agreement from bzr's network support maintainers. |
608 | - self.assertLength(54, self.hpss_calls) |
609 | + self.assertLength(50, self.hpss_calls) |
610 | self.expectFailure("signing commits requires VFS access", |
611 | self.assertThat, self.hpss_calls, NoVfsCalls) |
612 | |
613 | @@ -185,12 +185,5 @@ |
614 | # being too low. If rpc_count increases, more network roundtrips have |
615 | # become necessary for this use case. Please do not adjust this number |
616 | # upwards without agreement from bzr's network support maintainers. |
617 | - |
618 | - # The number of readv requests seems to vary depending on the generated |
619 | - # repository and how well it compresses, so allow for a bit of |
620 | - # variation: |
621 | - if len(self.hpss_calls) not in (18, 19): |
622 | - self.fail("Incorrect length: wanted 18 or 19, got %d for %r" % ( |
623 | - len(self.hpss_calls), self.hpss_calls)) |
624 | - self.expectFailure("verifying commits requires VFS access", |
625 | - self.assertThat, self.hpss_calls, NoVfsCalls) |
626 | + self.assertLength(10, self.hpss_calls) |
627 | + self.assertThat(self.hpss_calls, NoVfsCalls) |
628 | |
629 | === modified file 'bzrlib/tests/per_interbranch/test_push.py' |
630 | --- bzrlib/tests/per_interbranch/test_push.py 2011-10-15 01:09:01 +0000 |
631 | +++ bzrlib/tests/per_interbranch/test_push.py 2011-12-11 16:36:30 +0000 |
632 | @@ -279,10 +279,10 @@ |
633 | # remote graph any further. |
634 | bzr_core_trace = Equals( |
635 | ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19', |
636 | - 'get', 'Branch.set_last_revision_info', 'Branch.unlock']) |
637 | + 'Branch.set_last_revision_info', 'Branch.unlock']) |
638 | bzr_loom_trace = Equals( |
639 | ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19', |
640 | - 'get', 'Branch.set_last_revision_info', 'get', 'Branch.unlock']) |
641 | + 'Branch.set_last_revision_info', 'get', 'Branch.unlock']) |
642 | self.assertThat(calls_after_insert_stream, |
643 | MatchesAny(bzr_core_trace, bzr_loom_trace)) |
644 | |
645 | |
646 | === modified file 'bzrlib/tests/test_remote.py' |
647 | --- bzrlib/tests/test_remote.py 2011-12-11 16:36:30 +0000 |
648 | +++ bzrlib/tests/test_remote.py 2011-12-11 16:36:30 +0000 |
649 | @@ -69,6 +69,7 @@ |
650 | from bzrlib.smart.repository import ( |
651 | SmartServerRepositoryGetParentMap, |
652 | SmartServerRepositoryGetStream_1_19, |
653 | + _stream_to_byte_stream, |
654 | ) |
655 | from bzrlib.symbol_versioning import deprecated_in |
656 | from bzrlib.tests import ( |
657 | @@ -503,6 +504,43 @@ |
658 | self.assertRaises(errors.UnknownFormatError, a_bzrdir.cloning_metadir) |
659 | |
660 | |
661 | +class TestBzrDirCheckoutMetaDir(TestRemote): |
662 | + |
663 | + def test__get_checkout_format(self): |
664 | + transport = MemoryTransport() |
665 | + client = FakeClient(transport.base) |
666 | + reference_bzrdir_format = bzrdir.format_registry.get('default')() |
667 | + control_name = reference_bzrdir_format.network_name() |
668 | + client.add_expected_call( |
669 | + 'BzrDir.checkout_metadir', ('quack/', ), |
670 | + 'success', (control_name, '', '')) |
671 | + transport.mkdir('quack') |
672 | + transport = transport.clone('quack') |
673 | + a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(), |
674 | + _client=client) |
675 | + result = a_bzrdir.checkout_metadir() |
676 | + # We should have got a reference control dir with default branch and |
677 | + # repository formats. |
678 | + self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result)) |
679 | + self.assertEqual(None, result._repository_format) |
680 | + self.assertEqual(None, result._branch_format) |
681 | + self.assertFinished(client) |
682 | + |
683 | + def test_unknown_format(self): |
684 | + transport = MemoryTransport() |
685 | + client = FakeClient(transport.base) |
686 | + client.add_expected_call( |
687 | + 'BzrDir.checkout_metadir', ('quack/',), |
688 | + 'success', ('dontknow', '', '')) |
689 | + transport.mkdir('quack') |
690 | + transport = transport.clone('quack') |
691 | + a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(), |
692 | + _client=client) |
693 | + self.assertRaises(errors.UnknownFormatError, |
694 | + a_bzrdir.checkout_metadir) |
695 | + self.assertFinished(client) |
696 | + |
697 | + |
698 | class TestBzrDirDestroyBranch(TestRemote): |
699 | |
700 | def test_destroy_default(self): |
701 | @@ -1102,47 +1140,6 @@ |
702 | self.assertFinished(client) |
703 | |
704 | |
705 | -class TestBranchGetCheckoutFormat(RemoteBranchTestCase): |
706 | - |
707 | - def test__get_checkout_format(self): |
708 | - transport = MemoryTransport() |
709 | - client = FakeClient(transport.base) |
710 | - reference_bzrdir_format = bzrdir.format_registry.get('default')() |
711 | - control_name = reference_bzrdir_format.network_name() |
712 | - client.add_expected_call( |
713 | - 'Branch.get_stacked_on_url', ('quack/',), |
714 | - 'error', ('NotStacked',)) |
715 | - client.add_expected_call( |
716 | - 'Branch.get_checkout_format', ('quack/', False), |
717 | - 'success', (control_name, '', '')) |
718 | - transport.mkdir('quack') |
719 | - transport = transport.clone('quack') |
720 | - branch = self.make_remote_branch(transport, client) |
721 | - result = branch._get_checkout_format() |
722 | - # We should have got a reference control dir with default branch and |
723 | - # repository formats. |
724 | - self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result)) |
725 | - self.assertEqual(None, result._repository_format) |
726 | - self.assertEqual(None, result._branch_format) |
727 | - self.assertFinished(client) |
728 | - |
729 | - def test_unknown_format(self): |
730 | - transport = MemoryTransport() |
731 | - client = FakeClient(transport.base) |
732 | - client.add_expected_call( |
733 | - 'Branch.get_stacked_on_url', ('quack/',), |
734 | - 'error', ('NotStacked',)) |
735 | - client.add_expected_call( |
736 | - 'Branch.get_checkout_format', ('quack/', False), |
737 | - 'success', ('dontknow', '', '')) |
738 | - transport.mkdir('quack') |
739 | - transport = transport.clone('quack') |
740 | - branch = self.make_remote_branch(transport, client) |
741 | - self.assertRaises(errors.UnknownFormatError, |
742 | - branch._get_checkout_format) |
743 | - self.assertFinished(client) |
744 | - |
745 | - |
746 | class TestBranchGetPhysicalLockStatus(RemoteBranchTestCase): |
747 | |
748 | def test_get_physical_lock_status_yes(self): |
749 | @@ -4216,3 +4213,43 @@ |
750 | 'Repository.unlock', ('quack/', 'token', 'False'), |
751 | 'success', ('ok', )) |
752 | repo.pack(['hinta', 'hintb']) |
753 | + |
754 | + |
755 | +class TestRepositoryIterInventories(TestRemoteRepository): |
756 | + """Test Repository.iter_inventories.""" |
757 | + |
758 | + def _serialize_inv_delta(self, old_name, new_name, delta): |
759 | + serializer = inventory_delta.InventoryDeltaSerializer(True, False) |
760 | + return "".join(serializer.delta_to_lines(old_name, new_name, delta)) |
761 | + |
762 | + def test_single_empty(self): |
763 | + transport_path = 'quack' |
764 | + repo, client = self.setup_fake_client_and_repository(transport_path) |
765 | + fmt = bzrdir.format_registry.get('2a')().repository_format |
766 | + repo._format = fmt |
767 | + stream = [('inventory-delta', [ |
768 | + versionedfile.FulltextContentFactory('somerevid', None, None, |
769 | + self._serialize_inv_delta('null:', 'somerevid', []))])] |
770 | + client.add_expected_call( |
771 | + 'VersionedFileRepository.get_inventories', ('quack/', 'unordered'), |
772 | + 'success', ('ok', ), |
773 | + _stream_to_byte_stream(stream, fmt)) |
774 | + ret = list(repo.iter_inventories(["somerevid"])) |
775 | + self.assertLength(1, ret) |
776 | + inv = ret[0] |
777 | + self.assertEquals("somerevid", inv.revision_id) |
778 | + |
779 | + def test_empty(self): |
780 | + transport_path = 'quack' |
781 | + repo, client = self.setup_fake_client_and_repository(transport_path) |
782 | + ret = list(repo.iter_inventories([])) |
783 | + self.assertEquals(ret, []) |
784 | + |
785 | + def test_missing(self): |
786 | + transport_path = 'quack' |
787 | + repo, client = self.setup_fake_client_and_repository(transport_path) |
788 | + client.add_expected_call( |
789 | + 'VersionedFileRepository.get_inventories', ('quack/', 'unordered'), |
790 | + 'success', ('ok', ), iter([])) |
791 | + self.assertRaises(errors.NoSuchRevision, list, repo.iter_inventories( |
792 | + ["somerevid"])) |
793 | |
794 | === modified file 'bzrlib/tests/test_smart.py' |
795 | --- bzrlib/tests/test_smart.py 2011-12-11 16:36:30 +0000 |
796 | +++ bzrlib/tests/test_smart.py 2011-12-11 16:36:30 +0000 |
797 | @@ -32,6 +32,7 @@ |
798 | bzrdir, |
799 | errors, |
800 | gpg, |
801 | + inventory_delta, |
802 | tests, |
803 | transport, |
804 | urlutils, |
805 | @@ -224,6 +225,24 @@ |
806 | self.assertEqual(expected, request.execute('', 'False')) |
807 | |
808 | |
809 | +class TestSmartServerBzrDirRequestCloningMetaDir( |
810 | + tests.TestCaseWithMemoryTransport): |
811 | + """Tests for BzrDir.checkout_metadir.""" |
812 | + |
813 | + def test_checkout_metadir(self): |
814 | + backing = self.get_transport() |
815 | + request = smart_dir.SmartServerBzrDirRequestCheckoutMetaDir( |
816 | + backing) |
817 | + branch = self.make_branch('.', format='2a') |
818 | + response = request.execute('') |
819 | + self.assertEqual( |
820 | + smart_req.SmartServerResponse( |
821 | + ('Bazaar-NG meta directory, format 1\n', |
822 | + 'Bazaar repository format 2a (needs bzr 1.16 or later)\n', |
823 | + 'Bazaar Branch Format 7 (needs bzr 1.6)\n')), |
824 | + response) |
825 | + |
826 | + |
827 | class TestSmartServerBzrDirRequestDestroyBranch( |
828 | tests.TestCaseWithMemoryTransport): |
829 | """Tests for BzrDir.destroy_branch.""" |
830 | @@ -1458,35 +1477,6 @@ |
831 | smart_req.SmartServerResponse(('no',)), response) |
832 | |
833 | |
834 | -class TestSmartServerBranchRequestGetCheckoutFormat(TestLockedBranch): |
835 | - |
836 | - def test_lightweight(self): |
837 | - backing = self.get_transport() |
838 | - request = smart_branch.SmartServerBranchRequestGetCheckoutFormat( |
839 | - backing) |
840 | - branch = self.make_branch('.', format='2a') |
841 | - response = request.execute('', 'True') |
842 | - self.assertEqual( |
843 | - smart_req.SmartServerResponse( |
844 | - ('Bazaar-NG meta directory, format 1\n', |
845 | - 'Bazaar repository format 2a (needs bzr 1.16 or later)\n', |
846 | - 'Bazaar Branch Format 7 (needs bzr 1.6)\n')), |
847 | - response) |
848 | - |
849 | - def test_heavyweight(self): |
850 | - backing = self.get_transport() |
851 | - request = smart_branch.SmartServerBranchRequestGetCheckoutFormat( |
852 | - backing) |
853 | - branch = self.make_branch('.', format='2a') |
854 | - response = request.execute('', 'False') |
855 | - self.assertEqual( |
856 | - smart_req.SmartServerResponse(( |
857 | - 'Bazaar-NG meta directory, format 1\n', |
858 | - 'Bazaar repository format 2a (needs bzr 1.16 or later)\n', |
859 | - 'Bazaar Branch Format 7 (needs bzr 1.6)\n')), |
860 | - response) |
861 | - |
862 | - |
863 | class TestSmartServerBranchRequestUnlock(TestLockedBranch): |
864 | |
865 | def setUp(self): |
866 | @@ -2456,8 +2446,6 @@ |
867 | smart_branch.SmartServerBranchPutConfigFile) |
868 | self.assertHandlerEqual('Branch.get_parent', |
869 | smart_branch.SmartServerBranchGetParent) |
870 | - self.assertHandlerEqual('Branch.get_checkout_format', |
871 | - smart_branch.SmartServerBranchRequestGetCheckoutFormat) |
872 | self.assertHandlerEqual('Branch.get_physical_lock_status', |
873 | smart_branch.SmartServerBranchRequestGetPhysicalLockStatus) |
874 | self.assertHandlerEqual('Branch.get_tags_bytes', |
875 | @@ -2492,6 +2480,8 @@ |
876 | smart_dir.SmartServerRequestInitializeBzrDir) |
877 | self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16', |
878 | smart_dir.SmartServerRequestBzrDirInitializeEx) |
879 | + self.assertHandlerEqual('BzrDir.checkout_metadir', |
880 | + smart_dir.SmartServerBzrDirRequestCheckoutMetaDir) |
881 | self.assertHandlerEqual('BzrDir.cloning_metadir', |
882 | smart_dir.SmartServerBzrDirRequestCloningMetaDir) |
883 | self.assertHandlerEqual('BzrDir.get_config_file', |
884 | @@ -2558,6 +2548,8 @@ |
885 | smart_repo.SmartServerRepositoryAbortWriteGroup) |
886 | self.assertHandlerEqual('VersionedFileRepository.get_serializer_format', |
887 | smart_repo.SmartServerRepositoryGetSerializerFormat) |
888 | + self.assertHandlerEqual('VersionedFileRepository.get_inventories', |
889 | + smart_repo.SmartServerRepositoryGetInventories) |
890 | self.assertHandlerEqual('Transport.is_readonly', |
891 | smart_req.SmartServerIsReadonly) |
892 | |
893 | @@ -2623,3 +2615,48 @@ |
894 | smart_req.SuccessfulSmartServerResponse(('ok', ), ), |
895 | request.do_body('')) |
896 | |
897 | + |
898 | +class TestSmartServerRepositoryGetInventories(tests.TestCaseWithTransport): |
899 | + |
900 | + def _get_serialized_inventory_delta(self, repository, base_revid, revid): |
901 | + base_inv = repository.revision_tree(base_revid).inventory |
902 | + inv = repository.revision_tree(revid).inventory |
903 | + inv_delta = inv._make_delta(base_inv) |
904 | + serializer = inventory_delta.InventoryDeltaSerializer(True, False) |
905 | + return "".join(serializer.delta_to_lines(base_revid, revid, inv_delta)) |
906 | + |
907 | + def test_single(self): |
908 | + backing = self.get_transport() |
909 | + request = smart_repo.SmartServerRepositoryGetInventories(backing) |
910 | + t = self.make_branch_and_tree('.', format='2a') |
911 | + self.addCleanup(t.lock_write().unlock) |
912 | + self.build_tree_contents([("file", "somecontents")]) |
913 | + t.add(["file"], ["thefileid"]) |
914 | + t.commit(rev_id='somerev', message="add file") |
915 | + self.assertIs(None, request.execute('', 'unordered')) |
916 | + response = request.do_body("somerev\n") |
917 | + self.assertTrue(response.is_successful()) |
918 | + self.assertEquals(response.args, ("ok", )) |
919 | + stream = [('inventory-delta', [ |
920 | + versionedfile.FulltextContentFactory('somerev', None, None, |
921 | + self._get_serialized_inventory_delta( |
922 | + t.branch.repository, 'null:', 'somerev'))])] |
923 | + fmt = bzrdir.format_registry.get('2a')().repository_format |
924 | + self.assertEquals( |
925 | + "".join(response.body_stream), |
926 | + "".join(smart_repo._stream_to_byte_stream(stream, fmt))) |
927 | + |
928 | + def test_empty(self): |
929 | + backing = self.get_transport() |
930 | + request = smart_repo.SmartServerRepositoryGetInventories(backing) |
931 | + t = self.make_branch_and_tree('.', format='2a') |
932 | + self.addCleanup(t.lock_write().unlock) |
933 | + self.build_tree_contents([("file", "somecontents")]) |
934 | + t.add(["file"], ["thefileid"]) |
935 | + t.commit(rev_id='somerev', message="add file") |
936 | + self.assertIs(None, request.execute('', 'unordered')) |
937 | + response = request.do_body("") |
938 | + self.assertTrue(response.is_successful()) |
939 | + self.assertEquals(response.args, ("ok", )) |
940 | + self.assertEquals("".join(response.body_stream), |
941 | + "Bazaar pack format 1 (introduced in 0.18)\nB54\n\nBazaar repository format 2a (needs bzr 1.16 or later)\nE") |
942 | |
943 | === modified file 'doc/en/release-notes/bzr-2.5.txt' |
944 | --- doc/en/release-notes/bzr-2.5.txt 2011-12-11 16:36:30 +0000 |
945 | +++ doc/en/release-notes/bzr-2.5.txt 2011-12-11 16:36:30 +0000 |
946 | @@ -111,6 +111,9 @@ |
947 | * Plugins can now register additional "location aliases". |
948 | (Jelmer Vernooij) |
949 | |
950 | +* ``bzr status`` no longer shows shelves if files are specified. |
951 | + (Francis Devereux) |
952 | + |
953 | * Revision specifiers will now only browse as much history as they |
954 | need to, rather than grabbing the whole history unnecessarily in some |
955 | cases. (Jelmer Vernooij) |
956 | @@ -236,6 +239,13 @@ |
957 | ``Repository.get_revision_signature_text``. |
958 | (Jelmer Vernooij) |
959 | |
960 | +* Add HPSS calls for ``Repository.iter_files_bytes`` and |
961 | + ``VersionedFileRepository.get_inventories``, speeding up |
962 | + several commands including ``bzr export`` and ``bzr co --lightweight``. |
963 | + (Jelmer Vernooij, #608640) |
964 | + |
965 | +* Add HPSS call for ``Branch.get_checkout_format``. (Jelmer Vernooij, #894459) |
966 | + |
967 | * Add HPSS call for ``Repository.pack``. (Jelmer Vernooij, #894461) |
968 | |
969 | * Custom HPSS error handlers can now be installed in the smart server client |
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 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
~/src/bzr/
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 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
~/src/bzr/