Merge lp:~spiv/bzr/fetch-spec-everything-not-in-other into lp:bzr

Proposed by Andrew Bennetts
Status: Merged
Merged at revision: 5648
Proposed branch: lp:~spiv/bzr/fetch-spec-everything-not-in-other
Merge into: lp:bzr
Diff against target: 247 lines (+32/-36)
10 files modified
bzrlib/fetch.py (+10/-15)
bzrlib/graph.py (+6/-5)
bzrlib/remote.py (+3/-3)
bzrlib/repofmt/knitrepo.py (+1/-1)
bzrlib/repofmt/weaverepo.py (+1/-1)
bzrlib/repository.py (+2/-2)
bzrlib/tests/per_interrepository/test_interrepository.py (+1/-1)
bzrlib/tests/test_remote.py (+3/-3)
doc/en/release-notes/bzr-2.3.txt (+0/-5)
doc/en/release-notes/bzr-2.4.txt (+5/-0)
To merge this branch: bzr merge lp:~spiv/bzr/fetch-spec-everything-not-in-other
Reviewer Review Type Date Requested Status
Martin Pool Approve
John A Meinel Pending
Review via email: mp+42811@code.launchpad.net

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

Commit message

Add EverythingResult, EverythingNotInOther, and NotInOtherForRevs.

Description of the change

Like <https://code.launchpad.net/~spiv/bzr/sprout-does-not-reopen-repo/+merge/41037> this is a step towards bug 309682 (i.e. is a pre-req for <lp:~spiv/bzr/fetch-all-tags-309682>, as well as being a general improvement.

This patch defines some new “fetch specs” to be passed as the fetch_spec argument of fetch: EverythingResult, EmptySearchResult, EverythingNotInOther, and NotInOtherForRevs. I've created two new abstract classes, AbstractSearchResult, and AbstractSearch, to distinguish between the two different sorts of fetch spec. I think the naming is a bit off, the original fetch spec was called “SearchResult”, which made some sense, but there's also “PendingAncestryResult” which isn't really a “result”. So perhaps we should rename some or all of these things. In the current scheme EverythingNotInOther and NotInOtherForRevs are Searches, rather than Search Results. Suggestions for clearer naming would be very welcome!

Separately I'm not 100% sure we need the ability to have fetch specs that aren't resolved into search results immediately (i.e. how AbstractSearch instances are used). This happens to be effectively how the existing code works: e.g. code that does "target.fetch(source, revision_id=some_rev)" has known the value of some_rev without performing a search, delegating that to the fetch call. I don't think it would it simplify the code much in practice to remove this flexibility, but maybe it would simplify the concepts enough to be worth it?

A nice thing about adding these objects is that it shifts logic to handle these different cases out of the innards of fetch.py to somewhere more modular. This in turn means we can support these things better over HPSS, and so this patch does that: the 'everything' search is now added to the network protocol. Happily new verb was needed, we can safely interpret a BadSearch error in this case as meaning we need to fallback.

Another change in this patch is the deprecation of search_missing_revision_ids' revision_id argument for the new revision_ids (plural).

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote : Posted in a previous version of this proposal

+ if not isinstance(search, graph.EverythingResult):
^- can we add an attribute, rather than an isinstance check?

if not search.is_everything():

490 + def _present_source_revisions_for(self, revision_ids):
491 + """Returns set of all revisions in ancestry of revision_ids present in
492 + the source repo.
493 +
494 + :param revision_ids: if None, all revisions in source are returned.
495 + """
496 + if revision_ids is not None:
497 + # First, ensure all specified revisions exist. Callers expect
498 + # NoSuchRevision when they pass absent revision_ids here.
499 + revision_ids = set(revision_ids)
500 + graph = self.source.get_graph()
501 + present_revs = set(graph.get_parent_map(revision_ids))
502 + missing = revision_ids.difference(present_revs)
503 + if missing:
504 + raise errors.NoSuchRevision(self.source, missing.pop())
505 + source_ids = [rev_id for (rev_id, parents) in
506 + self.source.get_graph().iter_ancestry(revision_ids)
507 + if rev_id != _mod_revision.NULL_REVISION
508 + and parents is not None]
^- you have "graph = self.source.get_graph()" and then you inline "self.source.get_graph()" into the source_ids list comprehension. Better to re-use the graph object.

145 + def get_recipe(self):
146 + raise NotImplementedError(self.get_recipe)
147 +
148 + def get_network_struct(self):
149 + return ('everything',)

