Merge lp:~parthm/bzr/538868-message-for-heavy-checkout into lp:bzr

Proposed by Parth Malwankar
Status: Merged
Approved by: Parth Malwankar
Approved revision: no longer in the source branch.
Merged at revision: 5241
Proposed branch: lp:~parthm/bzr/538868-message-for-heavy-checkout
Merge into: lp:bzr
Diff against target: 357 lines (+160/-23)
8 files modified
NEWS (+3/-3)
bzrlib/builtins.py (+0/-5)
bzrlib/recordcounter.py (+86/-0)
bzrlib/remote.py (+2/-1)
bzrlib/repofmt/groupcompress_repo.py (+23/-4)
bzrlib/repository.py (+6/-4)
bzrlib/smart/repository.py (+40/-4)
bzrlib/tests/blackbox/test_checkout.py (+0/-2)
To merge this branch: bzr merge lp:~parthm/bzr/538868-message-for-heavy-checkout
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve
Gary van der Merwe Abstain
John A Meinel Needs Fixing
bzr-core 2nd review Pending
Martin Pool 2nd review Pending
Review via email: mp+25336@code.launchpad.net

This proposal supersedes a proposal from 2010-04-30.

Commit message

(parthm) Estimated records to be fetched are now shown for fetch (2a only).

Description of the change

=== Fixes Bug #538868 ===
For heavyweight checkout show a message showing that history is being copied and it may take some time.

Sample output:

[tmp]% ~/src/bzr.dev/538868-message-for-heavy-checkout/bzr --no-plugins checkout ~/src/bzr.dev/trunk foobar
Copying history to "foobar". This may take some time.
bzr: interrupted
[tmp]% ~/src/bzr.dev/538868-message-for-heavy-checkout/bzr --no-plugins checkout ~/src/bzr.dev/trunk
Copying history to "trunk". This may take some time.
bzr: interrupted

The only ugliness I see is in the off case that to_location already exists. In this case the output is:

[tmp]% ~/src/bzr.dev/538868-message-for-heavy-checkout/bzr --no-plugins checkout ~/src/bzr.dev/trunk
Copying history to "trunk". This may take some time.
bzr: ERROR: File exists: u'/home/parthm/tmp/trunk/.bzr': [Errno 17] File exists: '/home/parthm/tmp/trunk/.bzr'

It would be ideal if the "copying history" message is not shown. I suppose thats not too bad though. I had a early failure fix for this but haven't put it in considering that bzr works across multiple transports.

+ # Fail early if to_location/.bzr exists. We don't want to
+ # give a message "Copying history ..." and then fail
+ # saying to_location/.bzr exists.
+ to_loc_bzr = osutils.joinpath([to_location, '.bzr'])
+ if osutils.lexists(to_loc_bzr):
+ raise errors.BzrCommandError('"%s" exists.' % to_loc_bzr)
+

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote : Posted in a previous version of this proposal

Thanks, this is a very nice bug to fix.

I would prefer the message came out through trace or the ui factory
than directly to self.outf, because that will make it easier to
refactor out of the cmd implementation, and it's more likely to
automatically respect --quiet. You might then be able to test more
cleanly through TestUIFactory.

Revision history for this message
Gary van der Merwe (garyvdm) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Vincent Ladeuil (vila) wrote : Posted in a previous version of this proposal

Apart from the message tweaks mentioned on IRC, that's good to land !

review: Approve
Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

I realise this has gone through, so I'd like to just request some more
stuff if you have time; if not please file a bug.

The message will show up when doing a heavy checking in a repository;
that's just annoying - no history is being copied, so no message
should appear. Recommended fix: move the notification into the core,
out of builtins.py.

Secondly, if its worth telling people we're copying [a lot] of history
for checkout, I think its worth telling them about it for branch and
merge too. Perhaps lets set some sort of heuristic (e.g. 100 or more
revisions) and have the warning trigger on that?

-Rob

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

On 6 May 2010 04:28, Robert Collins <email address hidden> wrote:
> I realise this has gone through, so I'd like to just request some more
> stuff if you have time; if not please file a bug.
>
> The message will show up when doing a heavy checking in a repository;
> that's just annoying - no history is being copied, so no message
> should appear. Recommended fix: move the notification into the core,
> out of builtins.py.

