Merge lp:~spiv/bzr/stacking-friendly-revision-history-verb into lp:~bzr/bzr/trunk-old
- stacking-friendly-revision-history-verb
- Merge into trunk-old
Proposed by
Andrew Bennetts
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~spiv/bzr/stacking-friendly-revision-history-verb |
Merge into: | lp:~bzr/bzr/trunk-old |
Diff against target: | 655 lines |
To merge this branch: | bzr merge lp:~spiv/bzr/stacking-friendly-revision-history-verb |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Approve | ||
Review via email: mp+7443@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote : | # |
Robert Collins wrote:
> Review: Approve
> Looks conceptually fine. Some thoughts
> - rather than using a special revision marker, use the presence/absence
> of null: at the start of the history. This is unambiguous and doesn't
> require taking up another special revid.
There's no special revids here. There's an explicit slot in the protocol
(and Python API) for saying “yep found it” vs. “ran out of history in this
repo”. So it is unambiguous already, so I'll land that as is.
> - update the version numbers.
Oops, done. Thanks.
Revision history for this message
Robert Collins (lifeless) wrote : | # |
Ah, I see, I misread.
I guess what I was trying to get at is that that isn't really needed
with null: being unique anyhow.
Still, it looks ok - go ahead.
-Rob
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' |
2 | --- NEWS 2009-06-15 15:20:24 +0000 |
3 | +++ NEWS 2009-06-16 02:35:31 +0000 |
4 | @@ -26,6 +26,14 @@ |
5 | rather than requiring all command names be known a-priori. |
6 | (Robert Collins) |
7 | |
8 | +Improvements |
9 | +************ |
10 | + |
11 | +* Resolving a revno to a revision id on a branch accessed via ``bzr://`` |
12 | + or ``bzr+ssh://`` is now much faster and involves no VFS operations. |
13 | + This speeds up commands like ``bzr pull -r 123``. (Andrew Bennetts) |
14 | + |
15 | + |
16 | bzr 1.16rc1 "It's yesterday in California" 2009-06-11 |
17 | ##################################################### |
18 | :Codename: yesterday-in-california |
19 | |
20 | === modified file 'bzrlib/branch.py' |
21 | --- bzrlib/branch.py 2009-06-10 03:56:49 +0000 |
22 | +++ bzrlib/branch.py 2009-06-16 02:35:31 +0000 |
23 | @@ -91,6 +91,7 @@ |
24 | self._revision_history_cache = None |
25 | self._revision_id_to_revno_cache = None |
26 | self._partial_revision_id_to_revno_cache = {} |
27 | + self._partial_revision_history_cache = [] |
28 | self._last_revision_info_cache = None |
29 | self._merge_sorted_revisions_cache = None |
30 | self._open_hook() |
31 | @@ -125,6 +126,27 @@ |
32 | raise errors.UnstackableRepositoryFormat(self.repository._format, |
33 | self.repository.base) |
34 | |
35 | + def _extend_partial_history(self, stop_index=None, stop_revision=None): |
36 | + """Extend the partial history to include a given index |
37 | + |
38 | + If a stop_index is supplied, stop when that index has been reached. |
39 | + If a stop_revision is supplied, stop when that revision is |
40 | + encountered. Otherwise, stop when the beginning of history is |
41 | + reached. |
42 | + |
43 | + :param stop_index: The index which should be present. When it is |
44 | + present, history extension will stop. |
45 | + :param stop_revision: The revision id which should be present. When |
46 | + it is encountered, history extension will stop. |
47 | + """ |
48 | + if len(self._partial_revision_history_cache) == 0: |
49 | + self._partial_revision_history_cache = [self.last_revision()] |
50 | + repository._iter_for_revno( |
51 | + self.repository, self._partial_revision_history_cache, |
52 | + stop_index=stop_index, stop_revision=stop_revision) |
53 | + if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION: |
54 | + self._partial_revision_history_cache.pop() |
55 | + |
56 | @staticmethod |
57 | def open(base, _unsupported=False, possible_transports=None): |
58 | """Open the branch rooted at base. |
59 | @@ -698,6 +720,8 @@ |
60 | self._revision_id_to_revno_cache = None |
61 | self._last_revision_info_cache = None |
62 | self._merge_sorted_revisions_cache = None |
63 | + self._partial_revision_history_cache = [] |
64 | + self._partial_revision_id_to_revno_cache = {} |
65 | |
66 | def _gen_revision_history(self): |
67 | """Return sequence of revision hashes on to this branch. |
68 | @@ -831,15 +855,20 @@ |
69 | except ValueError: |
70 | raise errors.NoSuchRevision(self, revision_id) |
71 | |
72 | + @needs_read_lock |
73 | def get_rev_id(self, revno, history=None): |
74 | """Find the revision id of the specified revno.""" |
75 | if revno == 0: |
76 | return _mod_revision.NULL_REVISION |
77 | - if history is None: |
78 | - history = self.revision_history() |
79 | - if revno <= 0 or revno > len(history): |
80 | + last_revno, last_revid = self.last_revision_info() |
81 | + if revno == last_revno: |
82 | + return last_revid |
83 | + if revno <= 0 or revno > last_revno: |
84 | raise errors.NoSuchRevision(self, revno) |
85 | - return history[revno - 1] |
86 | + distance_from_last = last_revno - revno |
87 | + if len(self._partial_revision_history_cache) <= distance_from_last: |
88 | + self._extend_partial_history(revno) |
89 | + return self._partial_revision_history_cache[revno] |
90 | |
91 | @needs_write_lock |
92 | def pull(self, source, overwrite=False, stop_revision=None, |
93 | @@ -2376,13 +2405,11 @@ |
94 | self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False) |
95 | super(BzrBranch8, self).__init__(*args, **kwargs) |
96 | self._last_revision_info_cache = None |
97 | - self._partial_revision_history_cache = [] |
98 | self._reference_info = None |
99 | |
100 | def _clear_cached_state(self): |
101 | super(BzrBranch8, self)._clear_cached_state() |
102 | self._last_revision_info_cache = None |
103 | - self._partial_revision_history_cache = [] |
104 | self._reference_info = None |
105 | |
106 | def _last_revision_info(self): |
107 | @@ -2444,35 +2471,6 @@ |
108 | self._extend_partial_history(stop_index=last_revno-1) |
109 | return list(reversed(self._partial_revision_history_cache)) |
110 | |
111 | - def _extend_partial_history(self, stop_index=None, stop_revision=None): |
112 | - """Extend the partial history to include a given index |
113 | - |
114 | - If a stop_index is supplied, stop when that index has been reached. |
115 | - If a stop_revision is supplied, stop when that revision is |
116 | - encountered. Otherwise, stop when the beginning of history is |
117 | - reached. |
118 | - |
119 | - :param stop_index: The index which should be present. When it is |
120 | - present, history extension will stop. |
121 | - :param revision_id: The revision id which should be present. When |
122 | - it is encountered, history extension will stop. |
123 | - """ |
124 | - repo = self.repository |
125 | - if len(self._partial_revision_history_cache) == 0: |
126 | - iterator = repo.iter_reverse_revision_history(self.last_revision()) |
127 | - else: |
128 | - start_revision = self._partial_revision_history_cache[-1] |
129 | - iterator = repo.iter_reverse_revision_history(start_revision) |
130 | - #skip the last revision in the list |
131 | - next_revision = iterator.next() |
132 | - for revision_id in iterator: |
133 | - self._partial_revision_history_cache.append(revision_id) |
134 | - if (stop_index is not None and |
135 | - len(self._partial_revision_history_cache) > stop_index): |
136 | - break |
137 | - if revision_id == stop_revision: |
138 | - break |
139 | - |
140 | def _write_revision_history(self, history): |
141 | """Factored out of set_revision_history. |
142 | |
143 | |
144 | === modified file 'bzrlib/builtins.py' |
145 | --- bzrlib/builtins.py 2009-06-11 06:54:33 +0000 |
146 | +++ bzrlib/builtins.py 2009-06-16 02:35:31 +0000 |
147 | @@ -946,29 +946,36 @@ |
148 | if branch_to.get_parent() is None or remember: |
149 | branch_to.set_parent(branch_from.base) |
150 | |
151 | - if revision is not None: |
152 | - revision_id = revision.as_revision_id(branch_from) |
153 | - |
154 | - branch_to.lock_write() |
155 | + if branch_from is not branch_to: |
156 | + branch_from.lock_read() |
157 | try: |
158 | - if tree_to is not None: |
159 | - view_info = _get_view_info_for_change_reporter(tree_to) |
160 | - change_reporter = delta._ChangeReporter( |
161 | - unversioned_filter=tree_to.is_ignored, view_info=view_info) |
162 | - result = tree_to.pull(branch_from, overwrite, revision_id, |
163 | - change_reporter, |
164 | - possible_transports=possible_transports, |
165 | - local=local) |
166 | - else: |
167 | - result = branch_to.pull(branch_from, overwrite, revision_id, |
168 | - local=local) |
169 | - |
170 | - result.report(self.outf) |
171 | - if verbose and result.old_revid != result.new_revid: |
172 | - log.show_branch_change(branch_to, self.outf, result.old_revno, |
173 | - result.old_revid) |
174 | + if revision is not None: |
175 | + revision_id = revision.as_revision_id(branch_from) |
176 | + |
177 | + branch_to.lock_write() |
178 | + try: |
179 | + if tree_to is not None: |
180 | + view_info = _get_view_info_for_change_reporter(tree_to) |
181 | + change_reporter = delta._ChangeReporter( |
182 | + unversioned_filter=tree_to.is_ignored, |
183 | + view_info=view_info) |
184 | + result = tree_to.pull( |
185 | + branch_from, overwrite, revision_id, change_reporter, |
186 | + possible_transports=possible_transports, local=local) |
187 | + else: |
188 | + result = branch_to.pull( |
189 | + branch_from, overwrite, revision_id, local=local) |
190 | + |
191 | + result.report(self.outf) |
192 | + if verbose and result.old_revid != result.new_revid: |
193 | + log.show_branch_change( |
194 | + branch_to, self.outf, result.old_revno, |
195 | + result.old_revid) |
196 | + finally: |
197 | + branch_to.unlock() |
198 | finally: |
199 | - branch_to.unlock() |
200 | + if branch_from is not branch_to: |
201 | + branch_from.unlock() |
202 | |
203 | |
204 | class cmd_push(Command): |
205 | |
206 | === modified file 'bzrlib/remote.py' |
207 | --- bzrlib/remote.py 2009-06-11 09:11:21 +0000 |
208 | +++ bzrlib/remote.py 2009-06-16 02:35:31 +0000 |
209 | @@ -28,12 +28,10 @@ |
210 | errors, |
211 | graph, |
212 | lockdir, |
213 | - pack, |
214 | repository, |
215 | revision, |
216 | revision as _mod_revision, |
217 | symbol_versioning, |
218 | - urlutils, |
219 | ) |
220 | from bzrlib.branch import BranchReferenceFormat |
221 | from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat |
222 | @@ -675,6 +673,37 @@ |
223 | return self._real_repository.get_missing_parent_inventories( |
224 | check_for_missing_texts=check_for_missing_texts) |
225 | |
226 | + def _get_rev_id_for_revno_vfs(self, revno, known_pair): |
227 | + self._ensure_real() |
228 | + return self._real_repository.get_rev_id_for_revno( |
229 | + revno, known_pair) |
230 | + |
231 | + def get_rev_id_for_revno(self, revno, known_pair): |
232 | + """See Repository.get_rev_id_for_revno.""" |
233 | + path = self.bzrdir._path_for_remote_call(self._client) |
234 | + try: |
235 | + if self._client._medium._is_remote_before((1, 16)): |
236 | + return self._get_rev_id_for_revno_vfs(revno, known_pair) |
237 | + response = self._call( |
238 | + 'Repository.get_rev_id_for_revno', path, revno, known_pair) |
239 | + except errors.UnknownSmartMethod: |
240 | + self._client._medium._remember_remote_is_before((1, 16)) |
241 | + return self._get_rev_id_for_revno_vfs(revno, known_pair) |
242 | + if response[0] == 'ok': |
243 | + return True, response[1] |
244 | + elif response[0] == 'history-incomplete': |
245 | + known_pair = response[1:3] |
246 | + for fallback in self._fallback_repositories: |
247 | + found, result = fallback.get_rev_id_for_revno(revno, known_pair) |
248 | + if found: |
249 | + return True, result |
250 | + else: |
251 | + known_pair = result |
252 | + # Not found in any fallbacks |
253 | + return False, known_pair |
254 | + else: |
255 | + raise errors.UnexpectedSmartServerResponse(response) |
256 | + |
257 | def _ensure_real(self): |
258 | """Ensure that there is a _real_repository set. |
259 | |
260 | @@ -1919,11 +1948,6 @@ |
261 | # We intentionally don't call the parent class's __init__, because it |
262 | # will try to assign to self.tags, which is a property in this subclass. |
263 | # And the parent's __init__ doesn't do much anyway. |
264 | - self._revision_id_to_revno_cache = None |
265 | - self._partial_revision_id_to_revno_cache = {} |
266 | - self._revision_history_cache = None |
267 | - self._last_revision_info_cache = None |
268 | - self._merge_sorted_revisions_cache = None |
269 | self.bzrdir = remote_bzrdir |
270 | if _client is not None: |
271 | self._client = _client |
272 | @@ -1943,6 +1967,7 @@ |
273 | else: |
274 | self._real_branch = None |
275 | # Fill out expected attributes of branch for bzrlib API users. |
276 | + self._clear_cached_state() |
277 | self.base = self.bzrdir.root_transport.base |
278 | self._control_files = None |
279 | self._lock_mode = None |
280 | @@ -2229,6 +2254,15 @@ |
281 | raise NotImplementedError(self.dont_leave_lock_in_place) |
282 | self._leave_lock = False |
283 | |
284 | + def get_rev_id(self, revno, history=None): |
285 | + last_revision_info = self.last_revision_info() |
286 | + ok, result = self.repository.get_rev_id_for_revno( |
287 | + revno, last_revision_info) |
288 | + if ok: |
289 | + return result |
290 | + missing_parent = result[1] |
291 | + raise errors.RevisionNotPresent(missing_parent, self.repository) |
292 | + |
293 | def _last_revision_info(self): |
294 | response = self._call('Branch.last_revision_info', self._remote_path()) |
295 | if response[0] != 'ok': |
296 | |
297 | === modified file 'bzrlib/repository.py' |
298 | --- bzrlib/repository.py 2009-06-12 01:11:00 +0000 |
299 | +++ bzrlib/repository.py 2009-06-16 02:35:31 +0000 |
300 | @@ -2234,6 +2234,41 @@ |
301 | """ |
302 | return self.get_revision(revision_id).inventory_sha1 |
303 | |
304 | + def get_rev_id_for_revno(self, revno, known_pair): |
305 | + """Return the revision id of a revno, given a later (revno, revid) |
306 | + pair in the same history. |
307 | + |
308 | + :return: if found (True, revid). If the available history ran out |
309 | + before reaching the revno, then this returns |
310 | + (False, (closest_revno, closest_revid)). |
311 | + """ |
312 | + known_revno, known_revid = known_pair |
313 | + partial_history = [known_revid] |
314 | + distance_from_known = known_revno - revno |
315 | + if distance_from_known < 0: |
316 | + raise ValueError( |
317 | + 'requested revno (%d) is later than given known revno (%d)' |
318 | + % (revno, known_revno)) |
319 | + try: |
320 | + _iter_for_revno( |
321 | + self, partial_history, stop_index=distance_from_known) |
322 | + except errors.RevisionNotPresent, err: |
323 | + if err.revision_id == known_revid: |
324 | + # The start revision (known_revid) wasn't found. |
325 | + raise |
326 | + # This is a stacked repository with no fallbacks, or a there's a |
327 | + # left-hand ghost. Either way, even though the revision named in |
328 | + # the error isn't in this repo, we know it's the next step in this |
329 | + # left-hand history. |
330 | + partial_history.append(err.revision_id) |
331 | + if len(partial_history) <= distance_from_known: |
332 | + # Didn't find enough history to get a revid for the revno. |
333 | + earliest_revno = known_revno - len(partial_history) + 1 |
334 | + return (False, (earliest_revno, partial_history[-1])) |
335 | + if len(partial_history) - 1 > distance_from_known: |
336 | + raise AssertionError('_iter_for_revno returned too much history') |
337 | + return (True, partial_history[-1]) |
338 | + |
339 | def iter_reverse_revision_history(self, revision_id): |
340 | """Iterate backwards through revision ids in the lefthand history |
341 | |
342 | @@ -4417,3 +4452,35 @@ |
343 | yield versionedfile.FulltextContentFactory( |
344 | key, parent_keys, None, as_bytes) |
345 | |
346 | + |
347 | +def _iter_for_revno(repo, partial_history_cache, stop_index=None, |
348 | + stop_revision=None): |
349 | + """Extend the partial history to include a given index |
350 | + |
351 | + If a stop_index is supplied, stop when that index has been reached. |
352 | + If a stop_revision is supplied, stop when that revision is |
353 | + encountered. Otherwise, stop when the beginning of history is |
354 | + reached. |
355 | + |
356 | + :param stop_index: The index which should be present. When it is |
357 | + present, history extension will stop. |
358 | + :param stop_revision: The revision id which should be present. When |
359 | + it is encountered, history extension will stop. |
360 | + """ |
361 | + start_revision = partial_history_cache[-1] |
362 | + iterator = repo.iter_reverse_revision_history(start_revision) |
363 | + try: |
364 | + #skip the last revision in the list |
365 | + iterator.next() |
366 | + while True: |
367 | + if (stop_index is not None and |
368 | + len(partial_history_cache) > stop_index): |
369 | + break |
370 | + if partial_history_cache[-1] == stop_revision: |
371 | + break |
372 | + revision_id = iterator.next() |
373 | + partial_history_cache.append(revision_id) |
374 | + except StopIteration: |
375 | + # No more history |
376 | + return |
377 | + |
378 | |
379 | === modified file 'bzrlib/smart/repository.py' |
380 | --- bzrlib/smart/repository.py 2009-06-10 03:56:49 +0000 |
381 | +++ bzrlib/smart/repository.py 2009-06-16 02:35:31 +0000 |
382 | @@ -19,7 +19,6 @@ |
383 | import bz2 |
384 | import os |
385 | import Queue |
386 | -import struct |
387 | import sys |
388 | import tarfile |
389 | import tempfile |
390 | @@ -284,6 +283,31 @@ |
391 | return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines)) |
392 | |
393 | |
394 | +class SmartServerRepositoryGetRevIdForRevno(SmartServerRepositoryReadLocked): |
395 | + |
396 | + def do_readlocked_repository_request(self, repository, revno, |
397 | + known_pair): |
398 | + """Find the revid for a given revno, given a known revno/revid pair. |
399 | + |
400 | + New in 1.16. |
401 | + """ |
402 | + try: |
403 | + found_flag, result = repository.get_rev_id_for_revno(revno, known_pair) |
404 | + except errors.RevisionNotPresent, err: |
405 | + if err.revision_id != known_pair[1]: |
406 | + raise AssertionError( |
407 | + 'get_rev_id_for_revno raised RevisionNotPresent for ' |
408 | + 'non-initial revision: ' + err.revision_id) |
409 | + return FailedSmartServerResponse( |
410 | + ('nosuchrevision', err.revision_id)) |
411 | + if found_flag: |
412 | + return SuccessfulSmartServerResponse(('ok', result)) |
413 | + else: |
414 | + earliest_revno, earliest_revid = result |
415 | + return SuccessfulSmartServerResponse( |
416 | + ('history-incomplete', earliest_revno, earliest_revid)) |
417 | + |
418 | + |
419 | class SmartServerRequestHasRevision(SmartServerRepositoryRequest): |
420 | |
421 | def do_repository_request(self, repository, revision_id): |
422 | |
423 | === modified file 'bzrlib/smart/request.py' |
424 | --- bzrlib/smart/request.py 2009-06-12 01:48:43 +0000 |
425 | +++ bzrlib/smart/request.py 2009-06-16 02:35:31 +0000 |
426 | @@ -555,6 +555,9 @@ |
427 | request_handlers.register_lazy( |
428 | 'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock') |
429 | request_handlers.register_lazy( |
430 | + 'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository', |
431 | + 'SmartServerRepositoryGetRevIdForRevno') |
432 | +request_handlers.register_lazy( |
433 | 'Repository.get_stream', 'bzrlib.smart.repository', |
434 | 'SmartServerRepositoryGetStream') |
435 | request_handlers.register_lazy( |
436 | |
437 | === modified file 'bzrlib/tests/blackbox/test_pull.py' |
438 | --- bzrlib/tests/blackbox/test_pull.py 2009-06-11 07:43:56 +0000 |
439 | +++ bzrlib/tests/blackbox/test_pull.py 2009-06-16 02:35:32 +0000 |
440 | @@ -368,8 +368,12 @@ |
441 | See <https://launchpad.net/bugs/380314> |
442 | """ |
443 | self.setup_smart_server_with_call_log() |
444 | + # Make a stacked-on branch with two commits so that the |
445 | + # revision-history can't be determined just by looking at the parent |
446 | + # field in the revision in the stacked repo. |
447 | parent = self.make_branch_and_tree('parent', format='1.9') |
448 | parent.commit(message='first commit') |
449 | + parent.commit(message='second commit') |
450 | local = parent.bzrdir.sprout('local').open_workingtree() |
451 | local.commit(message='local commit') |
452 | local.branch.create_clone_on_transport( |
453 | @@ -383,7 +387,7 @@ |
454 | # being too low. If rpc_count increases, more network roundtrips have |
455 | # become necessary for this use case. Please do not adjust this number |
456 | # upwards without agreement from bzr's network support maintainers. |
457 | - self.assertLength(43, self.hpss_calls) |
458 | + self.assertLength(18, self.hpss_calls) |
459 | remote = Branch.open('stacked') |
460 | self.assertEndsWith(remote.get_stacked_on_url(), '/parent') |
461 | |
462 | |
463 | === modified file 'bzrlib/tests/per_repository_reference/__init__.py' |
464 | --- bzrlib/tests/per_repository_reference/__init__.py 2009-06-10 03:56:49 +0000 |
465 | +++ bzrlib/tests/per_repository_reference/__init__.py 2009-06-16 02:35:32 +0000 |
466 | @@ -98,6 +98,7 @@ |
467 | 'bzrlib.tests.per_repository_reference.test_check', |
468 | 'bzrlib.tests.per_repository_reference.test_default_stacking', |
469 | 'bzrlib.tests.per_repository_reference.test_fetch', |
470 | + 'bzrlib.tests.per_repository_reference.test_get_rev_id_for_revno', |
471 | 'bzrlib.tests.per_repository_reference.test_initialize', |
472 | 'bzrlib.tests.per_repository_reference.test_unlock', |
473 | ] |
474 | |
475 | === added file 'bzrlib/tests/per_repository_reference/test_get_rev_id_for_revno.py' |
476 | --- bzrlib/tests/per_repository_reference/test_get_rev_id_for_revno.py 1970-01-01 00:00:00 +0000 |
477 | +++ bzrlib/tests/per_repository_reference/test_get_rev_id_for_revno.py 2009-06-16 02:35:32 +0000 |
478 | @@ -0,0 +1,46 @@ |
479 | +# Copyright (C) 2009 Canonical Ltd |
480 | +# |
481 | +# This program is free software; you can redistribute it and/or modify |
482 | +# it under the terms of the GNU General Public License as published by |
483 | +# the Free Software Foundation; either version 2 of the License, or |
484 | +# (at your option) any later version. |
485 | +# |
486 | +# This program is distributed in the hope that it will be useful, |
487 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
488 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
489 | +# GNU General Public License for more details. |
490 | +# |
491 | +# You should have received a copy of the GNU General Public License |
492 | +# along with this program; if not, write to the Free Software |
493 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
494 | + |
495 | +"""Tests for get_rev_id_for_revno on a repository with external references.""" |
496 | + |
497 | +from bzrlib import errors |
498 | +from bzrlib.tests.per_repository_reference import ( |
499 | + TestCaseWithExternalReferenceRepository, |
500 | + ) |
501 | + |
502 | +class TestGetRevIdForRevno(TestCaseWithExternalReferenceRepository): |
503 | + |
504 | + def test_uses_fallback(self): |
505 | + tree = self.make_branch_and_tree('base') |
506 | + base = tree.branch.repository |
507 | + revid = tree.commit('one') |
508 | + revid2 = tree.commit('two') |
509 | + spare_tree = tree.bzrdir.sprout('spare').open_workingtree() |
510 | + revid3 = spare_tree.commit('three') |
511 | + branch = spare_tree.branch.create_clone_on_transport( |
512 | + self.get_transport('referring'), stacked_on=self.get_url('base')) |
513 | + repo = branch.repository |
514 | + # Sanity check: now repo has 'revid3', and base has 'revid' + 'revid2' |
515 | + self.assertEqual(set([revid3]), |
516 | + set(repo.bzrdir.open_repository().all_revision_ids())) |
517 | + self.assertEqual(set([revid2, revid]), |
518 | + set(base.bzrdir.open_repository().all_revision_ids())) |
519 | + # get_rev_id_for_revno will find revno 1 == 'revid', even though |
520 | + # that revision can only be found in the fallback. |
521 | + repo.lock_read() |
522 | + self.addCleanup(repo.unlock) |
523 | + self.assertEqual( |
524 | + (True, revid), repo.get_rev_id_for_revno(1, (3, revid3))) |
525 | |
526 | === modified file 'bzrlib/tests/test_remote.py' |
527 | --- bzrlib/tests/test_remote.py 2009-06-12 01:48:43 +0000 |
528 | +++ bzrlib/tests/test_remote.py 2009-06-16 02:35:32 +0000 |
529 | @@ -2018,6 +2018,65 @@ |
530 | self.assertEqual(('AnUnexpectedError',), e.error_tuple) |
531 | |
532 | |
533 | +class TestRepositoryGetRevIdForRevno(TestRemoteRepository): |
534 | + |
535 | + def test_ok(self): |
536 | + repo, client = self.setup_fake_client_and_repository('quack') |
537 | + client.add_expected_call( |
538 | + 'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')), |
539 | + 'success', ('ok', 'rev-five')) |
540 | + result = repo.get_rev_id_for_revno(5, (42, 'rev-foo')) |
541 | + self.assertEqual((True, 'rev-five'), result) |
542 | + client.finished_test() |
543 | + |
544 | + def test_history_incomplete(self): |
545 | + repo, client = self.setup_fake_client_and_repository('quack') |
546 | + client.add_expected_call( |
547 | + 'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')), |
548 | + 'success', ('history-incomplete', 10, 'rev-ten')) |
549 | + result = repo.get_rev_id_for_revno(5, (42, 'rev-foo')) |
550 | + self.assertEqual((False, (10, 'rev-ten')), result) |
551 | + client.finished_test() |
552 | + |
553 | + def test_history_incomplete_with_fallback(self): |
554 | + """A 'history-incomplete' response causes the fallback repository to be |
555 | + queried too, if one is set. |
556 | + """ |
557 | + # Make a repo with a fallback repo, both using a FakeClient. |
558 | + format = remote.response_tuple_to_repo_format( |
559 | + ('yes', 'no', 'yes', 'fake-network-name')) |
560 | + repo, client = self.setup_fake_client_and_repository('quack') |
561 | + repo._format = format |
562 | + fallback_repo, ignored = self.setup_fake_client_and_repository( |
563 | + 'fallback') |
564 | + fallback_repo._client = client |
565 | + repo.add_fallback_repository(fallback_repo) |
566 | + # First the client should ask the primary repo |
567 | + client.add_expected_call( |
568 | + 'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')), |
569 | + 'success', ('history-incomplete', 2, 'rev-two')) |
570 | + # Then it should ask the fallback, using revno/revid from the |
571 | + # history-incomplete response as the known revno/revid. |
572 | + client.add_expected_call( |
573 | + 'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')), |
574 | + 'success', ('ok', 'rev-one')) |
575 | + result = repo.get_rev_id_for_revno(1, (42, 'rev-foo')) |
576 | + self.assertEqual((True, 'rev-one'), result) |
577 | + client.finished_test() |
578 | + |
579 | + def test_nosuchrevision(self): |
580 | + # 'nosuchrevision' is returned when the known-revid is not found in the |
581 | + # remote repo. The client translates that response to NoSuchRevision. |
582 | + repo, client = self.setup_fake_client_and_repository('quack') |
583 | + client.add_expected_call( |
584 | + 'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')), |
585 | + 'error', ('nosuchrevision', 'rev-foo')) |
586 | + self.assertRaises( |
587 | + errors.NoSuchRevision, |
588 | + repo.get_rev_id_for_revno, 5, (42, 'rev-foo')) |
589 | + client.finished_test() |
590 | + |
591 | + |
592 | class TestRepositoryIsShared(TestRemoteRepository): |
593 | |
594 | def test_is_shared(self): |
595 | |
596 | === modified file 'bzrlib/tests/test_smart.py' |
597 | --- bzrlib/tests/test_smart.py 2009-06-12 01:48:43 +0000 |
598 | +++ bzrlib/tests/test_smart.py 2009-06-16 02:35:32 +0000 |
599 | @@ -1161,6 +1161,47 @@ |
600 | request.execute('', 'missingrevision')) |
601 | |
602 | |
603 | +class TestSmartServerRepositoryGetRevIdForRevno(tests.TestCaseWithMemoryTransport): |
604 | + |
605 | + def test_revno_found(self): |
606 | + backing = self.get_transport() |
607 | + request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing) |
608 | + tree = self.make_branch_and_memory_tree('.') |
609 | + tree.lock_write() |
610 | + tree.add('') |
611 | + rev1_id_utf8 = u'\xc8'.encode('utf-8') |
612 | + rev2_id_utf8 = u'\xc9'.encode('utf-8') |
613 | + tree.commit('1st commit', rev_id=rev1_id_utf8) |
614 | + tree.commit('2nd commit', rev_id=rev2_id_utf8) |
615 | + tree.unlock() |
616 | + |
617 | + self.assertEqual(SmartServerResponse(('ok', rev1_id_utf8)), |
618 | + request.execute('', 1, (2, rev2_id_utf8))) |
619 | + |
620 | + def test_known_revid_missing(self): |
621 | + backing = self.get_transport() |
622 | + request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing) |
623 | + repo = self.make_repository('.') |
624 | + self.assertEqual( |
625 | + FailedSmartServerResponse(('nosuchrevision', 'ghost')), |
626 | + request.execute('', 1, (2, 'ghost'))) |
627 | + |
628 | + def test_history_incomplete(self): |
629 | + backing = self.get_transport() |
630 | + request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing) |
631 | + parent = self.make_branch_and_memory_tree('parent', format='1.9') |
632 | + r1 = parent.commit(message='first commit') |
633 | + r2 = parent.commit(message='second commit') |
634 | + local = self.make_branch_and_memory_tree('local', format='1.9') |
635 | + local.branch.pull(parent.branch) |
636 | + local.set_parent_ids([r2]) |
637 | + r3 = local.commit(message='local commit') |
638 | + local.branch.create_clone_on_transport( |
639 | + self.get_transport('stacked'), stacked_on=self.get_url('parent')) |
640 | + self.assertEqual( |
641 | + SmartServerResponse(('history-incomplete', 2, r2)), |
642 | + request.execute('stacked', 1, (3, r3))) |
643 | + |
644 | class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport): |
645 | |
646 | def make_two_commit_repo(self): |
647 | @@ -1576,6 +1617,8 @@ |
648 | smart.repository.SmartServerRepositoryGatherStats) |
649 | self.assertHandlerEqual('Repository.get_parent_map', |
650 | smart.repository.SmartServerRepositoryGetParentMap) |
651 | + self.assertHandlerEqual('Repository.get_rev_id_for_revno', |
652 | + smart.repository.SmartServerRepositoryGetRevIdForRevno) |
653 | self.assertHandlerEqual('Repository.get_revision_graph', |
654 | smart.repository.SmartServerRepositoryGetRevisionGraph) |
655 | self.assertHandlerEqual('Repository.get_stream', |
Looks conceptually fine. Some thoughts
- rather than using a special revision marker, use the presence/absence
of null: at the start of the history. This is unambiguous and doesn't
require taking up another special revid.
- update the version numbers.
review +1