Why don't EverythingNotInOther and NotInOtherForRevs implement these functions?

Overall, I think these changes seem good, but I don't really see how this gets us closer to computing a search without having one end doing a step-by-step search. (Certainly you're still using the same 'search_missing_revision_ids', and you don't seem to be serializing anything over the wire...)

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

John A Meinel wrote:
> Review: Needs Fixing
> + if not isinstance(search, graph.EverythingResult):
> ^- can we add an attribute, rather than an isinstance check?
>
> if not search.is_everything():

Hmm, I can do that, although currently there's no common base class for these
objects, and my gut feeling is that it would be good to keep their interface as
small and simple as possible. So I'm unsure about whether or not to do this. I
think it's probably easier to add this later than it is to remove it later.

> 490 + def _present_source_revisions_for(self, revision_ids):
> 491 + """Returns set of all revisions in ancestry of revision_ids present in
> 492 + the source repo.
> 493 +
> 494 + :param revision_ids: if None, all revisions in source are returned.
> 495 + """
> 496 + if revision_ids is not None:
> 497 + # First, ensure all specified revisions exist. Callers expect
> 498 + # NoSuchRevision when they pass absent revision_ids here.
> 499 + revision_ids = set(revision_ids)
> 500 + graph = self.source.get_graph()
> 501 + present_revs = set(graph.get_parent_map(revision_ids))
> 502 + missing = revision_ids.difference(present_revs)
> 503 + if missing:
> 504 + raise errors.NoSuchRevision(self.source, missing.pop())
> 505 + source_ids = [rev_id for (rev_id, parents) in
> 506 + self.source.get_graph().iter_ancestry(revision_ids)
> 507 + if rev_id != _mod_revision.NULL_REVISION
> 508 + and parents is not None]
> ^- you have "graph = self.source.get_graph()" and then you inline "self.source.get_graph()" into the source_ids list comprehension. Better to re-use the graph object.

Fixed, thanks for spotting that. I think it occurred because I initially
copy-and-pasted the list comprehension from graph.py.

> 145 + def get_recipe(self):
> 146 + raise NotImplementedError(self.get_recipe)
> 147 +
> 148 + def get_network_struct(self):
> 149 + return ('everything',)
>
> Why don't EverythingNotInOther and NotInOtherForRevs implement these functions?

Because they are a different kind of object. Obviously that's not clear enough
in the current code!