+1

perhaps just showing it from fetch would be best

> Secondly, if its worth telling people we're copying [a lot] of history
> for checkout, I think its worth telling them about it for branch and
> merge too. Perhaps lets set some sort of heuristic (e.g. 100 or more
> revisions) and have the warning trigger on that?

-½ on that, because it will create questions about "but it worked
before, what changed?" If we want that kind of approach we should
make sure there's a clear progress bar message, so that it's visible
only while the slow operation is taking place.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Parth Malwankar (parthm) wrote : Posted in a previous version of this proposal

On Thu, May 6, 2010 at 8:58 AM, Robert Collins
<email address hidden> wrote:
> I realise this has gone through, so I'd like to just request some more
> stuff if you have time; if not please file a bug.
>
> The message will show up when doing a heavy checking in a repository;
> that's just annoying - no history is being copied, so no message
> should appear. Recommended fix: move the notification into the core,
> out of builtins.py.
>
> Secondly, if its worth telling people we're copying [a lot] of history
> for checkout, I think its worth telling them about it for branch and
> merge too. Perhaps lets set some sort of heuristic (e.g. 100 or more
> revisions) and have the warning trigger on that?
>

Good points. Thanks for the review.
As discussed on the IRC I will work on fixing this.
I don't have a good solution yet. Will propose something taking into
account Martin Pool recommendation.

Revision history for this message
Parth Malwankar (parthm) wrote : Posted in a previous version of this proposal

So I updated this patch to skip the message when checkout is done in a shared repo. However, there is an interesting case below.

[tmp]% bzr init-repo foo
Shared repository with trees (format: 2a)
Location:
  shared repository: foo
[tmp]% cd foo
[foo]% /home/parthm/src/bzr.dev/538868-message-for-heavy-checkout/bzr checkout ~/src/bzr.dev/trunk foo
[foo]%

In this case, the entire history is copied so it does take time. I am wondering if we should just stick to the simpler earlier patch. Alternatively, if there is a way to know how many changes need to be pulled we could show the message based on this.

This is still checkout specific and doesn't touch other operations.

Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

Well the main point for me is that the issue - lots of history being
copied - is separate from the commands. So I guess I'm really saying
'do it more broadly please'.

-Rob

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

test

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

test

review: Needs Information (2nd review)
Revision history for this message
John A Meinel (jameinel) wrote : Posted in a previous version of this proposal

23 pb = ui.ui_factory.nested_progress_bar()
24 + key_count = len(search.get_keys())
25 try:

^- We've discussed that this is a fairly unfortunate regression, as it requires polling the remote server for the list of revisions rather than just having it stream them out.

I'm pretty sure Parth is already looking at how to fix this.

review: Needs Resubmitting
Revision history for this message
Parth Malwankar (parthm) wrote : Posted in a previous version of this proposal

With a lot of help from John, this patch is is a good enough shape for review.
Its evolved from a fix for bug #538868 to a fix for bug #374740

The intent is to show users an _estimate_ of the amount of work pending in branch/push/pull/checkout (remote-local, local-remote, remote-remote) operations. This is done by showing the number of records pending.

E.g.

[tmp]% ~/src/bzr.dev/edge/bzr checkout ~/src/bzr.dev/trunk pqr
- Fetching revisions:Inserting stream:Estimate 106429/320381

As the number of records are proportional to the number of revisions to be fetched, for remote operations, this count is not known and the progress bar starts with "Estimating.. X" where X goes from 0 to revs-to-fetch, following this the progress bar changes to whats shown above. For the local ops, we know the count upfront so the progress starts at 0/N.

A RecordCounter object has been added to maintain current, max, key_count and to encapsulate the estimation algorithm. An instance of this is added to StreamSource which is then used among the various sub-streams to show progress. The wrap_and_count generator wraps existing sub-streams with the progress bar printer.

Revision history for this message
Parth Malwankar (parthm) wrote : Posted in a previous version of this proposal

Just to add. This progress is seen during the "Inserting stream" phase with is the big time consumer. There is still room for improvement with "Getting stream" and "Inserting missing keys" phase but that can probably be a separate bug.

Revision history for this message
John A Meinel (jameinel) wrote :

v- I'm pretty sure the copyright here should be just 2010.

