Merge lp:~mbp/bzr/704195-plugin-warnings into lp:bzr/2.3
- 704195-plugin-warnings
- Merge into 2.3
Proposed by
Martin Pool
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~mbp/bzr/704195-plugin-warnings | ||||
Merge into: | lp:bzr/2.3 | ||||
Diff against target: |
1763 lines (+877/-133) 30 files modified
bzr (+1/-1) bzrlib/__init__.py (+2/-2) bzrlib/branch.py (+1/-1) bzrlib/builtins.py (+3/-20) bzrlib/crash.py (+2/-6) bzrlib/fetch.py (+27/-9) bzrlib/graph.py (+182/-2) bzrlib/plugin.py (+51/-3) bzrlib/plugins/launchpad/lp_propose.py (+10/-8) bzrlib/remote.py (+30/-9) bzrlib/repofmt/knitrepo.py (+19/-11) bzrlib/repofmt/weaverepo.py (+20/-11) bzrlib/repository.py (+94/-18) bzrlib/smart/repository.py (+16/-0) bzrlib/smart/server.py (+1/-3) bzrlib/tests/blackbox/test_serve.py (+7/-10) bzrlib/tests/per_branch/test_push.py (+15/-0) bzrlib/tests/per_interrepository/test_interrepository.py (+9/-2) bzrlib/tests/per_repository_reference/test_fetch.py (+40/-1) bzrlib/tests/test_crash.py (+9/-0) bzrlib/tests/test_plugins.py (+18/-4) bzrlib/tests/test_remote.py (+59/-4) bzrlib/tests/test_repository.py (+1/-1) bzrlib/tests/test_server.py (+0/-6) bzrlib/tests/test_smart.py (+66/-1) doc/developers/fetch.txt (+86/-0) doc/developers/index.txt (+1/-0) doc/en/release-notes/bzr-2.3.txt (+10/-0) doc/en/release-notes/bzr-2.4.txt (+64/-0) doc/en/whats-new/whats-new-in-2.4.txt (+33/-0) |
||||
To merge this branch: | bzr merge lp:~mbp/bzr/704195-plugin-warnings | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
bzr-core | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2011-01-19.
Commit message
Description of the change
This stops bzr spamming you about out-of-date plugins. Telling people on every single invocation is not helpful. Now we just tell you on in `bzr plugins`.
As a follow-on in bug 704238 we can try to load them anyhow.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bzr' |
2 | --- bzr 2010-11-26 17:59:08 +0000 |
3 | +++ bzr 2011-01-19 00:31:58 +0000 |
4 | @@ -23,7 +23,7 @@ |
5 | import warnings |
6 | |
7 | # update this on each release |
8 | -_script_version = (2, 3, 0) |
9 | +_script_version = (2, 4, 0) |
10 | |
11 | try: |
12 | version_info = sys.version_info |
13 | |
14 | === modified file 'bzrlib/__init__.py' |
15 | --- bzrlib/__init__.py 2011-01-14 23:20:15 +0000 |
16 | +++ bzrlib/__init__.py 2011-01-19 00:31:58 +0000 |
17 | @@ -52,10 +52,10 @@ |
18 | # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a |
19 | # releaselevel of 'dev' for unreleased under-development code. |
20 | |
21 | -version_info = (2, 3, 0, 'dev', 6) |
22 | +version_info = (2, 4, 0, 'dev', 1) |
23 | |
24 | # API compatibility version |
25 | -api_minimum_version = (2, 3, 0) |
26 | +api_minimum_version = (2, 4, 0) |
27 | |
28 | |
29 | def _format_version_tuple(version_info): |
30 | |
31 | === modified file 'bzrlib/branch.py' |
32 | --- bzrlib/branch.py 2011-01-12 01:01:53 +0000 |
33 | +++ bzrlib/branch.py 2011-01-19 00:31:58 +0000 |
34 | @@ -2664,7 +2664,7 @@ |
35 | result.target_branch = target |
36 | result.old_revno, result.old_revid = target.last_revision_info() |
37 | self.update_references(target) |
38 | - if result.old_revid != self.last_revision(): |
39 | + if result.old_revid != stop_revision: |
40 | # We assume that during 'push' this repository is closer than |
41 | # the target. |
42 | graph = self.repository.get_graph(target.repository) |
43 | |
44 | === modified file 'bzrlib/builtins.py' |
45 | --- bzrlib/builtins.py 2011-01-12 01:01:53 +0000 |
46 | +++ bzrlib/builtins.py 2011-01-19 00:31:58 +0000 |
47 | @@ -4581,26 +4581,9 @@ |
48 | |
49 | @display_command |
50 | def run(self, verbose=False): |
51 | - import bzrlib.plugin |
52 | - from inspect import getdoc |
53 | - result = [] |
54 | - for name, plugin in bzrlib.plugin.plugins().items(): |
55 | - version = plugin.__version__ |
56 | - if version == 'unknown': |
57 | - version = '' |
58 | - name_ver = '%s %s' % (name, version) |
59 | - d = getdoc(plugin.module) |
60 | - if d: |
61 | - doc = d.split('\n')[0] |
62 | - else: |
63 | - doc = '(no description)' |
64 | - result.append((name_ver, doc, plugin.path())) |
65 | - for name_ver, doc, path in sorted(result): |
66 | - self.outf.write("%s\n" % name_ver) |
67 | - self.outf.write(" %s\n" % doc) |
68 | - if verbose: |
69 | - self.outf.write(" %s\n" % path) |
70 | - self.outf.write("\n") |
71 | + from bzrlib import plugin |
72 | + self.outf.writelines( |
73 | + plugin.describe_loaded_plugins(show_paths=verbose)) |
74 | |
75 | |
76 | class cmd_testament(Command): |
77 | |
78 | === modified file 'bzrlib/crash.py' |
79 | --- bzrlib/crash.py 2010-09-28 18:51:47 +0000 |
80 | +++ bzrlib/crash.py 2011-01-19 00:31:58 +0000 |
81 | @@ -1,4 +1,4 @@ |
82 | -# Copyright (C) 2009, 2010 Canonical Ltd |
83 | +# Copyright (C) 2009, 2010, 2011 Canonical Ltd |
84 | # |
85 | # This program is free software; you can redistribute it and/or modify |
86 | # it under the terms of the GNU General Public License as published by |
87 | @@ -254,11 +254,7 @@ |
88 | |
89 | |
90 | def _format_plugin_list(): |
91 | - plugin_lines = [] |
92 | - for name, a_plugin in sorted(plugin.plugins().items()): |
93 | - plugin_lines.append(" %-20s %s [%s]" % |
94 | - (name, a_plugin.path(), a_plugin.__version__)) |
95 | - return '\n'.join(plugin_lines) |
96 | + return ''.join(plugin.describe_loaded_plugins(show_paths=True)) |
97 | |
98 | |
99 | def _format_module_list(): |
100 | |
101 | === modified file 'bzrlib/fetch.py' |
102 | --- bzrlib/fetch.py 2011-01-10 22:20:12 +0000 |
103 | +++ bzrlib/fetch.py 2011-01-19 00:31:58 +0000 |
104 | @@ -28,6 +28,7 @@ |
105 | from bzrlib.lazy_import import lazy_import |
106 | lazy_import(globals(), """ |
107 | from bzrlib import ( |
108 | + graph, |
109 | tsort, |
110 | versionedfile, |
111 | ) |
112 | @@ -93,7 +94,8 @@ |
113 | try: |
114 | pb.update("Finding revisions", 0, 2) |
115 | search = self._revids_to_fetch() |
116 | - if search is None: |
117 | + mutter('fetching: %s', search) |
118 | + if search.is_empty(): |
119 | return |
120 | pb.update("Fetching revisions", 1, 2) |
121 | self._fetch_everything_for_search(search) |
122 | @@ -148,17 +150,33 @@ |
123 | """Determines the exact revisions needed from self.from_repository to |
124 | install self._last_revision in self.to_repository. |
125 | |
126 | - If no revisions need to be fetched, then this just returns None. |
127 | + :returns: A SearchResult of some sort. (Possibly a |
128 | + PendingAncestryResult, EmptySearchResult, etc.) |
129 | """ |
130 | - if self._fetch_spec is not None: |
131 | + mutter("self._fetch_spec, self._last_revision: %r, %r", |
132 | + self._fetch_spec, self._last_revision) |
133 | + get_search_result = getattr(self._fetch_spec, 'get_search_result', None) |
134 | + if get_search_result is not None: |
135 | + mutter( |
136 | + 'resolving fetch_spec into search result: %s', self._fetch_spec) |
137 | + # This is EverythingNotInOther or a similar kind of fetch_spec. |
138 | + # Turn it into a search result. |
139 | + return get_search_result() |
140 | + elif self._fetch_spec is not None: |
141 | + # The fetch spec is already a concrete search result. |
142 | return self._fetch_spec |
143 | - mutter('fetch up to rev {%s}', self._last_revision) |
144 | - if self._last_revision is NULL_REVISION: |
145 | + elif self._last_revision == NULL_REVISION: |
146 | + # fetch_spec is None + last_revision is null => empty fetch. |
147 | # explicit limit of no revisions needed |
148 | - return None |
149 | - return self.to_repository.search_missing_revision_ids( |
150 | - self.from_repository, self._last_revision, |
151 | - find_ghosts=self.find_ghosts) |
152 | + return graph.EmptySearchResult() |
153 | + elif self._last_revision is not None: |
154 | + return graph.NotInOtherForRevs(self.to_repository, |
155 | + self.from_repository, [self._last_revision], |
156 | + find_ghosts=self.find_ghosts).get_search_result() |
157 | + else: # self._last_revision is None: |
158 | + return graph.EverythingNotInOther(self.to_repository, |
159 | + self.from_repository, |
160 | + find_ghosts=self.find_ghosts).get_search_result() |
161 | |
162 | |
163 | class Inter1and2Helper(object): |
164 | |
165 | === modified file 'bzrlib/graph.py' |
166 | --- bzrlib/graph.py 2011-01-10 22:20:12 +0000 |
167 | +++ bzrlib/graph.py 2011-01-19 00:31:58 +0000 |
168 | @@ -1536,7 +1536,57 @@ |
169 | return revs, ghosts |
170 | |
171 | |
172 | -class SearchResult(object): |
173 | +class AbstractSearchResult(object): |
174 | + |
175 | + def get_recipe(self): |
176 | + """Return a recipe that can be used to replay this search. |
177 | + |
178 | + The recipe allows reconstruction of the same results at a later date. |
179 | + |
180 | + :return: A tuple of (search_kind_str, *details). The details vary by |
181 | + kind of search result. |
182 | + """ |
183 | + raise NotImplementedError(self.get_recipe) |
184 | + |
185 | + def get_network_struct(self): |
186 | + """Return a tuple that can be transmitted via the HPSS protocol.""" |
187 | + raise NotImplementedError(self.get_network_struct) |
188 | + |
189 | + def get_keys(self): |
190 | + """Return the keys found in this search. |
191 | + |
192 | + :return: A set of keys. |
193 | + """ |
194 | + raise NotImplementedError(self.get_keys) |
195 | + |
196 | + def is_empty(self): |
197 | + """Return false if the search lists 1 or more revisions.""" |
198 | + raise NotImplementedError(self.is_empty) |
199 | + |
200 | + def refine(self, seen, referenced): |
201 | + """Create a new search by refining this search. |
202 | + |
203 | + :param seen: Revisions that have been satisfied. |
204 | + :param referenced: Revision references observed while satisfying some |
205 | + of this search. |
206 | + :return: A search result. |
207 | + """ |
208 | + raise NotImplementedError(self.refine) |
209 | + |
210 | + |
211 | +class AbstractSearch(object): |
212 | + |
213 | + def get_search_result(self): |
214 | + """Construct a network-ready search result from this search description. |
215 | + |
216 | + This may take some time to search repositories, etc. |
217 | + |
218 | + :return: A search result. |
219 | + """ |
220 | + raise NotImplementedError(self.get_search_result) |
221 | + |
222 | + |
223 | +class SearchResult(AbstractSearchResult): |
224 | """The result of a breadth first search. |
225 | |
226 | A SearchResult provides the ability to reconstruct the search or access a |
227 | @@ -1557,6 +1607,19 @@ |
228 | self._recipe = ('search', start_keys, exclude_keys, key_count) |
229 | self._keys = frozenset(keys) |
230 | |
231 | + def __repr__(self): |
232 | + kind, start_keys, exclude_keys, key_count = self._recipe |
233 | + if len(start_keys) > 5: |
234 | + start_keys_repr = repr(list(start_keys)[:5])[:-1] + ', ...]' |
235 | + else: |
236 | + start_keys_repr = repr(start_keys) |
237 | + if len(exclude_keys) > 5: |
238 | + exclude_keys_repr = repr(list(exclude_keys)[:5])[:-1] + ', ...]' |
239 | + else: |
240 | + exclude_keys_repr = repr(exclude_keys) |
241 | + return '<%s %s:(%s, %s, %d)>' % (self.__class__.__name__, |
242 | + kind, start_keys_repr, exclude_keys_repr, key_count) |
243 | + |
244 | def get_recipe(self): |
245 | """Return a recipe that can be used to replay this search. |
246 | |
247 | @@ -1580,6 +1643,12 @@ |
248 | """ |
249 | return self._recipe |
250 | |
251 | + def get_network_struct(self): |
252 | + start_keys = ' '.join(self._recipe[1]) |
253 | + stop_keys = ' '.join(self._recipe[2]) |
254 | + count = str(self._recipe[3]) |
255 | + return (self._recipe[0], '\n'.join((start_keys, stop_keys, count))) |
256 | + |
257 | def get_keys(self): |
258 | """Return the keys found in this search. |
259 | |
260 | @@ -1617,7 +1686,7 @@ |
261 | return SearchResult(pending_refs, exclude, count, keys) |
262 | |
263 | |
264 | -class PendingAncestryResult(object): |
265 | +class PendingAncestryResult(AbstractSearchResult): |
266 | """A search result that will reconstruct the ancestry for some graph heads. |
267 | |
268 | Unlike SearchResult, this doesn't hold the complete search result in |
269 | @@ -1634,6 +1703,14 @@ |
270 | self.heads = frozenset(heads) |
271 | self.repo = repo |
272 | |
273 | + def __repr__(self): |
274 | + if len(self.heads) > 5: |
275 | + heads_repr = repr(list(self.heads)[:5] + ', ...]') |
276 | + else: |
277 | + heads_repr = repr(self.heads) |
278 | + return '<%s heads:%s repo:%r>' % ( |
279 | + self.__class__.__name__, heads_repr, self.repo) |
280 | + |
281 | def get_recipe(self): |
282 | """Return a recipe that can be used to replay this search. |
283 | |
284 | @@ -1647,6 +1724,11 @@ |
285 | """ |
286 | return ('proxy-search', self.heads, set(), -1) |
287 | |
288 | + def get_network_struct(self): |
289 | + parts = ['ancestry-of'] |
290 | + parts.extend(self.heads) |
291 | + return parts |
292 | + |
293 | def get_keys(self): |
294 | """See SearchResult.get_keys. |
295 | |
296 | @@ -1679,6 +1761,104 @@ |
297 | return PendingAncestryResult(referenced - seen, self.repo) |
298 | |
299 | |
300 | +class EmptySearchResult(AbstractSearchResult): |
301 | + """An empty search result.""" |
302 | + |
303 | + def is_empty(self): |
304 | + return True |
305 | + |
306 | + |
307 | +class EverythingResult(AbstractSearchResult): |
308 | + """A search result that simply requests everything in the repository.""" |
309 | + |
310 | + def __init__(self, repo): |
311 | + self._repo = repo |
312 | + |
313 | + def __repr__(self): |
314 | + return '%s(%r)' % (self.__class__.__name__, self._repo) |
315 | + |
316 | + def get_recipe(self): |
317 | + raise NotImplementedError(self.get_recipe) |
318 | + |
319 | + def get_network_struct(self): |
320 | + return ('everything',) |
321 | + |
322 | + def get_keys(self): |
323 | + if 'evil' in debug.debug_flags: |
324 | + from bzrlib import remote |
325 | + if isinstance(self._repo, remote.RemoteRepository): |
326 | + # warn developers (not users) not to do this |
327 | + trace.mutter_callsite( |
328 | + 2, "EverythingResult(RemoteRepository).get_keys() is slow.") |
329 | + return self._repo.all_revision_ids() |
330 | + |
331 | + def is_empty(self): |
332 | + # It's ok for this to wrongly return False: the worst that can happen |
333 | + # is that RemoteStreamSource will initiate a get_stream on an empty |
334 | + # repository. And almost all repositories are non-empty. |
335 | + return False |
336 | + |
337 | + def refine(self, seen, referenced): |
338 | + heads = set(self._repo.all_revision_ids()) |
339 | + heads.difference_update(seen) |
340 | + heads.update(referenced) |
341 | + return PendingAncestryResult(heads, self._repo) |
342 | + |
343 | + |
344 | +class EverythingNotInOther(AbstractSearch): |
345 | + """Find all revisions in that are in one repo but not the other.""" |
346 | + |
347 | + def __init__(self, to_repo, from_repo, find_ghosts=False): |
348 | + self.to_repo = to_repo |
349 | + self.from_repo = from_repo |
350 | + self.find_ghosts = find_ghosts |
351 | + |
352 | + def get_search_result(self): |
353 | + return self.to_repo.search_missing_revision_ids( |
354 | + self.from_repo, find_ghosts=self.find_ghosts) |
355 | + |
356 | + |
357 | +class NotInOtherForRevs(AbstractSearch): |
358 | + """Find all revisions missing in one repo for a some specific heads.""" |
359 | + |
360 | + def __init__(self, to_repo, from_repo, required_ids, if_present_ids=None, |
361 | + find_ghosts=False): |
362 | + """Constructor. |
363 | + |
364 | + :param required_ids: revision IDs of heads that must be found, or else |
365 | + the search will fail with NoSuchRevision. All revisions in their |
366 | + ancestry not already in the other repository will be included in |
367 | + the search result. |
368 | + :param if_present_ids: revision IDs of heads that may be absent in the |
369 | + source repository. If present, then their ancestry not already |
370 | + found in other will be included in the search result. |
371 | + """ |
372 | + self.to_repo = to_repo |
373 | + self.from_repo = from_repo |
374 | + self.find_ghosts = find_ghosts |
375 | + self.required_ids = required_ids |
376 | + self.if_present_ids = if_present_ids |
377 | + |
378 | + def __repr__(self): |
379 | + if len(self.required_ids) > 5: |
380 | + reqd_revs_repr = repr(list(self.required_ids)[:5])[:-1] + ', ...]' |
381 | + else: |
382 | + reqd_revs_repr = repr(self.required_ids) |
383 | + if self.if_present_ids and len(self.if_present_ids) > 5: |
384 | + ifp_revs_repr = repr(list(self.if_present_ids)[:5])[:-1] + ', ...]' |
385 | + else: |
386 | + ifp_revs_repr = repr(self.if_present_ids) |
387 | + |
388 | + return "<%s from:%r to:%r find_ghosts:%r req'd:%r if-present:%r>" % ( |
389 | + self.__class__.__name__, self.from_repo, self.to_repo, |
390 | + self.find_ghosts, reqd_revs_repr, ifp_revs_repr) |
391 | + |
392 | + def get_search_result(self): |
393 | + return self.to_repo.search_missing_revision_ids( |
394 | + self.from_repo, revision_ids=self.required_ids, |
395 | + if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts) |
396 | + |
397 | + |
398 | def collapse_linear_regions(parent_map): |
399 | """Collapse regions of the graph that are 'linear'. |
400 | |
401 | |
402 | === modified file 'bzrlib/plugin.py' |
403 | --- bzrlib/plugin.py 2010-06-11 08:02:42 +0000 |
404 | +++ bzrlib/plugin.py 2011-01-19 00:31:58 +0000 |
405 | @@ -1,4 +1,4 @@ |
406 | -# Copyright (C) 2005-2010 Canonical Ltd |
407 | +# Copyright (C) 2005-2011 Canonical Ltd |
408 | # |
409 | # This program is free software; you can redistribute it and/or modify |
410 | # it under the terms of the GNU General Public License as published by |
411 | @@ -63,6 +63,11 @@ |
412 | _plugins_disabled = False |
413 | |
414 | |
415 | +plugin_warnings = {} |
416 | +# Map from plugin name, to list of string warnings about eg plugin |
417 | +# dependencies. |
418 | + |
419 | + |
420 | def are_plugins_disabled(): |
421 | return _plugins_disabled |
422 | |
423 | @@ -77,6 +82,40 @@ |
424 | load_plugins([]) |
425 | |
426 | |
427 | +def describe_loaded_plugins(show_paths=False): |
428 | + """Generate text description of loaded plugins. |
429 | + |
430 | + :param show_paths: If true, |
431 | + :returns: Iterator of text lines (including newlines.) |
432 | + """ |
433 | + from inspect import getdoc |
434 | + unreported_warnings = plugin_warnings.copy() |
435 | + for name, plugin in sorted(plugins().items()): |
436 | + version = plugin.__version__ |
437 | + if version == 'unknown': |
438 | + version = '' |
439 | + name_ver = '%s %s' % (name, version) |
440 | + d = getdoc(plugin.module) |
441 | + if d: |
442 | + doc = d.split('\n')[0] |
443 | + else: |
444 | + doc = '(no description)' |
445 | + yield ("%s\n" % name_ver) |
446 | + yield (" %s\n" % doc) |
447 | + if show_paths: |
448 | + yield (" %s\n" % plugin.path()) |
449 | + if name in unreported_warnings: |
450 | + for line in unreported_warnings[name]: |
451 | + yield " ** " + line + '\n' |
452 | + del unreported_warnings[name] |
453 | + yield ("\n") |
454 | + for name in sorted(unreported_warnings.keys()): |
455 | + yield "%s (failed to load)\n" % name |
456 | + for line in unreported_warnings[name]: |
457 | + yield " ** " + line + '\n' |
458 | + yield '\n' |
459 | + |
460 | + |
461 | def _strip_trailing_sep(path): |
462 | return path.rstrip("\\/") |
463 | |
464 | @@ -327,6 +366,11 @@ |
465 | return None, None, (None, None, None) |
466 | |
467 | |
468 | +def record_plugin_warning(plugin_name, warning_message): |
469 | + trace.mutter(warning_message) |
470 | + plugin_warnings.setdefault(plugin_name, []).append(warning_message) |
471 | + |
472 | + |
473 | def _load_plugin_module(name, dir): |
474 | """Load plugin name from dir. |
475 | |
476 | @@ -340,10 +384,12 @@ |
477 | except KeyboardInterrupt: |
478 | raise |
479 | except errors.IncompatibleAPI, e: |
480 | - trace.warning("Unable to load plugin %r. It requested API version " |
481 | + warning_message = ( |
482 | + "Unable to load plugin %r. It requested API version " |
483 | "%s of module %s but the minimum exported version is %s, and " |
484 | "the maximum is %s" % |
485 | (name, e.wanted, e.api, e.minimum, e.current)) |
486 | + record_plugin_warning(name, warning_message) |
487 | except Exception, e: |
488 | trace.warning("%s" % e) |
489 | if re.search('\.|-| ', name): |
490 | @@ -354,7 +400,9 @@ |
491 | "file path isn't a valid module name; try renaming " |
492 | "it to %r." % (name, dir, sanitised_name)) |
493 | else: |
494 | - trace.warning('Unable to load plugin %r from %r' % (name, dir)) |
495 | + record_plugin_warning( |
496 | + name, |
497 | + 'Unable to load plugin %r from %r' % (name, dir)) |
498 | trace.log_exception_quietly() |
499 | if 'error' in debug.debug_flags: |
500 | trace.print_exception(sys.exc_info(), sys.stderr) |
501 | |
502 | === modified file 'bzrlib/plugins/launchpad/lp_propose.py' |
503 | --- bzrlib/plugins/launchpad/lp_propose.py 2010-12-02 10:41:05 +0000 |
504 | +++ bzrlib/plugins/launchpad/lp_propose.py 2011-01-19 00:31:58 +0000 |
505 | @@ -14,21 +14,22 @@ |
506 | # along with this program; if not, write to the Free Software |
507 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
508 | |
509 | - |
510 | -import webbrowser |
511 | - |
512 | from bzrlib import ( |
513 | errors, |
514 | hooks, |
515 | + ) |
516 | +from bzrlib.lazy_import import lazy_import |
517 | +lazy_import(globals(), """ |
518 | +import webbrowser |
519 | + |
520 | +from bzrlib import ( |
521 | msgeditor, |
522 | ) |
523 | from bzrlib.plugins.launchpad import ( |
524 | lp_api, |
525 | lp_registration, |
526 | ) |
527 | -from bzrlib.plugins.launchpad.lp_api import canonical_url |
528 | - |
529 | -from lazr.restfulclient import errors as restful_errors |
530 | +""") |
531 | |
532 | |
533 | class ProposeMergeHooks(hooks.Hooks): |
534 | @@ -153,7 +154,7 @@ |
535 | if mp.target_branch.self_link == self.target_branch.lp.self_link: |
536 | raise errors.BzrCommandError( |
537 | 'There is already a branch merge proposal: %s' % |
538 | - canonical_url(mp)) |
539 | + lp_api.canonical_url(mp)) |
540 | |
541 | def _get_prerequisite_branch(self): |
542 | hooks = self.hooks['get_prerequisite'] |
543 | @@ -174,6 +175,7 @@ |
544 | :param **kwargs: **kwargs for the call. |
545 | :return: The result of calling call(*args, *kwargs). |
546 | """ |
547 | + from lazr.restfulclient import errors as restful_errors |
548 | try: |
549 | return call(*args, **kwargs) |
550 | except restful_errors.HTTPError, e: |
551 | @@ -208,7 +210,7 @@ |
552 | review_types=review_types) |
553 | if self.approve: |
554 | self.call_webservice(mp.setStatus, status='Approved') |
555 | - webbrowser.open(canonical_url(mp)) |
556 | + webbrowser.open(lp_api.canonical_url(mp)) |
557 | |
558 | |
559 | def modified_files(old_tree, new_tree): |
560 | |
561 | === modified file 'bzrlib/remote.py' |
562 | --- bzrlib/remote.py 2011-01-10 22:20:12 +0000 |
563 | +++ bzrlib/remote.py 2011-01-19 00:31:58 +0000 |
564 | @@ -1348,15 +1348,29 @@ |
565 | return result |
566 | |
567 | @needs_read_lock |
568 | - def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True): |
569 | + def search_missing_revision_ids(self, other, |
570 | + revision_id=symbol_versioning.DEPRECATED_PARAMETER, |
571 | + find_ghosts=True, revision_ids=None, if_present_ids=None): |
572 | """Return the revision ids that other has that this does not. |
573 | |
574 | These are returned in topological order. |
575 | |
576 | revision_id: only return revision ids included by revision_id. |
577 | """ |
578 | - return repository.InterRepository.get( |
579 | - other, self).search_missing_revision_ids(revision_id, find_ghosts) |
580 | + if symbol_versioning.deprecated_passed(revision_id): |
581 | + symbol_versioning.warn( |
582 | + 'search_missing_revision_ids(revision_id=...) was ' |
583 | + 'deprecated in 2.3. Use revision_ids=[...] instead.', |
584 | + DeprecationWarning, stacklevel=2) |
585 | + if revision_ids is not None: |
586 | + raise AssertionError( |
587 | + 'revision_ids is mutually exclusive with revision_id') |
588 | + if revision_id is not None: |
589 | + revision_ids = [revision_id] |
590 | + inter_repo = repository.InterRepository.get(other, self) |
591 | + return inter_repo.search_missing_revision_ids( |
592 | + find_ghosts=find_ghosts, revision_ids=revision_ids, |
593 | + if_present_ids=if_present_ids) |
594 | |
595 | def fetch(self, source, revision_id=None, pb=None, find_ghosts=False, |
596 | fetch_spec=None): |
597 | @@ -1763,12 +1777,7 @@ |
598 | return '\n'.join((start_keys, stop_keys, count)) |
599 | |
600 | def _serialise_search_result(self, search_result): |
601 | - if isinstance(search_result, graph.PendingAncestryResult): |
602 | - parts = ['ancestry-of'] |
603 | - parts.extend(search_result.heads) |
604 | - else: |
605 | - recipe = search_result.get_recipe() |
606 | - parts = [recipe[0], self._serialise_search_recipe(recipe)] |
607 | + parts = search_result.get_network_struct() |
608 | return '\n'.join(parts) |
609 | |
610 | def autopack(self): |
611 | @@ -1968,6 +1977,7 @@ |
612 | candidate_verbs = [ |
613 | ('Repository.get_stream_1.19', (1, 19)), |
614 | ('Repository.get_stream', (1, 13))] |
615 | + |
616 | found_verb = False |
617 | for verb, version in candidate_verbs: |
618 | if medium._is_remote_before(version): |
619 | @@ -1977,6 +1987,17 @@ |
620 | verb, args, search_bytes) |
621 | except errors.UnknownSmartMethod: |
622 | medium._remember_remote_is_before(version) |
623 | + except errors.UnknownErrorFromSmartServer, e: |
624 | + if isinstance(search, graph.EverythingResult): |
625 | + error_verb = e.error_from_smart_server.error_verb |
626 | + if error_verb == 'BadSearch': |
627 | + # Pre-2.3 servers don't support this sort of search. |
628 | + # XXX: perhaps falling back to VFS on BadSearch is a |
629 | + # good idea in general? It might provide a little bit |
630 | + # of protection against client-side bugs. |
631 | + medium._remember_remote_is_before((2, 3)) |
632 | + break |
633 | + raise |
634 | else: |
635 | response_tuple, response_handler = response |
636 | found_verb = True |
637 | |
638 | === modified file 'bzrlib/repofmt/knitrepo.py' |
639 | --- bzrlib/repofmt/knitrepo.py 2010-11-20 21:41:05 +0000 |
640 | +++ bzrlib/repofmt/knitrepo.py 2011-01-19 00:31:58 +0000 |
641 | @@ -43,6 +43,7 @@ |
642 | RepositoryFormat, |
643 | RootCommitBuilder, |
644 | ) |
645 | +from bzrlib import symbol_versioning |
646 | |
647 | |
648 | class _KnitParentsProvider(object): |
649 | @@ -534,16 +535,23 @@ |
650 | return are_knits and InterRepository._same_model(source, target) |
651 | |
652 | @needs_read_lock |
653 | - def search_missing_revision_ids(self, revision_id=None, find_ghosts=True): |
654 | - """See InterRepository.missing_revision_ids().""" |
655 | - if revision_id is not None: |
656 | - source_ids = self.source.get_ancestry(revision_id) |
657 | - if source_ids[0] is not None: |
658 | - raise AssertionError() |
659 | - source_ids.pop(0) |
660 | - else: |
661 | - source_ids = self.source.all_revision_ids() |
662 | - source_ids_set = set(source_ids) |
663 | + def search_missing_revision_ids(self, |
664 | + revision_id=symbol_versioning.DEPRECATED_PARAMETER, |
665 | + find_ghosts=True, revision_ids=None, if_present_ids=None): |
666 | + """See InterRepository.search_missing_revision_ids().""" |
667 | + if symbol_versioning.deprecated_passed(revision_id): |
668 | + symbol_versioning.warn( |
669 | + 'search_missing_revision_ids(revision_id=...) was ' |
670 | + 'deprecated in 2.3. Use revision_ids=[...] instead.', |
671 | + DeprecationWarning, stacklevel=2) |
672 | + if revision_ids is not None: |
673 | + raise AssertionError( |
674 | + 'revision_ids is mutually exclusive with revision_id') |
675 | + if revision_id is not None: |
676 | + revision_ids = [revision_id] |
677 | + del revision_id |
678 | + source_ids_set = self._present_source_revisions_for( |
679 | + revision_ids, if_present_ids) |
680 | # source_ids is the worst possible case we may need to pull. |
681 | # now we want to filter source_ids against what we actually |
682 | # have in target, but don't try to check for existence where we know |
683 | @@ -553,7 +561,7 @@ |
684 | actually_present_revisions = set( |
685 | self.target._eliminate_revisions_not_present(possibly_present_revisions)) |
686 | required_revisions = source_ids_set.difference(actually_present_revisions) |
687 | - if revision_id is not None: |
688 | + if revision_ids is not None: |
689 | # we used get_ancestry to determine source_ids then we are assured all |
690 | # revisions referenced are present as they are installed in topological order. |
691 | # and the tip revision was validated by get_ancestry. |
692 | |
693 | === modified file 'bzrlib/repofmt/weaverepo.py' |
694 | --- bzrlib/repofmt/weaverepo.py 2011-01-13 00:41:19 +0000 |
695 | +++ bzrlib/repofmt/weaverepo.py 2011-01-19 00:31:58 +0000 |
696 | @@ -40,6 +40,7 @@ |
697 | lockable_files, |
698 | lockdir, |
699 | osutils, |
700 | + symbol_versioning, |
701 | trace, |
702 | tuned_gzip, |
703 | urlutils, |
704 | @@ -488,6 +489,7 @@ |
705 | _versionedfile_class = weave.WeaveFile |
706 | supports_ghosts = False |
707 | supports_chks = False |
708 | + supports_funky_characters = False |
709 | |
710 | _fetch_order = 'topological' |
711 | _fetch_reconcile = True |
712 | @@ -806,8 +808,10 @@ |
713 | self.target.fetch(self.source, revision_id=revision_id) |
714 | |
715 | @needs_read_lock |
716 | - def search_missing_revision_ids(self, revision_id=None, find_ghosts=True): |
717 | - """See InterRepository.missing_revision_ids().""" |
718 | + def search_missing_revision_ids(self, |
719 | + revision_id=symbol_versioning.DEPRECATED_PARAMETER, |
720 | + find_ghosts=True, revision_ids=None, if_present_ids=None): |
721 | + """See InterRepository.search_missing_revision_ids().""" |
722 | # we want all revisions to satisfy revision_id in source. |
723 | # but we don't want to stat every file here and there. |
724 | # we want then, all revisions other needs to satisfy revision_id |
725 | @@ -819,14 +823,19 @@ |
726 | # disk format scales terribly for push anyway due to rewriting |
727 | # inventory.weave, this is considered acceptable. |
728 | # - RBC 20060209 |
729 | - if revision_id is not None: |
730 | - source_ids = self.source.get_ancestry(revision_id) |
731 | - if source_ids[0] is not None: |
732 | - raise AssertionError() |
733 | - source_ids.pop(0) |
734 | - else: |
735 | - source_ids = self.source._all_possible_ids() |
736 | - source_ids_set = set(source_ids) |
737 | + if symbol_versioning.deprecated_passed(revision_id): |
738 | + symbol_versioning.warn( |
739 | + 'search_missing_revision_ids(revision_id=...) was ' |
740 | + 'deprecated in 2.3. Use revision_ids=[...] instead.', |
741 | + DeprecationWarning, stacklevel=2) |
742 | + if revision_ids is not None: |
743 | + raise AssertionError( |
744 | + 'revision_ids is mutually exclusive with revision_id') |
745 | + if revision_id is not None: |
746 | + revision_ids = [revision_id] |
747 | + del revision_id |
748 | + source_ids_set = self._present_source_revisions_for( |
749 | + revision_ids, if_present_ids) |
750 | # source_ids is the worst possible case we may need to pull. |
751 | # now we want to filter source_ids against what we actually |
752 | # have in target, but don't try to check for existence where we know |
753 | @@ -836,7 +845,7 @@ |
754 | actually_present_revisions = set( |
755 | self.target._eliminate_revisions_not_present(possibly_present_revisions)) |
756 | required_revisions = source_ids_set.difference(actually_present_revisions) |
757 | - if revision_id is not None: |
758 | + if revision_ids is not None: |
759 | # we used get_ancestry to determine source_ids then we are assured all |
760 | # revisions referenced are present as they are installed in topological order. |
761 | # and the tip revision was validated by get_ancestry. |
762 | |
763 | === modified file 'bzrlib/repository.py' |
764 | --- bzrlib/repository.py 2011-01-13 00:41:19 +0000 |
765 | +++ bzrlib/repository.py 2011-01-19 00:31:58 +0000 |
766 | @@ -42,7 +42,6 @@ |
767 | pyutils, |
768 | revision as _mod_revision, |
769 | static_tuple, |
770 | - symbol_versioning, |
771 | trace, |
772 | tsort, |
773 | versionedfile, |
774 | @@ -57,6 +56,7 @@ |
775 | from bzrlib import ( |
776 | errors, |
777 | registry, |
778 | + symbol_versioning, |
779 | ui, |
780 | ) |
781 | from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises |
782 | @@ -1597,15 +1597,28 @@ |
783 | return ret |
784 | |
785 | @needs_read_lock |
786 | - def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True): |
787 | + def search_missing_revision_ids(self, other, |
788 | + revision_id=symbol_versioning.DEPRECATED_PARAMETER, |
789 | + find_ghosts=True, revision_ids=None, if_present_ids=None): |
790 | """Return the revision ids that other has that this does not. |
791 | |
792 | These are returned in topological order. |
793 | |
794 | revision_id: only return revision ids included by revision_id. |
795 | """ |
796 | + if symbol_versioning.deprecated_passed(revision_id): |
797 | + symbol_versioning.warn( |
798 | + 'search_missing_revision_ids(revision_id=...) was ' |
799 | + 'deprecated in 2.3. Use revision_ids=[...] instead.', |
800 | + DeprecationWarning, stacklevel=3) |
801 | + if revision_ids is not None: |
802 | + raise AssertionError( |
803 | + 'revision_ids is mutually exclusive with revision_id') |
804 | + if revision_id is not None: |
805 | + revision_ids = [revision_id] |
806 | return InterRepository.get(other, self).search_missing_revision_ids( |
807 | - revision_id, find_ghosts) |
808 | + find_ghosts=find_ghosts, revision_ids=revision_ids, |
809 | + if_present_ids=if_present_ids) |
810 | |
811 | @staticmethod |
812 | def open(base): |
813 | @@ -3436,7 +3449,7 @@ |
814 | fetch_spec=fetch_spec, |
815 | find_ghosts=find_ghosts) |
816 | |
817 | - def _walk_to_common_revisions(self, revision_ids): |
818 | + def _walk_to_common_revisions(self, revision_ids, if_present_ids=None): |
819 | """Walk out from revision_ids in source to revisions target has. |
820 | |
821 | :param revision_ids: The start point for the search. |
822 | @@ -3444,10 +3457,14 @@ |
823 | """ |
824 | target_graph = self.target.get_graph() |
825 | revision_ids = frozenset(revision_ids) |
826 | + if if_present_ids: |
827 | + all_wanted_revs = revision_ids.union(if_present_ids) |
828 | + else: |
829 | + all_wanted_revs = revision_ids |
830 | missing_revs = set() |
831 | source_graph = self.source.get_graph() |
832 | # ensure we don't pay silly lookup costs. |
833 | - searcher = source_graph._make_breadth_first_searcher(revision_ids) |
834 | + searcher = source_graph._make_breadth_first_searcher(all_wanted_revs) |
835 | null_set = frozenset([_mod_revision.NULL_REVISION]) |
836 | searcher_exhausted = False |
837 | while True: |
838 | @@ -3489,30 +3506,79 @@ |
839 | return searcher.get_result() |
840 | |
841 | @needs_read_lock |
842 | - def search_missing_revision_ids(self, revision_id=None, find_ghosts=True): |
843 | + def search_missing_revision_ids(self, |
844 | + revision_id=symbol_versioning.DEPRECATED_PARAMETER, |
845 | + find_ghosts=True, revision_ids=None, if_present_ids=None): |
846 | """Return the revision ids that source has that target does not. |
847 | |
848 | :param revision_id: only return revision ids included by this |
849 | - revision_id. |
850 | + revision_id. |
851 | + :param revision_ids: return revision ids included by these |
852 | + revision_ids. NoSuchRevision will be raised if any of these |
853 | + revisions are not present. |
854 | + :param if_present_ids: like revision_ids, but will not cause |
855 | + NoSuchRevision if any of these are absent, instead they will simply |
856 | + not be in the result. This is useful for e.g. finding revisions |
857 | + to fetch for tags, which may reference absent revisions. |
858 | :param find_ghosts: If True find missing revisions in deep history |
859 | rather than just finding the surface difference. |
860 | :return: A bzrlib.graph.SearchResult. |
861 | """ |
862 | + if symbol_versioning.deprecated_passed(revision_id): |
863 | + symbol_versioning.warn( |
864 | + 'search_missing_revision_ids(revision_id=...) was ' |
865 | + 'deprecated in 2.3. Use revision_ids=[...] instead.', |
866 | + DeprecationWarning, stacklevel=2) |
867 | + if revision_ids is not None: |
868 | + raise AssertionError( |
869 | + 'revision_ids is mutually exclusive with revision_id') |
870 | + if revision_id is not None: |
871 | + revision_ids = [revision_id] |
872 | + del revision_id |
873 | # stop searching at found target revisions. |
874 | - if not find_ghosts and revision_id is not None: |
875 | - return self._walk_to_common_revisions([revision_id]) |
876 | + if not find_ghosts and (revision_ids is not None or if_present_ids is |
877 | + not None): |
878 | + return self._walk_to_common_revisions(revision_ids, |
879 | + if_present_ids=if_present_ids) |
880 | # generic, possibly worst case, slow code path. |
881 | target_ids = set(self.target.all_revision_ids()) |
882 | - if revision_id is not None: |
883 | - source_ids = self.source.get_ancestry(revision_id) |
884 | - if source_ids[0] is not None: |
885 | - raise AssertionError() |
886 | - source_ids.pop(0) |
887 | - else: |
888 | - source_ids = self.source.all_revision_ids() |
889 | + source_ids = self._present_source_revisions_for( |
890 | + revision_ids, if_present_ids) |
891 | result_set = set(source_ids).difference(target_ids) |
892 | return self.source.revision_ids_to_search_result(result_set) |
893 | |
894 | + def _present_source_revisions_for(self, revision_ids, if_present_ids=None): |
895 | + """Returns set of all revisions in ancestry of revision_ids present in |
896 | + the source repo. |
897 | + |
898 | + :param revision_ids: if None, all revisions in source are returned. |
899 | + :param if_present_ids: like revision_ids, but if any/all of these are |
900 | + absent no error is raised. |
901 | + """ |
902 | + if revision_ids is not None or if_present_ids is not None: |
903 | + # First, ensure all specified revisions exist. Callers expect |
904 | + # NoSuchRevision when they pass absent revision_ids here. |
905 | + if revision_ids is None: |
906 | + revision_ids = set() |
907 | + if if_present_ids is None: |
908 | + if_present_ids = set() |
909 | + revision_ids = set(revision_ids) |
910 | + if_present_ids = set(if_present_ids) |
911 | + all_wanted_ids = revision_ids.union(if_present_ids) |
912 | + graph = self.source.get_graph() |
913 | + present_revs = set(graph.get_parent_map(all_wanted_ids)) |
914 | + missing = revision_ids.difference(present_revs) |
915 | + if missing: |
916 | + raise errors.NoSuchRevision(self.source, missing.pop()) |
917 | + found_ids = all_wanted_ids.intersection(present_revs) |
918 | + source_ids = [rev_id for (rev_id, parents) in |
919 | + graph.iter_ancestry(found_ids) |
920 | + if rev_id != _mod_revision.NULL_REVISION |
921 | + and parents is not None] |
922 | + else: |
923 | + source_ids = self.source.all_revision_ids() |
924 | + return set(source_ids) |
925 | + |
926 | @staticmethod |
927 | def _same_model(source, target): |
928 | """True if source and target have the same data representation. |
929 | @@ -3836,7 +3902,13 @@ |
930 | fetch_spec=None): |
931 | """See InterRepository.fetch().""" |
932 | if fetch_spec is not None: |
933 | - raise AssertionError("Not implemented yet...") |
934 | + if (isinstance(fetch_spec, graph.NotInOtherForRevs) and |
935 | + len(fetch_spec.required_ids) == 1 and not |
936 | + fetch_spec.if_present_ids): |
937 | + revision_id = list(fetch_spec.required_ids)[0] |
938 | + del fetch_spec |
939 | + else: |
940 | + raise AssertionError("Not implemented yet...") |
941 | ui.ui_factory.warn_experimental_format_fetch(self) |
942 | if (not self.source.supports_rich_root() |
943 | and self.target.supports_rich_root()): |
944 | @@ -3849,8 +3921,12 @@ |
945 | ui.ui_factory.show_user_warning('cross_format_fetch', |
946 | from_format=self.source._format, |
947 | to_format=self.target._format) |
948 | + if revision_id: |
949 | + search_revision_ids = [revision_id] |
950 | + else: |
951 | + search_revision_ids = None |
952 | revision_ids = self.target.search_missing_revision_ids(self.source, |
953 | - revision_id, find_ghosts=find_ghosts).get_keys() |
954 | + revision_ids=search_revision_ids, find_ghosts=find_ghosts).get_keys() |
955 | if not revision_ids: |
956 | return 0, 0 |
957 | revision_ids = tsort.topo_sort( |
958 | |
959 | === modified file 'bzrlib/smart/repository.py' |
960 | --- bzrlib/smart/repository.py 2010-11-16 06:06:11 +0000 |
961 | +++ bzrlib/smart/repository.py 2011-01-19 00:31:58 +0000 |
962 | @@ -81,6 +81,8 @@ |
963 | recreate_search trusts that clients will look for missing things |
964 | they expected and get it from elsewhere. |
965 | """ |
966 | + if search_bytes == 'everything': |
967 | + return graph.EverythingResult(repository), None |
968 | lines = search_bytes.split('\n') |
969 | if lines[0] == 'ancestry-of': |
970 | heads = lines[1:] |
971 | @@ -412,6 +414,13 @@ |
972 | def do_repository_request(self, repository, to_network_name): |
973 | """Get a stream for inserting into a to_format repository. |
974 | |
975 | + The request body is 'search_bytes', a description of the revisions |
976 | + being requested. |
977 | + |
978 | + In 2.3 this verb added support for search_bytes == 'everything'. Older |
979 | + implementations will respond with a BadSearch error, and clients should |
980 | + catch this and fallback appropriately. |
981 | + |
982 | :param repository: The repository to stream from. |
983 | :param to_network_name: The network name of the format of the target |
984 | repository. |
985 | @@ -489,6 +498,13 @@ |
986 | |
987 | |
988 | class SmartServerRepositoryGetStream_1_19(SmartServerRepositoryGetStream): |
989 | + """The same as Repository.get_stream, but will return stream CHK formats to |
990 | + clients. |
991 | + |
992 | + See SmartServerRepositoryGetStream._should_fake_unknown. |
993 | + |
994 | + New in 1.19. |
995 | + """ |
996 | |
997 | def _should_fake_unknown(self): |
998 | """Returns False; we don't need to workaround bugs in 1.19+ clients.""" |
999 | |
1000 | === modified file 'bzrlib/smart/server.py' |
1001 | --- bzrlib/smart/server.py 2010-07-01 15:25:41 +0000 |
1002 | +++ bzrlib/smart/server.py 2011-01-19 00:31:58 +0000 |
1003 | @@ -18,7 +18,6 @@ |
1004 | |
1005 | import errno |
1006 | import os.path |
1007 | -import select |
1008 | import socket |
1009 | import sys |
1010 | import threading |
1011 | @@ -27,7 +26,6 @@ |
1012 | from bzrlib import ( |
1013 | errors, |
1014 | trace, |
1015 | - transport, |
1016 | ) |
1017 | from bzrlib.lazy_import import lazy_import |
1018 | lazy_import(globals(), """ |
1019 | @@ -178,7 +176,7 @@ |
1020 | |
1021 | def get_url(self): |
1022 | """Return the url of the server""" |
1023 | - return "bzr://%s:%d/" % self._sockname |
1024 | + return "bzr://%s:%s/" % (self._sockname[0], self._sockname[1]) |
1025 | |
1026 | def serve_conn(self, conn, thread_name_suffix): |
1027 | # For WIN32, where the timeout value from the listening socket |
1028 | |
1029 | === modified file 'bzrlib/tests/blackbox/test_serve.py' |
1030 | --- bzrlib/tests/blackbox/test_serve.py 2011-01-10 22:20:12 +0000 |
1031 | +++ bzrlib/tests/blackbox/test_serve.py 2011-01-19 00:31:58 +0000 |
1032 | @@ -18,16 +18,12 @@ |
1033 | """Tests of the bzr serve command.""" |
1034 | |
1035 | import os |
1036 | -import os.path |
1037 | import signal |
1038 | -import subprocess |
1039 | -import sys |
1040 | import thread |
1041 | import threading |
1042 | |
1043 | from bzrlib import ( |
1044 | builtins, |
1045 | - debug, |
1046 | errors, |
1047 | osutils, |
1048 | revision as _mod_revision, |
1049 | @@ -37,11 +33,13 @@ |
1050 | from bzrlib.branch import Branch |
1051 | from bzrlib.bzrdir import BzrDir |
1052 | from bzrlib.smart import client, medium |
1053 | -from bzrlib.smart.server import BzrServerFactory, SmartTCPServer |
1054 | +from bzrlib.smart.server import ( |
1055 | + BzrServerFactory, |
1056 | + SmartTCPServer, |
1057 | + ) |
1058 | from bzrlib.tests import ( |
1059 | TestCaseWithMemoryTransport, |
1060 | TestCaseWithTransport, |
1061 | - TestSkipped, |
1062 | ) |
1063 | from bzrlib.trace import mutter |
1064 | from bzrlib.transport import remote |
1065 | @@ -53,9 +51,9 @@ |
1066 | *func_args, **func_kwargs): |
1067 | """Run 'bzr serve', and run the given func in a thread once the server |
1068 | has started. |
1069 | - |
1070 | + |
1071 | When 'func' terminates, the server will be terminated too. |
1072 | - |
1073 | + |
1074 | Returns stdout and stderr. |
1075 | """ |
1076 | # install hook |
1077 | @@ -164,7 +162,7 @@ |
1078 | url = 'bzr://localhost:%d/' % port |
1079 | self.permit_url(url) |
1080 | return process, url |
1081 | - |
1082 | + |
1083 | def test_bzr_serve_quiet(self): |
1084 | self.make_branch('.') |
1085 | args = ['--port', 'localhost:0', '--quiet'] |
1086 | @@ -334,4 +332,3 @@ |
1087 | self.assertEqual(base_url, self.bzr_serve_transport.base) |
1088 | self.assertEqual(base_dir, |
1089 | server_maker.get_base_path(self.bzr_serve_transport)) |
1090 | - |
1091 | |
1092 | === modified file 'bzrlib/tests/per_branch/test_push.py' |
1093 | --- bzrlib/tests/per_branch/test_push.py 2011-01-13 01:02:53 +0000 |
1094 | +++ bzrlib/tests/per_branch/test_push.py 2011-01-19 00:31:58 +0000 |
1095 | @@ -170,6 +170,21 @@ |
1096 | self.assertEqual(tree.branch.last_revision(), |
1097 | to_branch.last_revision()) |
1098 | |
1099 | + def test_push_overwrite_with_older_mainline_rev(self): |
1100 | + """Pushing an older mainline revision with overwrite. |
1101 | + |
1102 | + This was <https://bugs.launchpad.net/bzr/+bug/386576>. |
1103 | + """ |
1104 | + source = self.make_branch_and_tree('source') |
1105 | + target = self.make_branch('target') |
1106 | + |
1107 | + source.commit('1st commit') |
1108 | + source.commit('2nd commit', rev_id='rev-2') |
1109 | + source.commit('3rd commit') |
1110 | + source.branch.push(target) |
1111 | + source.branch.push(target, stop_revision='rev-2', overwrite=True) |
1112 | + self.assertEqual('rev-2', target.last_revision()) |
1113 | + |
1114 | def test_push_overwrite_of_non_tip_with_stop_revision(self): |
1115 | """Combining the stop_revision and overwrite options works. |
1116 | |
1117 | |
1118 | === modified file 'bzrlib/tests/per_interrepository/test_interrepository.py' |
1119 | --- bzrlib/tests/per_interrepository/test_interrepository.py 2011-01-13 00:41:19 +0000 |
1120 | +++ bzrlib/tests/per_interrepository/test_interrepository.py 2011-01-19 00:31:58 +0000 |
1121 | @@ -131,9 +131,15 @@ |
1122 | self.assertFalse(repo_b.has_revision('pizza')) |
1123 | # Asking specifically for an absent revision errors. |
1124 | self.assertRaises(errors.NoSuchRevision, |
1125 | - repo_b.search_missing_revision_ids, repo_a, revision_id='pizza', |
1126 | + repo_b.search_missing_revision_ids, repo_a, revision_ids=['pizza'], |
1127 | find_ghosts=True) |
1128 | self.assertRaises(errors.NoSuchRevision, |
1129 | + repo_b.search_missing_revision_ids, repo_a, revision_ids=['pizza'], |
1130 | + find_ghosts=False) |
1131 | + self.callDeprecated( |
1132 | + ['search_missing_revision_ids(revision_id=...) was deprecated in ' |
1133 | + '2.3. Use revision_ids=[...] instead.'], |
1134 | + self.assertRaises, errors.NoSuchRevision, |
1135 | repo_b.search_missing_revision_ids, repo_a, revision_id='pizza', |
1136 | find_ghosts=False) |
1137 | |
1138 | @@ -143,7 +149,8 @@ |
1139 | # make a repository to compare against that is empty |
1140 | repo_b = self.make_to_repository('empty') |
1141 | repo_a = self.bzrdir.open_repository() |
1142 | - result = repo_b.search_missing_revision_ids(repo_a, revision_id='rev1') |
1143 | + result = repo_b.search_missing_revision_ids( |
1144 | + repo_a, revision_ids=['rev1']) |
1145 | self.assertEqual(set(['rev1']), result.get_keys()) |
1146 | self.assertEqual(('search', set(['rev1']), set([NULL_REVISION]), 1), |
1147 | result.get_recipe()) |
1148 | |
1149 | === modified file 'bzrlib/tests/per_repository_reference/test_fetch.py' |
1150 | --- bzrlib/tests/per_repository_reference/test_fetch.py 2009-06-01 18:13:46 +0000 |
1151 | +++ bzrlib/tests/per_repository_reference/test_fetch.py 2011-01-19 00:31:58 +0000 |
1152 | @@ -18,6 +18,7 @@ |
1153 | from bzrlib import ( |
1154 | branch, |
1155 | errors, |
1156 | + graph, |
1157 | ) |
1158 | from bzrlib.smart import ( |
1159 | server, |
1160 | @@ -25,7 +26,7 @@ |
1161 | from bzrlib.tests.per_repository import TestCaseWithRepository |
1162 | |
1163 | |
1164 | -class TestFetch(TestCaseWithRepository): |
1165 | +class TestFetchBase(TestCaseWithRepository): |
1166 | |
1167 | def make_source_branch(self): |
1168 | # It would be nice if there was a way to force this to be memory-only |
1169 | @@ -51,6 +52,9 @@ |
1170 | self.addCleanup(source_b.unlock) |
1171 | return content, source_b |
1172 | |
1173 | + |
1174 | +class TestFetch(TestFetchBase): |
1175 | + |
1176 | def test_sprout_from_stacked_with_short_history(self): |
1177 | content, source_b = self.make_source_branch() |
1178 | # Split the generated content into a base branch, and a stacked branch |
1179 | @@ -149,3 +153,38 @@ |
1180 | source_b.lock_read() |
1181 | self.addCleanup(source_b.unlock) |
1182 | stacked.pull(source_b, stop_revision='B-id') |
1183 | + |
1184 | + |
1185 | +class TestFetchFromRepoWithUnconfiguredFallbacks(TestFetchBase): |
1186 | + |
1187 | + def make_stacked_source_repo(self): |
1188 | + _, source_b = self.make_source_branch() |
1189 | + # Use 'make_branch' which gives us a bzr:// branch when appropriate, |
1190 | + # rather than creating a branch-on-disk |
1191 | + stack_b = self.make_branch('stack-on') |
1192 | + stack_b.pull(source_b, stop_revision='B-id') |
1193 | + stacked_b = self.make_branch('stacked') |
1194 | + stacked_b.set_stacked_on_url('../stack-on') |
1195 | + stacked_b.pull(source_b, stop_revision='C-id') |
1196 | + return stacked_b.repository |
1197 | + |
1198 | + def test_fetch_everything_includes_parent_invs(self): |
1199 | + stacked = self.make_stacked_source_repo() |
1200 | + repo_missing_fallbacks = stacked.bzrdir.open_repository() |
1201 | + self.addCleanup(repo_missing_fallbacks.lock_read().unlock) |
1202 | + target = self.make_repository('target') |
1203 | + self.addCleanup(target.lock_write().unlock) |
1204 | + target.fetch( |
1205 | + repo_missing_fallbacks, |
1206 | + fetch_spec=graph.EverythingResult(repo_missing_fallbacks)) |
1207 | + self.assertEqual(repo_missing_fallbacks.revisions.keys(), |
1208 | + target.revisions.keys()) |
1209 | + self.assertEqual(repo_missing_fallbacks.inventories.keys(), |
1210 | + target.inventories.keys()) |
1211 | + self.assertEqual(['C-id'], |
1212 | + sorted(k[-1] for k in target.revisions.keys())) |
1213 | + self.assertEqual(['B-id', 'C-id'], |
1214 | + sorted(k[-1] for k in target.inventories.keys())) |
1215 | + |
1216 | + |
1217 | + |
1218 | |
1219 | === modified file 'bzrlib/tests/test_crash.py' |
1220 | --- bzrlib/tests/test_crash.py 2011-01-12 01:01:53 +0000 |
1221 | +++ bzrlib/tests/test_crash.py 2011-01-19 00:31:58 +0000 |
1222 | @@ -26,6 +26,7 @@ |
1223 | config, |
1224 | crash, |
1225 | osutils, |
1226 | + plugin, |
1227 | tests, |
1228 | ) |
1229 | |
1230 | @@ -42,6 +43,11 @@ |
1231 | self.overrideEnv('APPORT_CRASH_DIR', crash_dir) |
1232 | self.assertEquals(crash_dir, config.crash_dir()) |
1233 | |
1234 | + self.overrideAttr( |
1235 | + plugin, |
1236 | + 'plugin_warnings', |
1237 | + {'example': ['Failed to load plugin foo']}) |
1238 | + |
1239 | stderr = StringIO() |
1240 | |
1241 | try: |
1242 | @@ -71,3 +77,6 @@ |
1243 | self.assertContainsRe(report, 'test_apport_report') |
1244 | # should also be in there |
1245 | self.assertContainsRe(report, '(?m)^CommandLine:') |
1246 | + self.assertContainsRe( |
1247 | + report, |
1248 | + 'Failed to load plugin foo') |
1249 | |
1250 | === modified file 'bzrlib/tests/test_plugins.py' |
1251 | --- bzrlib/tests/test_plugins.py 2011-01-10 22:20:12 +0000 |
1252 | +++ bzrlib/tests/test_plugins.py 2011-01-19 00:31:58 +0000 |
1253 | @@ -267,8 +267,14 @@ |
1254 | stream.close() |
1255 | |
1256 | def test_plugin_with_bad_api_version_reports(self): |
1257 | - # This plugin asks for bzrlib api version 1.0.0, which is not supported |
1258 | - # anymore. |
1259 | + """Try loading a plugin that requests an unsupported api. |
1260 | + |
1261 | + Up to bzr 2.2, the plugin just didn't load. But now we prefer to let |
1262 | + it load, and record a warning that can be shown in error messages. |
1263 | + |
1264 | + See https://bugs.launchpad.net/bzr/+bug/704195 |
1265 | + """ |
1266 | + self.overrideAttr(plugin, 'plugin_warnings', {}) |
1267 | name = 'wants100.py' |
1268 | f = file(name, 'w') |
1269 | try: |
1270 | @@ -276,9 +282,14 @@ |
1271 | "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n") |
1272 | finally: |
1273 | f.close() |
1274 | - |
1275 | log = self.load_and_capture(name) |
1276 | - self.assertContainsRe(log, |
1277 | + self.assertNotContainsRe(log, |
1278 | + r"It requested API version") |
1279 | + self.assertEquals( |
1280 | + ['wants100'], |
1281 | + plugin.plugin_warnings.keys()) |
1282 | + self.assertContainsRe( |
1283 | + plugin.plugin_warnings['wants100'][0], |
1284 | r"It requested API version") |
1285 | |
1286 | def test_plugin_with_bad_name_does_not_load(self): |
1287 | @@ -847,6 +858,9 @@ |
1288 | self.create_plugin_package('test_foo', dir='standard/test_foo') |
1289 | # All the tests will load the 'test_foo' plugin from various locations |
1290 | self.addCleanup(self._unregister_plugin, 'test_foo') |
1291 | + # Unfortunately there's global cached state for the specific |
1292 | + # registered paths. |
1293 | + self.addCleanup(plugin.PluginImporter.reset) |
1294 | |
1295 | def assertTestFooLoadedFrom(self, path): |
1296 | self.assertPluginKnown('test_foo') |
1297 | |
1298 | === modified file 'bzrlib/tests/test_remote.py' |
1299 | --- bzrlib/tests/test_remote.py 2011-01-12 01:01:53 +0000 |
1300 | +++ bzrlib/tests/test_remote.py 2011-01-19 00:31:58 +0000 |
1301 | @@ -57,9 +57,12 @@ |
1302 | ) |
1303 | from bzrlib.repofmt import groupcompress_repo, pack_repo |
1304 | from bzrlib.revision import NULL_REVISION |
1305 | -from bzrlib.smart import medium |
1306 | +from bzrlib.smart import medium, request |
1307 | from bzrlib.smart.client import _SmartClient |
1308 | -from bzrlib.smart.repository import SmartServerRepositoryGetParentMap |
1309 | +from bzrlib.smart.repository import ( |
1310 | + SmartServerRepositoryGetParentMap, |
1311 | + SmartServerRepositoryGetStream_1_19, |
1312 | + ) |
1313 | from bzrlib.tests import ( |
1314 | test_server, |
1315 | ) |
1316 | @@ -3180,11 +3183,63 @@ |
1317 | |
1318 | def test_copy_content_into_avoids_revision_history(self): |
1319 | local = self.make_branch('local') |
1320 | - remote_backing_tree = self.make_branch_and_tree('remote') |
1321 | - remote_backing_tree.commit("Commit.") |
1322 | + builder = self.make_branch_builder('remote') |
1323 | + builder.build_commit(message="Commit.") |
1324 | remote_branch_url = self.smart_server.get_url() + 'remote' |
1325 | remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch() |
1326 | local.repository.fetch(remote_branch.repository) |
1327 | self.hpss_calls = [] |
1328 | remote_branch.copy_content_into(local) |
1329 | self.assertFalse('Branch.revision_history' in self.hpss_calls) |
1330 | + |
1331 | + def test_fetch_everything_needs_just_one_call(self): |
1332 | + local = self.make_branch('local') |
1333 | + builder = self.make_branch_builder('remote') |
1334 | + builder.build_commit(message="Commit.") |
1335 | + remote_branch_url = self.smart_server.get_url() + 'remote' |
1336 | + remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch() |
1337 | + self.hpss_calls = [] |
1338 | + local.repository.fetch(remote_branch.repository, |
1339 | + fetch_spec=graph.EverythingResult(remote_branch.repository)) |
1340 | + self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls) |
1341 | + |
1342 | + def override_verb(self, verb_name, verb): |
1343 | + request_handlers = request.request_handlers |
1344 | + orig_verb = request_handlers.get(verb_name) |
1345 | + request_handlers.register(verb_name, verb, override_existing=True) |
1346 | + self.addCleanup(request_handlers.register, verb_name, orig_verb, |
1347 | + override_existing=True) |
1348 | + |
1349 | + def test_fetch_everything_backwards_compat(self): |
1350 | + """Can fetch with EverythingResult even with pre 2.3 servers. |
1351 | + |
1352 | + Pre-2.3 do not support 'everything' searches with the |
1353 | + Repository.get_stream_1.19 verb. |
1354 | + """ |
1355 | + verb_log = [] |
1356 | + class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19): |
1357 | + """A version of the Repository.get_stream_1.19 verb patched to |
1358 | + reject 'everything' searches the way 2.2 and earlier do. |
1359 | + """ |
1360 | + def recreate_search(self, repository, search_bytes, discard_excess=False): |
1361 | + verb_log.append(search_bytes.split('\n', 1)[0]) |
1362 | + if search_bytes == 'everything': |
1363 | + return (None, request.FailedSmartServerResponse(('BadSearch',))) |
1364 | + return super(OldGetStreamVerb, |
1365 | + self).recreate_search(repository, search_bytes, |
1366 | + discard_excess=discard_excess) |
1367 | + self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb) |
1368 | + local = self.make_branch('local') |
1369 | + builder = self.make_branch_builder('remote') |
1370 | + builder.build_commit(message="Commit.") |
1371 | + remote_branch_url = self.smart_server.get_url() + 'remote' |
1372 | + remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch() |
1373 | + self.hpss_calls = [] |
1374 | + local.repository.fetch(remote_branch.repository, |
1375 | + fetch_spec=graph.EverythingResult(remote_branch.repository)) |
1376 | + # make sure the overridden verb was used |
1377 | + self.assertLength(1, verb_log) |
1378 | + # more than one HPSS call is needed, but because it's a VFS callback |
1379 | + # its hard to predict exactly how many. |
1380 | + self.assertTrue(len(self.hpss_calls) > 1) |
1381 | + |
1382 | |
1383 | === modified file 'bzrlib/tests/test_repository.py' |
1384 | --- bzrlib/tests/test_repository.py 2010-11-22 22:27:58 +0000 |
1385 | +++ bzrlib/tests/test_repository.py 2011-01-19 00:31:58 +0000 |
1386 | @@ -1659,7 +1659,7 @@ |
1387 | self.orig_pack = target.pack |
1388 | target.pack = self.log_pack |
1389 | search = target.search_missing_revision_ids( |
1390 | - source_tree.branch.repository, tip) |
1391 | + source_tree.branch.repository, revision_ids=[tip]) |
1392 | stream = source.get_stream(search) |
1393 | from_format = source_tree.branch.repository._format |
1394 | sink = target._get_sink() |
1395 | |
1396 | === modified file 'bzrlib/tests/test_server.py' |
1397 | --- bzrlib/tests/test_server.py 2011-01-12 01:01:53 +0000 |
1398 | +++ bzrlib/tests/test_server.py 2011-01-19 00:31:58 +0000 |
1399 | @@ -694,8 +694,6 @@ |
1400 | server.SmartTCPServer.__init__(self, backing_transport, |
1401 | root_client_path) |
1402 | def serve(self): |
1403 | - # FIXME: No test are exercising the hooks for the test server |
1404 | - # -- vila 20100618 |
1405 | self.run_server_started_hooks() |
1406 | try: |
1407 | TestingThreadingTCPServer.serve(self) |
1408 | @@ -803,7 +801,3 @@ |
1409 | """Get a backing transport from a server we are decorating.""" |
1410 | url = 'readonly+' + backing_transport_server.get_url() |
1411 | return transport.get_transport(url) |
1412 | - |
1413 | - |
1414 | - |
1415 | - |
1416 | |
1417 | === modified file 'bzrlib/tests/test_smart.py' |
1418 | --- bzrlib/tests/test_smart.py 2011-01-12 01:01:53 +0000 |
1419 | +++ bzrlib/tests/test_smart.py 2011-01-19 00:31:58 +0000 |
1420 | @@ -41,6 +41,7 @@ |
1421 | repository as smart_repo, |
1422 | packrepository as smart_packrepo, |
1423 | request as smart_req, |
1424 | + server, |
1425 | vfs, |
1426 | ) |
1427 | from bzrlib.tests import test_server |
1428 | @@ -1469,7 +1470,7 @@ |
1429 | request.execute('stacked', 1, (3, r3))) |
1430 | |
1431 | |
1432 | -class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport): |
1433 | +class GetStreamTestBase(tests.TestCaseWithMemoryTransport): |
1434 | |
1435 | def make_two_commit_repo(self): |
1436 | tree = self.make_branch_and_memory_tree('.') |
1437 | @@ -1481,6 +1482,9 @@ |
1438 | repo = tree.branch.repository |
1439 | return repo, r1, r2 |
1440 | |
1441 | + |
1442 | +class TestSmartServerRepositoryGetStream(GetStreamTestBase): |
1443 | + |
1444 | def test_ancestry_of(self): |
1445 | """The search argument may be a 'ancestry-of' some heads'.""" |
1446 | backing = self.get_transport() |
1447 | @@ -1507,6 +1511,18 @@ |
1448 | stream_bytes = ''.join(response.body_stream) |
1449 | self.assertStartsWith(stream_bytes, 'Bazaar pack format 1') |
1450 | |
1451 | + def test_search_everything(self): |
1452 | + """A search of 'everything' returns a stream.""" |
1453 | + backing = self.get_transport() |
1454 | + request = smart_repo.SmartServerRepositoryGetStream_1_19(backing) |
1455 | + repo, r1, r2 = self.make_two_commit_repo() |
1456 | + serialised_fetch_spec = 'everything' |
1457 | + request.execute('', repo._format.network_name()) |
1458 | + response = request.do_body(serialised_fetch_spec) |
1459 | + self.assertEqual(('ok',), response.args) |
1460 | + stream_bytes = ''.join(response.body_stream) |
1461 | + self.assertStartsWith(stream_bytes, 'Bazaar pack format 1') |
1462 | + |
1463 | |
1464 | class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport): |
1465 | |
1466 | @@ -1906,6 +1922,8 @@ |
1467 | smart_repo.SmartServerRepositoryGetRevisionGraph) |
1468 | self.assertHandlerEqual('Repository.get_stream', |
1469 | smart_repo.SmartServerRepositoryGetStream) |
1470 | + self.assertHandlerEqual('Repository.get_stream_1.19', |
1471 | + smart_repo.SmartServerRepositoryGetStream_1_19) |
1472 | self.assertHandlerEqual('Repository.has_revision', |
1473 | smart_repo.SmartServerRequestHasRevision) |
1474 | self.assertHandlerEqual('Repository.insert_stream', |
1475 | @@ -1922,3 +1940,50 @@ |
1476 | smart_repo.SmartServerRepositoryUnlock) |
1477 | self.assertHandlerEqual('Transport.is_readonly', |
1478 | smart_req.SmartServerIsReadonly) |
1479 | + |
1480 | + |
1481 | +class SmartTCPServerHookTests(tests.TestCaseWithMemoryTransport): |
1482 | + """Tests for SmartTCPServer hooks.""" |
1483 | + |
1484 | + def setUp(self): |
1485 | + super(SmartTCPServerHookTests, self).setUp() |
1486 | + self.server = server.SmartTCPServer(self.get_transport()) |
1487 | + |
1488 | + def test_run_server_started_hooks(self): |
1489 | + """Test the server started hooks get fired properly.""" |
1490 | + started_calls = [] |
1491 | + server.SmartTCPServer.hooks.install_named_hook('server_started', |
1492 | + lambda backing_urls, url: started_calls.append((backing_urls, url)), |
1493 | + None) |
1494 | + started_ex_calls = [] |
1495 | + server.SmartTCPServer.hooks.install_named_hook('server_started_ex', |
1496 | + lambda backing_urls, url: started_ex_calls.append((backing_urls, url)), |
1497 | + None) |
1498 | + self.server._sockname = ('example.com', 42) |
1499 | + self.server.run_server_started_hooks() |
1500 | + self.assertEquals(started_calls, |
1501 | + [([self.get_transport().base], 'bzr://example.com:42/')]) |
1502 | + self.assertEquals(started_ex_calls, |
1503 | + [([self.get_transport().base], self.server)]) |
1504 | + |
1505 | + def test_run_server_started_hooks_ipv6(self): |
1506 | + """Test that socknames can contain 4-tuples.""" |
1507 | + self.server._sockname = ('::', 42, 0, 0) |
1508 | + started_calls = [] |
1509 | + server.SmartTCPServer.hooks.install_named_hook('server_started', |
1510 | + lambda backing_urls, url: started_calls.append((backing_urls, url)), |
1511 | + None) |
1512 | + self.server.run_server_started_hooks() |
1513 | + self.assertEquals(started_calls, |
1514 | + [([self.get_transport().base], 'bzr://:::42/')]) |
1515 | + |
1516 | + def test_run_server_stopped_hooks(self): |
1517 | + """Test the server stopped hooks.""" |
1518 | + self.server._sockname = ('example.com', 42) |
1519 | + stopped_calls = [] |
1520 | + server.SmartTCPServer.hooks.install_named_hook('server_stopped', |
1521 | + lambda backing_urls, url: stopped_calls.append((backing_urls, url)), |
1522 | + None) |
1523 | + self.server.run_server_stopped_hooks() |
1524 | + self.assertEquals(stopped_calls, |
1525 | + [([self.get_transport().base], 'bzr://example.com:42/')]) |
1526 | |
1527 | === added file 'doc/developers/fetch.txt' |
1528 | --- doc/developers/fetch.txt 1970-01-01 00:00:00 +0000 |
1529 | +++ doc/developers/fetch.txt 2011-01-19 00:31:58 +0000 |
1530 | @@ -0,0 +1,86 @@ |
1531 | +============= |
1532 | +Fetching data |
1533 | +============= |
1534 | + |
1535 | +Overview of a fetch |
1536 | +=================== |
1537 | + |
1538 | +Inside bzr, a typical fetch happens like this: |
1539 | + |
1540 | +* a user runs a command like ``bzr branch`` or ``bzr pull`` |
1541 | + |
1542 | +* ``Repository.fetch`` is called (by a higher-level method such as |
1543 | + ``ControlDir.sprout``, ``Branch.fetch``, etc). |
1544 | + |
1545 | +* An ``InterRepository`` object is created. The exact implementation of |
1546 | + ``InterRepository`` chosen depends on the format/capabilities of the |
1547 | + source and target repos. |
1548 | + |
1549 | +* The source and target repositories are compared to determine which data |
1550 | + needs to be transferred. |
1551 | + |
1552 | +* The repository data is copied. Often this is done by creating a |
1553 | + ``StreamSource`` and ``StreamSink`` from the source and target |
1554 | + repositories and feeding the stream from the source into the sink, but |
1555 | + some ``InterRepository`` implementations do differently. |
1556 | + |
1557 | + |
1558 | +How objects to be transferred are determined |
1559 | +============================================ |
1560 | + |
1561 | +See ``InterRepository._walk_to_common_revisions``. The basic idea is to |
1562 | +do a breadth-first search in the source repository's revision graph |
1563 | +(starting from the head or heads the caller asked for), and look in the |
1564 | +target repository to see if those revisions are already present. |
1565 | +Eventually this will find the common ancestors in both graphs, and thus |
1566 | +the set of revisions to be copied has been identified. |
1567 | + |
1568 | +All inventories for the copied revisions need to be present (and all |
1569 | +parent inventories at the stacking boundary too, to support stacking). |
1570 | + |
1571 | +All texts versions introduced by those inventories need to be transferred |
1572 | +(but see also stacking constraints). |
1573 | + |
1574 | +Fetch specs |
1575 | +=========== |
1576 | + |
1577 | +The most ``fetch`` methods accept a ``fetch_spec`` parameter. This is how |
1578 | +the caller controls what is fetched: e.g. all revisions for a given head |
1579 | +(that aren't already present in the target), the full ancestry for one or |
1580 | +more heads, or even the full contents of the source repository. |
1581 | + |
1582 | +The ``fetch_spec`` parameter is an object that implements the interface |
1583 | +defined by ``AbstractSearchResult`` in ``bzrlib.graph``. It describes |
1584 | +which keys should be fetched. Current implementations are |
1585 | +``SearchResult``, ``PendingAncestryResult``, ``EmptySearchResult``, and |
1586 | +``EverythingResult``. Some have options controlling if missing revisions |
1587 | +cause errors or not, etc. |
1588 | + |
1589 | +There are also some “search” objects, which can be used to conveniently |
1590 | +construct a search result for common cases: ``EverythingNotInOther`` and |
1591 | +``NotInOtherForRevs``. They provide an ``execute`` method that performs |
1592 | +the search and returns a search result. |
1593 | + |
1594 | +Also, ``Graph._make_breadth_first_searcher`` returns an object with a |
1595 | +``get_result`` method that returns a search result. |
1596 | + |
1597 | + |
1598 | +Streams |
1599 | +======= |
1600 | + |
1601 | +A **stream** is an iterable of (substream type, substream) pairs. |
1602 | +The **substream type** is a ``str`` that will be one of ``texts``, |
1603 | +``inventories``, ``inventory-deltas``, ``chk_bytes``, ``revisions`` or |
1604 | +``signatures``. A **substream** is a record stream. The format of those |
1605 | +records depends on the repository format being streamed, except for |
1606 | +``inventory-deltas`` records which are format-independent. |
1607 | + |
1608 | +A stream source can be constructed with ``repo._get_source(to_format)``, |
1609 | +and it provides a ``get_stream(search)`` method (among others). A stream |
1610 | +sink can be constructed with ``repo._get_sink()``, and provides an |
1611 | +``insert_stream(stream, src_format, resume_tokens)`` method (among |
1612 | +others). |
1613 | + |
1614 | + |
1615 | +.. |
1616 | + vim: ft=rst tw=74 ai |
1617 | |
1618 | === modified file 'doc/developers/index.txt' |
1619 | --- doc/developers/index.txt 2010-10-13 04:13:48 +0000 |
1620 | +++ doc/developers/index.txt 2011-01-19 00:31:58 +0000 |
1621 | @@ -38,6 +38,7 @@ |
1622 | |
1623 | transports |
1624 | ui |
1625 | + fetch |
1626 | |
1627 | Releasing and Packaging |
1628 | ======================= |
1629 | |
1630 | === modified file 'doc/en/release-notes/bzr-2.3.txt' |
1631 | --- doc/en/release-notes/bzr-2.3.txt 2011-01-15 17:29:41 +0000 |
1632 | +++ doc/en/release-notes/bzr-2.3.txt 2011-01-19 00:31:58 +0000 |
1633 | @@ -32,6 +32,11 @@ |
1634 | .. Fixes for situations where bzr would previously crash or give incorrect |
1635 | or undesirable results. |
1636 | |
1637 | +* Plugins incompatible with bzr no longer produce a warning on every |
1638 | + command invocation. Instead, a message is shown by ``bzr plugins`` and |
1639 | + in crash reports. |
1640 | + (#704195, Martin Pool) |
1641 | + |
1642 | Documentation |
1643 | ************* |
1644 | |
1645 | @@ -275,6 +280,11 @@ |
1646 | crashes when encountering private bugs (they are just displayed as such). |
1647 | (Vincent Ladeuil, #354985) |
1648 | |
1649 | +* The ``revision_id`` parameter of |
1650 | + ``Repository.search_missing_revision_ids`` and |
1651 | + ``InterRepository.search_missing_revision_ids`` is deprecated. It is |
1652 | + replaced by the ``revision_ids`` parameter. (Andrew Bennetts) |
1653 | + |
1654 | Internals |
1655 | ********* |
1656 | |
1657 | |
1658 | === added file 'doc/en/release-notes/bzr-2.4.txt' |
1659 | --- doc/en/release-notes/bzr-2.4.txt 1970-01-01 00:00:00 +0000 |
1660 | +++ doc/en/release-notes/bzr-2.4.txt 2011-01-19 00:31:58 +0000 |
1661 | @@ -0,0 +1,64 @@ |
1662 | +#################### |
1663 | +Bazaar Release Notes |
1664 | +#################### |
1665 | + |
1666 | +.. toctree:: |
1667 | + :maxdepth: 1 |
1668 | + |
1669 | +bzr 2.4b1 |
1670 | +######### |
1671 | + |
1672 | +:2.4b1: NOT RELEASED YET |
1673 | + |
1674 | +External Compatibility Breaks |
1675 | +***************************** |
1676 | + |
1677 | +.. These may require users to change the way they use Bazaar. |
1678 | + |
1679 | +New Features |
1680 | +************ |
1681 | + |
1682 | +.. New commands, options, etc that users may wish to try out. |
1683 | + |
1684 | +Improvements |
1685 | +************ |
1686 | + |
1687 | +.. Improvements to existing commands, especially improved performance |
1688 | + or memory usage, or better results. |
1689 | + |
1690 | +Bug Fixes |
1691 | +********* |
1692 | + |
1693 | +.. Fixes for situations where bzr would previously crash or give incorrect |
1694 | + or undesirable results. |
1695 | + |
1696 | +* ``bzr push --overwrite`` with an older revision specified will now correctly |
1697 | + roll back the target branch. (Jelmer Vernooij, #386576) |
1698 | + |
1699 | +* ``bzr serve`` no longer crashes when a server_started hook is installed and IPv6 |
1700 | + support is available on the system. (Jelmer Vernooij, #293697) |
1701 | + |
1702 | +Documentation |
1703 | +************* |
1704 | + |
1705 | +.. Improved or updated documentation. |
1706 | + |
1707 | +API Changes |
1708 | +*********** |
1709 | + |
1710 | +.. Changes that may require updates in plugins or other code that uses |
1711 | + bzrlib. |
1712 | + |
1713 | +Internals |
1714 | +********* |
1715 | + |
1716 | +.. Major internal changes, unlikely to be visible to users or plugin |
1717 | + developers, but interesting for bzr developers. |
1718 | + |
1719 | +Testing |
1720 | +******* |
1721 | + |
1722 | +.. Fixes and changes that are only relevant to bzr's test framework and |
1723 | + suite. This can include new facilities for writing tests, fixes to |
1724 | + spurious test failures and changes to the way things should be tested. |
1725 | + |
1726 | |
1727 | === added file 'doc/en/whats-new/whats-new-in-2.4.txt' |
1728 | --- doc/en/whats-new/whats-new-in-2.4.txt 1970-01-01 00:00:00 +0000 |
1729 | +++ doc/en/whats-new/whats-new-in-2.4.txt 2011-01-19 00:31:58 +0000 |
1730 | @@ -0,0 +1,33 @@ |
1731 | +************************* |
1732 | +What's New in Bazaar 2.4? |
1733 | +************************* |
1734 | + |
1735 | +Bazaar 2.4 is still under development, and will be released in August 2011. |
1736 | +This document accumulates a high level summary of what's changed. See the |
1737 | +:doc:`../release-notes/index` for a full list. |
1738 | + |
1739 | +Users are encouraged to upgrade from the other stable series. This |
1740 | +document outlines the improvements in Bazaar 2.4 vs Bazaar 2.3. As well as |
1741 | +summarizing improvements made to the core product, it highlights |
1742 | +enhancements within the broader Bazaar world of potential interest to |
1743 | +those upgrading. |
1744 | + |
1745 | +Bazaar 2.4.0 is fully compatible both locally and on the network with 2.0 |
1746 | +2.1, 2.2 and 2.3, and can read and write repositories generated by all |
1747 | +previous versions. |
1748 | + |
1749 | + |
1750 | +Further information |
1751 | +******************* |
1752 | + |
1753 | +For more detailed information on the changes made, see the the |
1754 | +:doc:`../release-notes/index` for: |
1755 | + |
1756 | +* the interim bzr `milestones <https://launchpad.net/bzr/2.4>`_ |
1757 | +* the plugins you use. |
1758 | + |
1759 | +For a summary of changes made in earlier releases, see: |
1760 | + |
1761 | +* :doc:`whats-new-in-2.1` |
1762 | +* :doc:`whats-new-in-2.2` |
1763 | +* :doc:`whats-new-in-2.3` |