The names could stand to be improved, but there are basically two kinds of
“fetch spec” in this patch:

 * a network-ready search result: SearchResult, PendingAncestryResult,
   EverythingResult. They implement all the methods SearchResult implements
   (I'm not certain that's the perfect design, but it's not too far off.)
 * a lightweight object that can be resolved into a network-ready search result
   later: EverythingNotInOther, NotInOtherForRevs. They implement a get_search
   method that inspects the repository/ies to generate the search result.

> Overall, I think these changes seem good, but I don't really see how this gets
> us closer to computing a search without having one end doing a step-by-step
> search. (Certainly you're still using the same 'search_missing_revision_ids',
> and you don't seem to be serializing anything over the wire...)

Right, this change isn't meant to solve that problem. The concrete performance
issue they help with is in the next patch, fetch-all-tags-309682, to make it
and easy for the sprout code to request a fetch of all tags at the
same time as the tip.

Separately...

Read more...

Revision history for this message
Martin Pool (mbp) wrote :

I think I like the concept of splitting these out into something more like objects.

I don't want to hold this hostage but it seems like this would be a good moment to add a little developer documentation to the architectural guide describing at a high level how fetch works.

+ def get_network_struct(self):
+ start_keys = ' '.join(self._recipe[1])
+ stop_keys = ' '.join(self._recipe[2])
+ count = str(self._recipe[3])
+ return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
+

rather than adhoc encoding, could you send it as bencode?

+ medium._remember_remote_is_before((2, 3))

out of scope for this, but concluding it's older than 2.3 because it doesn't support this particular operation is really not accurate.

+ """See InterRepository.searcH_missing_revision_ids()."""

typo.

+ def test_fetch_everything_backwards_compat(self):
+ """Can fetch with EverythingResult even when the server does not have
+ the Repository.get_stream_2.3 verb.
+ """
+ local = self.make_branch('local')
+ builder = self.make_branch_builder('remote')
+ builder.build_commit(message="Commit.")
+ remote_branch_url = self.smart_server.get_url() + 'remote'
+ remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
+ self.hpss_calls = []
+ local.repository.fetch(remote_branch.repository,
+ fetch_spec=graph.EverythingResult(remote_branch.repository))
+

I don't understand how this tests that the server doesn't have this verb.

=== modified file 'doc/en/release-notes/bzr-2.3.txt'
--- doc/en/release-notes/bzr-2.3.txt 2010-12-14 23:32:28 +0000
+++ doc/en/release-notes/bzr-2.3.txt 2010-12-15 05:36:46 +0000
@@ -158,6 +158,11 @@
   crashes when encountering private bugs (they are just displayed as such).
   (Vincent Ladeuil, #354985)

+* The ``revision_id`` parameter of
+ ``Repository.search_missing_revision_ids`` and
+ ``InterRepository.search_missing_revision_ids`` is deprecated. It is
+ replaced by the ``revision_ids`` parameter. (Andrew Bennetts)
+
 Internals
 *********

spiv said on irc that he's testing this; if it turns out to be faster in practice that should be mentioned too. But I guess this patch alone won't change performance.

review: Needs Information
Revision history for this message
Andrew Bennetts (spiv) wrote :

Martin Pool wrote:
> Review: Needs Information
> I think I like the concept of splitting these out into something more like
> objects.
>
> I don't want to hold this hostage but it seems like this would be a good
> moment to add a little developer documentation to the architectural guide
> describing at a high level how fetch works.

Good idea. I'll see what I can write up without spending ages on it.

> + def get_network_struct(self):
> + start_keys = ' '.join(self._recipe[1])
> + stop_keys = ' '.join(self._recipe[2])
> + count = str(self._recipe[3])
> + return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
> +
>
> rather than adhoc encoding, could you send it as bencode?

For this struct: no, because this is how existing clients encode SearchResult in
get_stream requests. (And get_parent_map requests, but that's handled
separately in remote.py. See RemoteRepository._serialise_search_recipe.)

> + medium._remember_remote_is_before((2, 3))
>
> out of scope for this, but concluding it's older than 2.3 because it doesn't
> support this particular operation is really not accurate.

Yeah. But that is definitely out of scope :)

> + """See InterRepository.searcH_missing_revision_ids()."""
>
> typo.

Fixed, thanks.

> + def test_fetch_everything_backwards_compat(self):
> + """Can fetch with EverythingResult even when the server does not have
> + the Repository.get_stream_2.3 verb.
> + """
> + local = self.make_branch('local')
> + builder = self.make_branch_builder('remote')
> + builder.build_commit(message="Commit.")
> + remote_branch_url = self.smart_server.get_url() + 'remote'
> + remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
> + self.hpss_calls = []
> + local.repository.fetch(remote_branch.repository,
> + fetch_spec=graph.EverythingResult(remote_branch.repository))
> +
>
> I don't understand how this tests that the server doesn't have this verb.

Oops, out-of-date docstring and test. I didn't need a new verb in the end, I
just added more search types to the existing get_stream_1.19 verb. I'll fix
that: instead I should be testing that a BadSearch reply to the new search
triggers the old requests (i.e. VFS). Probably that's a bit too hard to mock
(because it involves VFS) so I might have to monkeypatch or add a knob to the
get_stream handler class...

[...]
> spiv said on irc that he's testing this; if it turns out to be faster in
> practice that should be mentioned too. But I guess this patch alone won't
> change performance.

I don't expect it to improve performance (unless compared to fetching all the
tags by hand with the existing bzr CLI!) either.

The concern jam had is that querying for all the tags on every update and other
operations might have a noticeable negative impact on branches with many tags
such as emacs. My guess and preliminary results are that this isn't a problem,
but I'll make sure before landing this.

-Andrew.

Revision history for this message
Andrew Bennetts (spiv) wrote :

In the case of lp:emacs there's no significant performance difference. The HPSS call count, memory consumption and wall clock time are unchanged (aside from noise in measurement). The HPSS request for the initial 'bzr branch' is larger, as it requests every rev named in the tags, not just tip, but that's a drop in the bucket vs. fetching the 239M of lp:emacs.

Also, lp:emacs has no tags pointing to missing revs (or even non-ancestry revs), so "bzr pull" works out identically with the new code vs. bzr.dev: all the revisions named in the source's tags are already present, so nothing changes. The wall clock time for the new code is again the same (within the noise of my measurements).

The main case I'd be concerned about is when the source branch has tags that refer to missing revisions. The target will copy those tags, and then on every update/pull re-request those tags from the source repository. Because those lookups in the revisions index will be misses, the server will end up opening every *.rix file. In the HPSS case that will happen server side, so it probably isn't a big concern there, but it might be an issue if using a dumb remote transport. The solution for affected branches is to delete the broken tags, or fill in the missing revisions. That's probably worth the benefit this gives, which is making tags Just Work in more cases. Branches and tags made with this new bzr version will of course fetch all tags by default, so that scenario won't be a concern for them.

In short: I'm confident. If it copes with a branch with as much history and tags as lp:emacs, it probably copes with everything else.

Revision history for this message
Andrew Bennetts (spiv) wrote :

> > I don't want to hold this hostage but it seems like this would be a good
> > moment to add a little developer documentation to the architectural guide
> > describing at a high level how fetch works.
>
> Good idea. I'll see what I can write up without spending ages on it.

Done: <https://code.launchpad.net/~spiv/bzr/fetch-dev-docs/+merge/46053>

I believe all the points/questions raised so far have now been answered.

Revision history for this message
Martin Pool (mbp) wrote :

I think it would be worth adding docstrings on AbstractSearchResult and SearchResult because the names are suggestive but not self-explanatory. The docstrings on the members are good. It is pretty well covered in your document (https://code.launchpad.net/~spiv/bzr/fetch-dev-docs/+merge/46053) so a sentence or two should be enough.

+ if symbol_versioning.deprecated_passed(revision_id):
+ symbol_versioning.warn(
+ 'search_missing_revision_ids(revision_id=...) was '
+ 'deprecated in 2.3. Use revision_ids=[...] instead.',
+ DeprecationWarning, stacklevel=2)

That will now be 2.4. It seems like we ought to have common handling of deprecated parameters. (https://bugs.launchpad.net/bzr/+bug/712922) And there's a couple more.

merge-with-tweaks

Thanks, and sorry for the delay.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/fetch.py'
2--- bzrlib/fetch.py 2011-01-14 17:18:23 +0000
3+++ bzrlib/fetch.py 2011-02-07 02:57:39 +0000
4@@ -55,6 +55,8 @@
5
6 :param last_revision: If set, try to limit to the data this revision
7 references.
8+ :param fetch_spec: A SearchResult specifying which revisions to fetch.
9+ If set, this overrides last_revision.
10 :param find_ghosts: If True search the entire history for ghosts.
11 """
12 # repository.fetch has the responsibility for short-circuiting
13@@ -93,12 +95,12 @@
14 pb.show_pct = pb.show_count = False
15 try:
16 pb.update("Finding revisions", 0, 2)
17- search = self._revids_to_fetch()
18- mutter('fetching: %s', search)
19- if search.is_empty():
20+ search_result = self._revids_to_fetch()
21+ mutter('fetching: %s', search_result)
22+ if search_result.is_empty():
23 return
24 pb.update("Fetching revisions", 1, 2)
25- self._fetch_everything_for_search(search)
26+ self._fetch_everything_for_search(search_result)
27 finally:
28 pb.finished()
29
30@@ -151,18 +153,11 @@
31 install self._last_revision in self.to_repository.
32
33 :returns: A SearchResult of some sort. (Possibly a
34- PendingAncestryResult, EmptySearchResult, etc.)
35+ PendingAncestryResult, EmptySearchResult, etc.)
36 """
37 mutter("self._fetch_spec, self._last_revision: %r, %r",
38 self._fetch_spec, self._last_revision)
39- get_search_result = getattr(self._fetch_spec, 'get_search_result', None)
40- if get_search_result is not None:
41- mutter(
42- 'resolving fetch_spec into search result: %s', self._fetch_spec)
43- # This is EverythingNotInOther or a similar kind of fetch_spec.
44- # Turn it into a search result.
45- return get_search_result()
46- elif self._fetch_spec is not None:
47+ if self._fetch_spec is not None:
48 # The fetch spec is already a concrete search result.
49 return self._fetch_spec
50 elif self._last_revision == NULL_REVISION:
51@@ -172,11 +167,11 @@
52 elif self._last_revision is not None:
53 return graph.NotInOtherForRevs(self.to_repository,
54 self.from_repository, [self._last_revision],
55- find_ghosts=self.find_ghosts).get_search_result()
56+ find_ghosts=self.find_ghosts).execute()
57 else: # self._last_revision is None:
58 return graph.EverythingNotInOther(self.to_repository,
59 self.from_repository,
60- find_ghosts=self.find_ghosts).get_search_result()
61+ find_ghosts=self.find_ghosts).execute()
62
63
64 class Inter1and2Helper(object):
65
66=== modified file 'bzrlib/graph.py'
67--- bzrlib/graph.py 2011-01-14 17:18:23 +0000
68+++ bzrlib/graph.py 2011-02-07 02:57:39 +0000
69@@ -1576,14 +1576,15 @@
70
71 class AbstractSearch(object):
72
73- def get_search_result(self):
74+ def execute(self):
75 """Construct a network-ready search result from this search description.
76
77 This may take some time to search repositories, etc.
78
79- :return: A search result.
80+ :return: A search result (an object that implements
81+ AbstractSearchResult's API).
82 """
83- raise NotImplementedError(self.get_search_result)
84+ raise NotImplementedError(self.execute)
85
86
87 class SearchResult(AbstractSearchResult):
88@@ -1813,7 +1814,7 @@
89 self.from_repo = from_repo
90 self.find_ghosts = find_ghosts
91
92- def get_search_result(self):
93+ def execute(self):
94 return self.to_repo.search_missing_revision_ids(
95 self.from_repo, find_ghosts=self.find_ghosts)
96
97@@ -1853,7 +1854,7 @@
98 self.__class__.__name__, self.from_repo, self.to_repo,
99 self.find_ghosts, reqd_revs_repr, ifp_revs_repr)
100
101- def get_search_result(self):
102+ def execute(self):
103 return self.to_repo.search_missing_revision_ids(
104 self.from_repo, revision_ids=self.required_ids,
105 if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts)
106
107=== modified file 'bzrlib/remote.py'
108--- bzrlib/remote.py 2011-01-14 17:18:23 +0000
109+++ bzrlib/remote.py 2011-02-07 02:57:39 +0000
110@@ -1360,7 +1360,7 @@
111 if symbol_versioning.deprecated_passed(revision_id):
112 symbol_versioning.warn(
113 'search_missing_revision_ids(revision_id=...) was '
114- 'deprecated in 2.3. Use revision_ids=[...] instead.',
115+ 'deprecated in 2.4. Use revision_ids=[...] instead.',
116 DeprecationWarning, stacklevel=2)
117 if revision_ids is not None:
118 raise AssertionError(
119@@ -1991,11 +1991,11 @@
120 if isinstance(search, graph.EverythingResult):
121 error_verb = e.error_from_smart_server.error_verb
122 if error_verb == 'BadSearch':
123- # Pre-2.3 servers don't support this sort of search.
124+ # Pre-2.4 servers don't support this sort of search.
125 # XXX: perhaps falling back to VFS on BadSearch is a
126 # good idea in general? It might provide a little bit
127 # of protection against client-side bugs.
128- medium._remember_remote_is_before((2, 3))
129+ medium._remember_remote_is_before((2, 4))
130 break
131 raise
132 else:
133
134=== modified file 'bzrlib/repofmt/knitrepo.py'
135--- bzrlib/repofmt/knitrepo.py 2010-12-21 06:10:11 +0000
136+++ bzrlib/repofmt/knitrepo.py 2011-02-07 02:57:39 +0000
137@@ -542,7 +542,7 @@
138 if symbol_versioning.deprecated_passed(revision_id):
139 symbol_versioning.warn(
140 'search_missing_revision_ids(revision_id=...) was '
141- 'deprecated in 2.3. Use revision_ids=[...] instead.',
142+ 'deprecated in 2.4. Use revision_ids=[...] instead.',
143 DeprecationWarning, stacklevel=2)
144 if revision_ids is not None:
145 raise AssertionError(
146
147=== modified file 'bzrlib/repofmt/weaverepo.py'
148--- bzrlib/repofmt/weaverepo.py 2011-01-14 17:18:23 +0000
149+++ bzrlib/repofmt/weaverepo.py 2011-02-07 02:57:39 +0000
150@@ -826,7 +826,7 @@
151 if symbol_versioning.deprecated_passed(revision_id):
152 symbol_versioning.warn(
153 'search_missing_revision_ids(revision_id=...) was '
154- 'deprecated in 2.3. Use revision_ids=[...] instead.',
155+ 'deprecated in 2.4. Use revision_ids=[...] instead.',
156 DeprecationWarning, stacklevel=2)
157 if revision_ids is not None:
158 raise AssertionError(
159
160=== modified file 'bzrlib/repository.py'
161--- bzrlib/repository.py 2011-01-19 23:00:12 +0000
162+++ bzrlib/repository.py 2011-02-07 02:57:39 +0000
163@@ -1608,7 +1608,7 @@
164 if symbol_versioning.deprecated_passed(revision_id):
165 symbol_versioning.warn(
166 'search_missing_revision_ids(revision_id=...) was '
167- 'deprecated in 2.3. Use revision_ids=[...] instead.',
168+ 'deprecated in 2.4. Use revision_ids=[...] instead.',
169 DeprecationWarning, stacklevel=3)
170 if revision_ids is not None:
171 raise AssertionError(
172@@ -3526,7 +3526,7 @@
173 if symbol_versioning.deprecated_passed(revision_id):
174 symbol_versioning.warn(
175 'search_missing_revision_ids(revision_id=...) was '
176- 'deprecated in 2.3. Use revision_ids=[...] instead.',
177+ 'deprecated in 2.4. Use revision_ids=[...] instead.',
178 DeprecationWarning, stacklevel=2)
179 if revision_ids is not None:
180 raise AssertionError(
181
182=== modified file 'bzrlib/tests/per_interrepository/test_interrepository.py'
183--- bzrlib/tests/per_interrepository/test_interrepository.py 2011-01-14 17:18:23 +0000
184+++ bzrlib/tests/per_interrepository/test_interrepository.py 2011-02-07 02:57:39 +0000
185@@ -138,7 +138,7 @@
186 find_ghosts=False)
187 self.callDeprecated(
188 ['search_missing_revision_ids(revision_id=...) was deprecated in '
189- '2.3. Use revision_ids=[...] instead.'],
190+ '2.4. Use revision_ids=[...] instead.'],
191 self.assertRaises, errors.NoSuchRevision,
192 repo_b.search_missing_revision_ids, repo_a, revision_id='pizza',
193 find_ghosts=False)
194
195=== modified file 'bzrlib/tests/test_remote.py'
196--- bzrlib/tests/test_remote.py 2011-01-14 17:18:23 +0000
197+++ bzrlib/tests/test_remote.py 2011-02-07 02:57:39 +0000
198@@ -3211,15 +3211,15 @@
199 override_existing=True)
200
201 def test_fetch_everything_backwards_compat(self):
202- """Can fetch with EverythingResult even with pre 2.3 servers.
203+ """Can fetch with EverythingResult even with pre 2.4 servers.
204
205- Pre-2.3 do not support 'everything' searches with the
206+ Pre-2.4 do not support 'everything' searches with the
207 Repository.get_stream_1.19 verb.
208 """
209 verb_log = []
210 class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
211 """A version of the Repository.get_stream_1.19 verb patched to
212- reject 'everything' searches the way 2.2 and earlier do.
213+ reject 'everything' searches the way 2.3 and earlier do.
214 """
215 def recreate_search(self, repository, search_bytes, discard_excess=False):
216 verb_log.append(search_bytes.split('\n', 1)[0])
217
218=== modified file 'doc/en/release-notes/bzr-2.3.txt'
219--- doc/en/release-notes/bzr-2.3.txt 2011-02-03 05:13:46 +0000
220+++ doc/en/release-notes/bzr-2.3.txt 2011-02-07 02:57:39 +0000
221@@ -275,11 +275,6 @@
222 crashes when encountering private bugs (they are just displayed as such).
223 (Vincent Ladeuil, #354985)
224
225-* The ``revision_id`` parameter of
226- ``Repository.search_missing_revision_ids`` and
227- ``InterRepository.search_missing_revision_ids`` is deprecated. It is
228- replaced by the ``revision_ids`` parameter. (Andrew Bennetts)
229-
230 Internals
231 *********
232
233
234=== modified file 'doc/en/release-notes/bzr-2.4.txt'
235--- doc/en/release-notes/bzr-2.4.txt 2011-02-03 05:46:08 +0000
236+++ doc/en/release-notes/bzr-2.4.txt 2011-02-07 02:57:39 +0000
237@@ -94,6 +94,11 @@
238 * Added ``bzrlib.mergetools`` module with helper functions for working with
239 the list of external merge tools. (Gordon Tyler, #489915)
240
241+* The ``revision_id`` parameter of
242+ ``Repository.search_missing_revision_ids`` and
243+ ``InterRepository.search_missing_revision_ids`` is deprecated. It is
244+ replaced by the ``revision_ids`` parameter. (Andrew Bennetts)
245+
246 Internals
247 *********
248