38 +# Copyright (C) 2006-2010 Canonical Ltd
...
52 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53 +"""Record counting support for showing progress of revision fetch."""
54 +
55 +class RecordCounter(object):

^- and you should have an extra blank line on the top level between the module docstring and class...

The NEWS entry should probably be clear that this is only for 2a format repos. At least, I think that is what I see from your patch.

Otherwise I think this is good. Def. the sort of thing we want to land into bzr.dev and see it work for a while. So only minor tweaks needed.

review: Needs Fixing
Revision history for this message
Gary van der Merwe (garyvdm) wrote :

It will really be cool to have this.

Abstain, because I don't know enough about this code.

review: Abstain
Revision history for this message
Parth Malwankar (parthm) wrote :

>
> v- I'm pretty sure the copyright here should be just 2010.
>

Aah. The joys of copying and pasting :) ... Fixed.

>
> ^- and you should have an extra blank line on the top level between the module
> docstring and class...
>
>
> The NEWS entry should probably be clear that this is only for 2a format repos.
> At least, I think that is what I see from your patch.
>

Fixed. Thanks for the review.

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

A few nits

68 + self.STEP = 71

Well, two actually: why do you use ALLCAPS for step and where is this magic value coming from ?

81 + return int(key_count * 10.3)

You explain that it was obtained from empirical data but not how you calculated this value, let's document this piece of knowledge.

Since these tweaks are only documentation related, feel free to land when you're satisfied.

review: Approve
Revision history for this message
Parth Malwankar (parthm) wrote :

> A few nits
>
> 68 + self.STEP = 71
>
> Well, two actually: why do you use ALLCAPS for step and where is this magic
> value coming from ?
>
>
> 81 + return int(key_count * 10.3)
>
> You explain that it was obtained from empirical data but not how you
> calculated this value, let's document this piece of knowledge.
>

Thanks for the review Vincent. I have added comment of the reason
for choosing 10.3 and 71.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-05-19 02:20:50 +0000
3+++ NEWS 2010-05-19 13:33:35 +0000
4@@ -101,9 +101,9 @@
5 versions before 1.6.
6 (Andrew Bennetts, #528041)
7
8-* Heavyweight checkout operation now shows a message to the user indicating
9- history is being copied.
10- (Parth Malwankar, #538868)
11+* Improved progress bar for fetch (2a format only). Bazaar now shows an
12+ estimate of the number of records to be fetched vs actually fetched.
13+ (Parth Malwankar, #374740, #538868)
14
15 * Reduce peak memory by one copy of compressed text.
16 (John Arbash Meinel, #566940)
17
18=== modified file 'bzrlib/builtins.py'
19--- bzrlib/builtins.py 2010-05-14 13:39:47 +0000
20+++ bzrlib/builtins.py 2010-05-19 13:33:35 +0000
21@@ -1336,11 +1336,6 @@
22 except errors.NoWorkingTree:
23 source.bzrdir.create_workingtree(revision_id)
24 return
25-
26- if not lightweight:
27- message = ('Copying history to "%s". '
28- 'To checkout without local history use --lightweight.' % to_location)
29- ui.ui_factory.show_message(message)
30 source.create_checkout(to_location, revision_id, lightweight,
31 accelerator_tree, hardlink)
32
33
34=== added file 'bzrlib/recordcounter.py'
35--- bzrlib/recordcounter.py 1970-01-01 00:00:00 +0000
36+++ bzrlib/recordcounter.py 2010-05-19 13:33:35 +0000
37@@ -0,0 +1,86 @@
38+# Copyright (C) 2010 Canonical Ltd
39+#
40+# This program is free software; you can redistribute it and/or modify
41+# it under the terms of the GNU General Public License as published by
42+# the Free Software Foundation; either version 2 of the License, or
43+# (at your option) any later version.
44+#
45+# This program is distributed in the hope that it will be useful,
46+# but WITHOUT ANY WARRANTY; without even the implied warranty of
47+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48+# GNU General Public License for more details.
49+#
50+# You should have received a copy of the GNU General Public License
51+# along with this program; if not, write to the Free Software
52+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53+"""Record counting support for showing progress of revision fetch."""
54+
55+
56+class RecordCounter(object):
57+ """Container for maintains estimates of work requires for fetch.
58+
59+ Instance of this class is used along with a progress bar to provide
60+ the user an estimate of the amount of work pending for a fetch (push,
61+ pull, branch, checkout) operation.
62+ """
63+ def __init__(self):
64+ self.initialized = False
65+ self.current = 0
66+ self.key_count = 0
67+ self.max = 0
68+
69+ # Users of RecordCounter instance update progress bar every
70+ # _STEP_ records. We choose are reasonably high number to keep
71+ # display updates from being two frequent. This is an odd number
72+ # to ensure that the last digit of the records fetched in
73+ # fetches vs estimate ratio changes periodically.
74+ self.STEP = 71
75+
76+ def is_initialized(self):
77+ return self.initialized
78+
79+ def _estimate_max(self, key_count):
80+ """Estimate the maximum amount of 'inserting stream' work.
81+
82+ This is just an estimate.
83+ """
84+ # Note: The magic number below is based of empirical data
85+ # based on 3 seperate projects. Estimatation can probably
86+ # be improved but this should work well for most cases.
87+ # The project used for the estimate (with approx. numbers) were:
88+ # lp:bzr with records_fetched = 7 * revs_required
89+ # lp:emacs with records_fetched = 8 * revs_required
90+ # bzr-svn checkout of lp:parrot = 10.63 * revs_required
91+ # Hence, 10.3 was chosen as for a realistic progress bar as:
92+ # 1. If records fetched is is lower than 10.3x then we simply complete
93+ # with 10.3x. Under promise, over deliver.
94+ # 2. In case of remote fetch, when we start the count fetch vs estimate
95+ # display with revs_required/estimate, having a multiplier with a
96+ # decimal point produces a realistic looking _estimate_ number rather
97+ # than using something like 3125/31250 (for 10x)
98+ # 3. Based on the above data, the possibility of overshooting this
99+ # factor is minimal, and in case of an overshoot the estimate value
100+ # should not need to be corrected too many times.
101+ return int(key_count * 10.3)
102+
103+ def setup(self, key_count, current=0):
104+ """Setup RecordCounter with basic estimate of work pending.
105+
106+ Setup self.max and self.current to reflect the amount of work
107+ pending for a fetch.
108+ """
109+ self.current = current
110+ self.key_count = key_count
111+ self.max = self._estimate_max(key_count)
112+ self.initialized = True
113+
114+ def increment(self, count):
115+ """Increment self.current by count.
116+
117+ Apart from incrementing self.current by count, also ensure
118+ that self.max > self.current.
119+ """
120+ self.current += count
121+ if self.current > self.max:
122+ self.max += self.key_count
123+
124
125=== modified file 'bzrlib/remote.py'
126--- bzrlib/remote.py 2010-05-13 16:17:54 +0000
127+++ bzrlib/remote.py 2010-05-19 13:33:35 +0000
128@@ -1980,7 +1980,8 @@
129 if response_tuple[0] != 'ok':
130 raise errors.UnexpectedSmartServerResponse(response_tuple)
131 byte_stream = response_handler.read_streamed_body()
132- src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
133+ src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
134+ self._record_counter)
135 if src_format.network_name() != repo._format.network_name():
136 raise AssertionError(
137 "Mismatched RemoteRepository and stream src %r, %r" % (
138
139=== modified file 'bzrlib/repofmt/groupcompress_repo.py'
140--- bzrlib/repofmt/groupcompress_repo.py 2010-05-13 18:52:58 +0000
141+++ bzrlib/repofmt/groupcompress_repo.py 2010-05-19 13:33:35 +0000
142@@ -1108,13 +1108,29 @@
143 yield 'chk_bytes', _get_parent_id_basename_to_file_id_pages()
144
145 def get_stream(self, search):
146+ def wrap_and_count(pb, rc, stream):
147+ """Yield records from stream while showing progress."""
148+ count = 0
149+ for record in stream:
150+ if count == rc.STEP:
151+ rc.increment(count)
152+ pb.update('Estimate', rc.current, rc.max)
153+ count = 0
154+ count += 1
155+ yield record
156+
157 revision_ids = search.get_keys()
158+ pb = ui.ui_factory.nested_progress_bar()
159+ rc = self._record_counter
160+ self._record_counter.setup(len(revision_ids))
161 for stream_info in self._fetch_revision_texts(revision_ids):
162- yield stream_info
163+ yield (stream_info[0],
164+ wrap_and_count(pb, rc, stream_info[1]))
165 self._revision_keys = [(rev_id,) for rev_id in revision_ids]
166 self.from_repository.revisions.clear_cache()
167 self.from_repository.signatures.clear_cache()
168- yield self._get_inventory_stream(self._revision_keys)
169+ s = self._get_inventory_stream(self._revision_keys)
170+ yield (s[0], wrap_and_count(pb, rc, s[1]))
171 self.from_repository.inventories.clear_cache()
172 # TODO: The keys to exclude might be part of the search recipe
173 # For now, exclude all parents that are at the edge of ancestry, for
174@@ -1123,10 +1139,13 @@
175 parent_keys = from_repo._find_parent_keys_of_revisions(
176 self._revision_keys)
177 for stream_info in self._get_filtered_chk_streams(parent_keys):
178- yield stream_info
179+ yield (stream_info[0], wrap_and_count(pb, rc, stream_info[1]))
180 self.from_repository.chk_bytes.clear_cache()
181- yield self._get_text_stream()
182+ s = self._get_text_stream()
183+ yield (s[0], wrap_and_count(pb, rc, s[1]))
184 self.from_repository.texts.clear_cache()
185+ pb.update('Done', rc.max, rc.max)
186+ pb.finished()
187
188 def get_stream_for_missing_keys(self, missing_keys):
189 # missing keys can only occur when we are byte copying and not
190
191=== modified file 'bzrlib/repository.py'
192--- bzrlib/repository.py 2010-05-13 18:52:58 +0000
193+++ bzrlib/repository.py 2010-05-19 13:33:35 +0000
194@@ -43,7 +43,6 @@
195 symbol_versioning,
196 trace,
197 tsort,
198- ui,
199 versionedfile,
200 )
201 from bzrlib.bundle import serializer
202@@ -55,6 +54,7 @@
203 from bzrlib import (
204 errors,
205 registry,
206+ ui,
207 )
208 from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
209 from bzrlib.inter import InterObject
210@@ -64,6 +64,7 @@
211 ROOT_ID,
212 entry_factory,
213 )
214+from bzrlib.recordcounter import RecordCounter
215 from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
216 from bzrlib.trace import (
217 log_exception_quietly, note, mutter, mutter_callsite, warning)
218@@ -4283,7 +4284,8 @@
219 is_resume = False
220 try:
221 # locked_insert_stream performs a commit|suspend.
222- return self._locked_insert_stream(stream, src_format, is_resume)
223+ return self._locked_insert_stream(stream, src_format,
224+ is_resume)
225 except:
226 self.target_repo.abort_write_group(suppress_errors=True)
227 raise
228@@ -4336,8 +4338,7 @@
229 # required if the serializers are different only in terms of
230 # the inventory.
231 if src_serializer == to_serializer:
232- self.target_repo.revisions.insert_record_stream(
233- substream)
234+ self.target_repo.revisions.insert_record_stream(substream)
235 else:
236 self._extract_and_insert_revisions(substream,
237 src_serializer)
238@@ -4451,6 +4452,7 @@
239 """Create a StreamSource streaming from from_repository."""
240 self.from_repository = from_repository
241 self.to_format = to_format
242+ self._record_counter = RecordCounter()
243
244 def delta_on_metadata(self):
245 """Return True if delta's are permitted on metadata streams.
246
247=== modified file 'bzrlib/smart/repository.py'
248--- bzrlib/smart/repository.py 2010-05-06 23:41:35 +0000
249+++ bzrlib/smart/repository.py 2010-05-19 13:33:35 +0000
250@@ -39,6 +39,7 @@
251 SuccessfulSmartServerResponse,
252 )
253 from bzrlib.repository import _strip_NULL_ghosts, network_format_registry
254+from bzrlib.recordcounter import RecordCounter
255 from bzrlib import revision as _mod_revision
256 from bzrlib.versionedfile import (
257 NetworkRecordStream,
258@@ -544,12 +545,14 @@
259 :ivar first_bytes: The first bytes to give the next NetworkRecordStream.
260 """
261
262- def __init__(self, byte_stream):
263+ def __init__(self, byte_stream, record_counter):
264 """Create a _ByteStreamDecoder."""
265 self.stream_decoder = pack.ContainerPushParser()
266 self.current_type = None
267 self.first_bytes = None
268 self.byte_stream = byte_stream
269+ self._record_counter = record_counter
270+ self.key_count = 0
271
272 def iter_stream_decoder(self):
273 """Iterate the contents of the pack from stream_decoder."""
274@@ -580,13 +583,46 @@
275
276 def record_stream(self):
277 """Yield substream_type, substream from the byte stream."""
278+ def wrap_and_count(pb, rc, substream):
279+ """Yield records from stream while showing progress."""
280+ counter = 0
281+ if rc:
282+ if self.current_type != 'revisions' and self.key_count != 0:
283+ # As we know the number of revisions now (in self.key_count)
284+ # we can setup and use record_counter (rc).
285+ if not rc.is_initialized():
286+ rc.setup(self.key_count, self.key_count)
287+ for record in substream.read():
288+ if rc:
289+ if rc.is_initialized() and counter == rc.STEP:
290+ rc.increment(counter)
291+ pb.update('Estimate', rc.current, rc.max)
292+ counter = 0
293+ if self.current_type == 'revisions':
294+ # Total records is proportional to number of revs
295+ # to fetch. With remote, we used self.key_count to
296+ # track the number of revs. Once we have the revs
297+ # counts in self.key_count, the progress bar changes
298+ # from 'Estimating..' to 'Estimate' above.
299+ self.key_count += 1
300+ if counter == rc.STEP:
301+ pb.update('Estimating..', self.key_count)
302+ counter = 0
303+ counter += 1
304+ yield record
305+
306 self.seed_state()
307+ pb = ui.ui_factory.nested_progress_bar()
308+ rc = self._record_counter
309 # Make and consume sub generators, one per substream type:
310 while self.first_bytes is not None:
311 substream = NetworkRecordStream(self.iter_substream_bytes())
312 # after substream is fully consumed, self.current_type is set to
313 # the next type, and self.first_bytes is set to the matching bytes.
314- yield self.current_type, substream.read()
315+ yield self.current_type, wrap_and_count(pb, rc, substream)
316+ if rc:
317+ pb.update('Done', rc.max, rc.max)
318+ pb.finished()
319
320 def seed_state(self):
321 """Prepare the _ByteStreamDecoder to decode from the pack stream."""
322@@ -597,13 +633,13 @@
323 list(self.iter_substream_bytes())
324
325
326-def _byte_stream_to_stream(byte_stream):
327+def _byte_stream_to_stream(byte_stream, record_counter=None):
328 """Convert a byte stream into a format and a stream.
329
330 :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
331 :return: (RepositoryFormat, stream_generator)
332 """
333- decoder = _ByteStreamDecoder(byte_stream)
334+ decoder = _ByteStreamDecoder(byte_stream, record_counter)
335 for bytes in byte_stream:
336 decoder.stream_decoder.accept_bytes(bytes)
337 for record in decoder.stream_decoder.read_pending_records(max=1):
338
339=== modified file 'bzrlib/tests/blackbox/test_checkout.py'
340--- bzrlib/tests/blackbox/test_checkout.py 2010-04-30 09:52:08 +0000
341+++ bzrlib/tests/blackbox/test_checkout.py 2010-05-19 13:33:35 +0000
342@@ -65,7 +65,6 @@
343
344 def test_checkout_dash_r(self):
345 out, err = self.run_bzr(['checkout', '-r', '-2', 'branch', 'checkout'])
346- self.assertContainsRe(out, 'Copying history to "checkout".')
347 # the working tree should now be at revision '1' with the content
348 # from 1.
349 result = bzrdir.BzrDir.open('checkout')
350@@ -75,7 +74,6 @@
351 def test_checkout_light_dash_r(self):
352 out, err = self.run_bzr(['checkout','--lightweight', '-r', '-2',
353 'branch', 'checkout'])
354- self.assertNotContainsRe(out, 'Copying history')
355 # the working tree should now be at revision '1' with the content
356 # from 1.
357 result = bzrdir.BzrDir.open('checkout')