Merge lp:~jelmer/bzr/private-update-revisions into lp:~jelmer/bzr/foreign
- private-update-revisions
- Merge into foreign
Proposed by
Jelmer Vernooij
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 5817 | ||||
Proposed branch: | lp:~jelmer/bzr/private-update-revisions | ||||
Merge into: | lp:~jelmer/bzr/foreign | ||||
Diff against target: |
783 lines (+140/-286) 12 files modified
bzrlib/_dirstate_helpers_pyx.pyx (+16/-4) bzrlib/branch.py (+47/-104) bzrlib/dirstate.py (+6/-1) bzrlib/merge.py (+1/-8) bzrlib/tests/per_branch/test_update.py (+0/-48) bzrlib/tests/per_interbranch/__init__.py (+0/-1) bzrlib/tests/per_interbranch/test_update_revisions.py (+0/-70) bzrlib/tests/test__dirstate_helpers.py (+23/-7) bzrlib/tests/test_dirstate.py (+33/-37) bzrlib/tests/test_revisionspec.py (+2/-4) bzrlib/workingtree_4.py (+1/-1) doc/en/release-notes/bzr-2.4.txt (+11/-1) |
||||
To merge this branch: | bzr merge lp:~jelmer/bzr/private-update-revisions | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Pending | ||
Review via email: mp+59754@code.launchpad.net |
Commit message
Description of the change
Make InterBranch.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bzrlib/_dirstate_helpers_pyx.pyx' |
2 | --- bzrlib/_dirstate_helpers_pyx.pyx 2010-08-27 18:02:22 +0000 |
3 | +++ bzrlib/_dirstate_helpers_pyx.pyx 2011-05-03 10:31:11 +0000 |
4 | @@ -866,6 +866,7 @@ |
5 | # _st mode of the compiled stat objects. |
6 | cdef int minikind, saved_minikind |
7 | cdef void * details |
8 | + cdef int worth_saving |
9 | minikind = minikind_from_mode(stat_value.st_mode) |
10 | if 0 == minikind: |
11 | return None |
12 | @@ -900,6 +901,7 @@ |
13 | # If we have gotten this far, that means that we need to actually |
14 | # process this entry. |
15 | link_or_sha1 = None |
16 | + worth_saving = 1 |
17 | if minikind == c'f': |
18 | executable = self._is_executable(stat_value.st_mode, |
19 | saved_executable) |
20 | @@ -916,10 +918,15 @@ |
21 | entry[1][0] = ('f', link_or_sha1, stat_value.st_size, |
22 | executable, packed_stat) |
23 | else: |
24 | - entry[1][0] = ('f', '', stat_value.st_size, |
25 | - executable, DirState.NULLSTAT) |
26 | + # This file is not worth caching the sha1. Either it is too new, or |
27 | + # it is newly added. Regardless, the only things we are changing |
28 | + # are derived from the stat, and so are not worth caching. So we do |
29 | + # *not* set the IN_MEMORY_MODIFIED flag. (But we'll save the |
30 | + # updated values if there is *other* data worth saving.) |
31 | + entry[1][0] = ('f', '', stat_value.st_size, executable, |
32 | + DirState.NULLSTAT) |
33 | + worth_saving = 0 |
34 | elif minikind == c'd': |
35 | - link_or_sha1 = None |
36 | entry[1][0] = ('d', '', 0, False, packed_stat) |
37 | if saved_minikind != c'd': |
38 | # This changed from something into a directory. Make sure we |
39 | @@ -929,6 +936,10 @@ |
40 | self._get_block_entry_index(entry[0][0], entry[0][1], 0) |
41 | self._ensure_block(block_index, entry_index, |
42 | pathjoin(entry[0][0], entry[0][1])) |
43 | + else: |
44 | + # Any changes are derived trivially from the stat object, not worth |
45 | + # re-writing a dirstate for just this |
46 | + worth_saving = 0 |
47 | elif minikind == c'l': |
48 | link_or_sha1 = self._read_link(abspath, saved_link_or_sha1) |
49 | if self._cutoff_time is None: |
50 | @@ -940,7 +951,8 @@ |
51 | else: |
52 | entry[1][0] = ('l', '', stat_value.st_size, |
53 | False, DirState.NULLSTAT) |
54 | - self._dirblock_state = DirState.IN_MEMORY_MODIFIED |
55 | + if worth_saving: |
56 | + self._dirblock_state = DirState.IN_MEMORY_MODIFIED |
57 | return link_or_sha1 |
58 | |
59 | |
60 | |
61 | === modified file 'bzrlib/branch.py' |
62 | --- bzrlib/branch.py 2011-04-21 00:20:51 +0000 |
63 | +++ bzrlib/branch.py 2011-05-03 10:31:11 +0000 |
64 | @@ -669,20 +669,15 @@ |
65 | raise errors.UnsupportedOperation(self.get_reference_info, self) |
66 | |
67 | @needs_write_lock |
68 | - def fetch(self, from_branch, last_revision=None, fetch_spec=None): |
69 | + def fetch(self, from_branch, last_revision=None): |
70 | """Copy revisions from from_branch into this branch. |
71 | |
72 | :param from_branch: Where to copy from. |
73 | :param last_revision: What revision to stop at (None for at the end |
74 | of the branch. |
75 | - :param fetch_spec: If specified, a SearchResult or |
76 | - PendingAncestryResult that describes which revisions to copy. This |
77 | - allows copying multiple heads at once. Mutually exclusive with |
78 | - last_revision. |
79 | :return: None |
80 | """ |
81 | - return InterBranch.get(from_branch, self).fetch(last_revision, |
82 | - fetch_spec) |
83 | + return InterBranch.get(from_branch, self).fetch(last_revision) |
84 | |
85 | def get_bound_location(self): |
86 | """Return the URL of the branch we are bound to. |
87 | @@ -999,23 +994,6 @@ |
88 | else: |
89 | return (0, _mod_revision.NULL_REVISION) |
90 | |
91 | - def update_revisions(self, other, stop_revision=None, overwrite=False, |
92 | - graph=None, fetch_tags=True): |
93 | - """Pull in new perfect-fit revisions. |
94 | - |
95 | - :param other: Another Branch to pull from |
96 | - :param stop_revision: Updated until the given revision |
97 | - :param overwrite: Always set the branch pointer, rather than checking |
98 | - to see if it is a proper descendant. |
99 | - :param graph: A Graph object that can be used to query history |
100 | - information. This can be None. |
101 | - :param fetch_tags: Flag that specifies if tags from other should be |
102 | - fetched too. |
103 | - :return: None |
104 | - """ |
105 | - return InterBranch.get(other, self).update_revisions(stop_revision, |
106 | - overwrite, graph, fetch_tags=fetch_tags) |
107 | - |
108 | @deprecated_method(deprecated_in((2, 4, 0))) |
109 | def import_last_revision_info(self, source_repo, revno, revid): |
110 | """Set the last revision info, importing from another repo if necessary. |
111 | @@ -1045,14 +1023,7 @@ |
112 | (should only be different from the arguments when lossy=True) |
113 | """ |
114 | if not self.repository.has_same_location(source.repository): |
115 | - try: |
116 | - tags_to_fetch = set(source.tags.get_reverse_tag_dict()) |
117 | - except errors.TagsNotSupported: |
118 | - tags_to_fetch = set() |
119 | - fetch_spec = _mod_graph.NotInOtherForRevs(self.repository, |
120 | - source.repository, [revid], |
121 | - if_present_ids=tags_to_fetch).execute() |
122 | - self.repository.fetch(source.repository, fetch_spec=fetch_spec) |
123 | + self.fetch(source, revid) |
124 | self.set_last_revision_info(revno, revid) |
125 | return (revno, revid) |
126 | |
127 | @@ -2625,27 +2596,6 @@ |
128 | pass |
129 | return None |
130 | |
131 | - def _basic_push(self, target, overwrite, stop_revision): |
132 | - """Basic implementation of push without bound branches or hooks. |
133 | - |
134 | - Must be called with source read locked and target write locked. |
135 | - """ |
136 | - result = BranchPushResult() |
137 | - result.source_branch = self |
138 | - result.target_branch = target |
139 | - result.old_revno, result.old_revid = target.last_revision_info() |
140 | - self.update_references(target) |
141 | - if result.old_revid != stop_revision: |
142 | - # We assume that during 'push' this repository is closer than |
143 | - # the target. |
144 | - graph = self.repository.get_graph(target.repository) |
145 | - target.update_revisions(self, stop_revision, |
146 | - overwrite=overwrite, graph=graph) |
147 | - if self._push_should_merge_tags(): |
148 | - result.tag_conflicts = self.tags.merge_to(target.tags, overwrite) |
149 | - result.new_revno, result.new_revid = target.last_revision_info() |
150 | - return result |
151 | - |
152 | def get_stacked_on_url(self): |
153 | raise errors.UnstackableBranchFormat(self._format, self.user_url) |
154 | |
155 | @@ -3301,22 +3251,6 @@ |
156 | raise NotImplementedError(self.pull) |
157 | |
158 | @needs_write_lock |
159 | - def update_revisions(self, stop_revision=None, overwrite=False, |
160 | - graph=None, fetch_tags=True): |
161 | - """Pull in new perfect-fit revisions. |
162 | - |
163 | - :param stop_revision: Updated until the given revision |
164 | - :param overwrite: Always set the branch pointer, rather than checking |
165 | - to see if it is a proper descendant. |
166 | - :param graph: A Graph object that can be used to query history |
167 | - information. This can be None. |
168 | - :param fetch_tags: Flag that specifies if tags from source should be |
169 | - fetched too. |
170 | - :return: None |
171 | - """ |
172 | - raise NotImplementedError(self.update_revisions) |
173 | - |
174 | - @needs_write_lock |
175 | def push(self, overwrite=False, stop_revision=None, |
176 | _override_hook_source_branch=None): |
177 | """Mirror the source branch into the target branch. |
178 | @@ -3335,11 +3269,10 @@ |
179 | raise NotImplementedError(self.copy_content_into) |
180 | |
181 | @needs_write_lock |
182 | - def fetch(self, stop_revision=None, fetch_spec=None): |
183 | + def fetch(self, stop_revision=None): |
184 | """Fetch revisions. |
185 | |
186 | :param stop_revision: Last revision to fetch |
187 | - :param fetch_spec: Fetch spec. |
188 | """ |
189 | raise NotImplementedError(self.fetch) |
190 | |
191 | @@ -3383,26 +3316,26 @@ |
192 | self.source.tags.merge_to(self.target.tags) |
193 | |
194 | @needs_write_lock |
195 | - def fetch(self, stop_revision=None, fetch_spec=None): |
196 | - if fetch_spec is not None and stop_revision is not None: |
197 | - raise AssertionError( |
198 | - "fetch_spec and last_revision are mutually exclusive.") |
199 | + def fetch(self, stop_revision=None): |
200 | if self.target.base == self.source.base: |
201 | return (0, []) |
202 | self.source.lock_read() |
203 | try: |
204 | - if stop_revision is None and fetch_spec is None: |
205 | - stop_revision = self.source.last_revision() |
206 | - stop_revision = _mod_revision.ensure_null(stop_revision) |
207 | + fetch_spec_factory = fetch.FetchSpecFactory() |
208 | + fetch_spec_factory.source_branch = self.source |
209 | + fetch_spec_factory.source_branch_stop_revision_id = stop_revision |
210 | + fetch_spec_factory.source_repo = self.source.repository |
211 | + fetch_spec_factory.target_repo = self.target.repository |
212 | + fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING |
213 | + fetch_spec = fetch_spec_factory.make_fetch_spec() |
214 | return self.target.repository.fetch(self.source.repository, |
215 | - revision_id=stop_revision, fetch_spec=fetch_spec) |
216 | + fetch_spec=fetch_spec) |
217 | finally: |
218 | self.source.unlock() |
219 | |
220 | @needs_write_lock |
221 | - def update_revisions(self, stop_revision=None, overwrite=False, |
222 | - graph=None, fetch_tags=True): |
223 | - """See InterBranch.update_revisions().""" |
224 | + def _update_revisions(self, stop_revision=None, overwrite=False, |
225 | + graph=None): |
226 | other_revno, other_last_revision = self.source.last_revision_info() |
227 | stop_revno = None # unknown |
228 | if stop_revision is None: |
229 | @@ -3419,18 +3352,7 @@ |
230 | # case of having something to pull, and so that the check for |
231 | # already merged can operate on the just fetched graph, which will |
232 | # be cached in memory. |
233 | - if fetch_tags: |
234 | - fetch_spec_factory = fetch.FetchSpecFactory() |
235 | - fetch_spec_factory.source_branch = self.source |
236 | - fetch_spec_factory.source_branch_stop_revision_id = stop_revision |
237 | - fetch_spec_factory.source_repo = self.source.repository |
238 | - fetch_spec_factory.target_repo = self.target.repository |
239 | - fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING |
240 | - fetch_spec = fetch_spec_factory.make_fetch_spec() |
241 | - else: |
242 | - fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository, |
243 | - self.source.repository, revision_ids=[stop_revision]).execute() |
244 | - self.target.fetch(self.source, fetch_spec=fetch_spec) |
245 | + self.fetch(stop_revision=stop_revision) |
246 | # Check to see if one is an ancestor of the other |
247 | if not overwrite: |
248 | if graph is None: |
249 | @@ -3505,6 +3427,28 @@ |
250 | finally: |
251 | self.source.unlock() |
252 | |
253 | + def _basic_push(self, overwrite, stop_revision): |
254 | + """Basic implementation of push without bound branches or hooks. |
255 | + |
256 | + Must be called with source read locked and target write locked. |
257 | + """ |
258 | + result = BranchPushResult() |
259 | + result.source_branch = self.source |
260 | + result.target_branch = self.target |
261 | + result.old_revno, result.old_revid = self.target.last_revision_info() |
262 | + self.source.update_references(self.target) |
263 | + if result.old_revid != stop_revision: |
264 | + # We assume that during 'push' this repository is closer than |
265 | + # the target. |
266 | + graph = self.source.repository.get_graph(self.target.repository) |
267 | + self._update_revisions(stop_revision, overwrite=overwrite, |
268 | + graph=graph) |
269 | + if self.source._push_should_merge_tags(): |
270 | + result.tag_conflicts = self.source.tags.merge_to(self.target.tags, |
271 | + overwrite) |
272 | + result.new_revno, result.new_revid = self.target.last_revision_info() |
273 | + return result |
274 | + |
275 | def _push_with_bound_branches(self, overwrite, stop_revision, |
276 | _override_hook_source_branch=None): |
277 | """Push from source into target, and into target's master if any. |
278 | @@ -3525,12 +3469,12 @@ |
279 | master_branch.lock_write() |
280 | try: |
281 | # push into the master from the source branch. |
282 | - self.source._basic_push(master_branch, overwrite, stop_revision) |
283 | - # and push into the target branch from the source. Note that we |
284 | - # push from the source branch again, because it's considered the |
285 | - # highest bandwidth repository. |
286 | - result = self.source._basic_push(self.target, overwrite, |
287 | - stop_revision) |
288 | + master_inter = InterBranch.get(self.source, master_branch) |
289 | + master_inter._basic_push(overwrite, stop_revision) |
290 | + # and push into the target branch from the source. Note that |
291 | + # we push from the source branch again, because it's considered |
292 | + # the highest bandwidth repository. |
293 | + result = self._basic_push(overwrite, stop_revision) |
294 | result.master_branch = master_branch |
295 | result.local_branch = self.target |
296 | _run_hooks() |
297 | @@ -3539,8 +3483,7 @@ |
298 | master_branch.unlock() |
299 | else: |
300 | # no master branch |
301 | - result = self.source._basic_push(self.target, overwrite, |
302 | - stop_revision) |
303 | + result = self._basic_push(overwrite, stop_revision) |
304 | # TODO: Why set master_branch and local_branch if there's no |
305 | # binding? Maybe cleaner to just leave them unset? -- mbp |
306 | # 20070504 |
307 | @@ -3589,8 +3532,8 @@ |
308 | # -- JRV20090506 |
309 | result.old_revno, result.old_revid = \ |
310 | self.target.last_revision_info() |
311 | - self.target.update_revisions(self.source, stop_revision, |
312 | - overwrite=overwrite, graph=graph) |
313 | + self._update_revisions(stop_revision, overwrite=overwrite, |
314 | + graph=graph) |
315 | # TODO: The old revid should be specified when merging tags, |
316 | # so a tags implementation that versions tags can only |
317 | # pull in the most recent changes. -- JRV20090506 |
318 | |
319 | === modified file 'bzrlib/dirstate.py' |
320 | --- bzrlib/dirstate.py 2011-04-20 15:55:42 +0000 |
321 | +++ bzrlib/dirstate.py 2011-05-03 10:31:11 +0000 |
322 | @@ -3205,6 +3205,7 @@ |
323 | # If we have gotten this far, that means that we need to actually |
324 | # process this entry. |
325 | link_or_sha1 = None |
326 | + worth_saving = True |
327 | if minikind == 'f': |
328 | executable = state._is_executable(stat_value.st_mode, |
329 | saved_executable) |
330 | @@ -3226,6 +3227,7 @@ |
331 | else: |
332 | entry[1][0] = ('f', '', stat_value.st_size, |
333 | executable, DirState.NULLSTAT) |
334 | + worth_saving = False |
335 | elif minikind == 'd': |
336 | link_or_sha1 = None |
337 | entry[1][0] = ('d', '', 0, False, packed_stat) |
338 | @@ -3237,6 +3239,8 @@ |
339 | state._get_block_entry_index(entry[0][0], entry[0][1], 0) |
340 | state._ensure_block(block_index, entry_index, |
341 | osutils.pathjoin(entry[0][0], entry[0][1])) |
342 | + else: |
343 | + worth_saving = False |
344 | elif minikind == 'l': |
345 | link_or_sha1 = state._read_link(abspath, saved_link_or_sha1) |
346 | if state._cutoff_time is None: |
347 | @@ -3248,7 +3252,8 @@ |
348 | else: |
349 | entry[1][0] = ('l', '', stat_value.st_size, |
350 | False, DirState.NULLSTAT) |
351 | - state._dirblock_state = DirState.IN_MEMORY_MODIFIED |
352 | + if worth_saving: |
353 | + state._dirblock_state = DirState.IN_MEMORY_MODIFIED |
354 | return link_or_sha1 |
355 | |
356 | |
357 | |
358 | === modified file 'bzrlib/merge.py' |
359 | --- bzrlib/merge.py 2011-04-17 23:06:22 +0000 |
360 | +++ bzrlib/merge.py 2011-05-03 10:31:11 +0000 |
361 | @@ -563,14 +563,7 @@ |
362 | |
363 | def _maybe_fetch(self, source, target, revision_id): |
364 | if not source.repository.has_same_location(target.repository): |
365 | - try: |
366 | - tags_to_fetch = set(source.tags.get_reverse_tag_dict()) |
367 | - except errors.TagsNotSupported: |
368 | - tags_to_fetch = None |
369 | - fetch_spec = _mod_graph.NotInOtherForRevs(target.repository, |
370 | - source.repository, [revision_id], |
371 | - if_present_ids=tags_to_fetch).execute() |
372 | - target.fetch(source, fetch_spec=fetch_spec) |
373 | + target.fetch(source, revision_id) |
374 | |
375 | def find_base(self): |
376 | revisions = [_mod_revision.ensure_null(self.this_basis), |
377 | |
378 | === modified file 'bzrlib/tests/per_branch/test_update.py' |
379 | --- bzrlib/tests/per_branch/test_update.py 2011-01-10 21:31:59 +0000 |
380 | +++ bzrlib/tests/per_branch/test_update.py 2011-05-03 10:31:11 +0000 |
381 | @@ -70,54 +70,6 @@ |
382 | self.assertEqual('foo', child_tree.branch.update()) |
383 | self.assertEqual(['bar'], child_tree.branch.revision_history()) |
384 | |
385 | - |
386 | -class TestUpdateRevisions(per_branch.TestCaseWithBranch): |
387 | - |
388 | - def test_accepts_graph(self): |
389 | - # An implementation may not use it, but it should allow a 'graph' to be |
390 | - # supplied |
391 | - tree1 = self.make_branch_and_tree('tree1') |
392 | - rev1 = tree1.commit('one') |
393 | - tree2 = tree1.bzrdir.sprout('tree2').open_workingtree() |
394 | - rev2 = tree2.commit('two') |
395 | - |
396 | - tree1.lock_write() |
397 | - self.addCleanup(tree1.unlock) |
398 | - tree2.lock_read() |
399 | - self.addCleanup(tree2.unlock) |
400 | - graph = tree2.branch.repository.get_graph(tree1.branch.repository) |
401 | - |
402 | - tree1.branch.update_revisions(tree2.branch, graph=graph) |
403 | - self.assertEqual((2, rev2), tree1.branch.last_revision_info()) |
404 | - |
405 | - def test_overwrite_ignores_diverged(self): |
406 | - tree1 = self.make_branch_and_tree('tree1') |
407 | - rev1 = tree1.commit('one') |
408 | - tree2 = tree1.bzrdir.sprout('tree2').open_workingtree() |
409 | - rev2 = tree1.commit('two') |
410 | - rev2b = tree2.commit('alt two') |
411 | - |
412 | - self.assertRaises(errors.DivergedBranches, |
413 | - tree1.branch.update_revisions, |
414 | - tree2.branch, overwrite=False) |
415 | - # However, the revision should be copied into the repository |
416 | - self.assertTrue(tree1.branch.repository.has_revision(rev2b)) |
417 | - |
418 | - tree1.branch.update_revisions(tree2.branch, overwrite=True) |
419 | - self.assertEqual((2, rev2b), tree1.branch.last_revision_info()) |
420 | - |
421 | - def test_ignores_older_unless_overwrite(self): |
422 | - tree1 = self.make_branch_and_tree('tree1') |
423 | - rev1 = tree1.commit('one') |
424 | - tree2 = tree1.bzrdir.sprout('tree2').open_workingtree() |
425 | - rev2 = tree1.commit('two') |
426 | - |
427 | - tree1.branch.update_revisions(tree2.branch) |
428 | - self.assertEqual((2, rev2), tree1.branch.last_revision_info()) |
429 | - |
430 | - tree1.branch.update_revisions(tree2.branch, overwrite=True) |
431 | - self.assertEqual((1, rev1), tree1.branch.last_revision_info()) |
432 | - |
433 | def test_update_in_checkout_of_readonly(self): |
434 | tree1 = self.make_branch_and_tree('tree1') |
435 | rev1 = tree1.commit('one') |
436 | |
437 | === modified file 'bzrlib/tests/per_interbranch/__init__.py' |
438 | --- bzrlib/tests/per_interbranch/__init__.py 2011-03-26 01:53:34 +0000 |
439 | +++ bzrlib/tests/per_interbranch/__init__.py 2011-05-03 10:31:11 +0000 |
440 | @@ -171,7 +171,6 @@ |
441 | 'bzrlib.tests.per_interbranch.test_copy_content_into', |
442 | 'bzrlib.tests.per_interbranch.test_pull', |
443 | 'bzrlib.tests.per_interbranch.test_push', |
444 | - 'bzrlib.tests.per_interbranch.test_update_revisions', |
445 | ]) |
446 | scenarios = make_scenarios(default_test_list()) |
447 | return multiply_tests(submod_tests, scenarios, standard_tests) |
448 | |
449 | === removed file 'bzrlib/tests/per_interbranch/test_update_revisions.py' |
450 | --- bzrlib/tests/per_interbranch/test_update_revisions.py 2010-06-17 09:23:19 +0000 |
451 | +++ bzrlib/tests/per_interbranch/test_update_revisions.py 1970-01-01 00:00:00 +0000 |
452 | @@ -1,70 +0,0 @@ |
453 | -# Copyright (C) 2009, 2010 Canonical Ltd |
454 | -# |
455 | -# This program is free software; you can redistribute it and/or modify |
456 | -# it under the terms of the GNU General Public License as published by |
457 | -# the Free Software Foundation; either version 2 of the License, or |
458 | -# (at your option) any later version. |
459 | -# |
460 | -# This program is distributed in the hope that it will be useful, |
461 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
462 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
463 | -# GNU General Public License for more details. |
464 | -# |
465 | -# You should have received a copy of the GNU General Public License |
466 | -# along with this program; if not, write to the Free Software |
467 | -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
468 | - |
469 | -from bzrlib import ( |
470 | - errors, |
471 | - ) |
472 | -from bzrlib.tests.per_interbranch import ( |
473 | - TestCaseWithInterBranch, |
474 | - ) |
475 | - |
476 | - |
477 | -class TestUpdateRevisions(TestCaseWithInterBranch): |
478 | - |
479 | - def setUp(self): |
480 | - super(TestUpdateRevisions, self).setUp() |
481 | - self.tree1 = self.make_from_branch_and_tree('tree1') |
482 | - rev1 = self.tree1.commit('one') |
483 | - branch2 = self.make_to_branch('tree2') |
484 | - branch2.repository.fetch(self.tree1.branch.repository) |
485 | - self.tree1.branch.copy_content_into(branch2) |
486 | - self.tree2 = branch2.bzrdir.create_workingtree() |
487 | - |
488 | - def test_accepts_graph(self): |
489 | - # An implementation may not use it, but it should allow a 'graph' to be |
490 | - # supplied |
491 | - rev2 = self.tree2.commit('two') |
492 | - |
493 | - self.tree1.lock_write() |
494 | - self.addCleanup(self.tree1.unlock) |
495 | - self.tree2.lock_read() |
496 | - self.addCleanup(self.tree2.unlock) |
497 | - graph = self.tree2.branch.repository.get_graph( |
498 | - self.tree1.branch.repository) |
499 | - |
500 | - self.tree1.branch.update_revisions(self.tree2.branch, graph=graph) |
501 | - self.assertEqual((2, rev2), self.tree1.branch.last_revision_info()) |
502 | - |
503 | - def test_overwrite_ignores_diverged(self): |
504 | - rev2 = self.tree1.commit('two') |
505 | - rev2b = self.tree2.commit('alt two') |
506 | - |
507 | - self.assertRaises(errors.DivergedBranches, |
508 | - self.tree1.branch.update_revisions, |
509 | - self.tree2.branch, overwrite=False) |
510 | - # However, the revision should be copied into the repository |
511 | - self.assertTrue(self.tree1.branch.repository.has_revision(rev2b)) |
512 | - |
513 | - self.tree1.branch.update_revisions(self.tree2.branch, overwrite=True) |
514 | - self.assertEqual((2, rev2b), self.tree1.branch.last_revision_info()) |
515 | - |
516 | - def test_ignores_older_unless_overwrite(self): |
517 | - rev2 = self.tree1.commit('two') |
518 | - |
519 | - self.tree1.branch.update_revisions(self.tree2.branch) |
520 | - self.assertEqual((2, rev2), self.tree1.branch.last_revision_info()) |
521 | - |
522 | - self.tree1.branch.update_revisions(self.tree2.branch, overwrite=True) |
523 | |
524 | === modified file 'bzrlib/tests/test__dirstate_helpers.py' |
525 | --- bzrlib/tests/test__dirstate_helpers.py 2011-01-10 22:20:12 +0000 |
526 | +++ bzrlib/tests/test__dirstate_helpers.py 2011-05-03 10:31:11 +0000 |
527 | @@ -871,10 +871,12 @@ |
528 | stat_value=stat_value) |
529 | self.assertEqual(None, link_or_sha1) |
530 | |
531 | - # The dirblock entry should not have cached the file's sha1 (too new) |
532 | + # The dirblock entry should not have computed or cached the file's |
533 | + # sha1, but it did update the files' st_size. However, this is not |
534 | + # worth writing a dirstate file for, so we leave the state UNMODIFIED |
535 | self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT), |
536 | entry[1][0]) |
537 | - self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED, |
538 | + self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
539 | state._dirblock_state) |
540 | mode = stat_value.st_mode |
541 | self.assertEqual([('is_exec', mode, False)], state._log) |
542 | @@ -883,9 +885,8 @@ |
543 | self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
544 | state._dirblock_state) |
545 | |
546 | - # If we do it again right away, we don't know if the file has changed |
547 | - # so we will re-read the file. Roll the clock back so the file is |
548 | - # guaranteed to look too new. |
549 | + # Roll the clock back so the file is guaranteed to look too new. We |
550 | + # should still not compute the sha1. |
551 | state.adjust_time(-10) |
552 | del state._log[:] |
553 | |
554 | @@ -893,7 +894,7 @@ |
555 | stat_value=stat_value) |
556 | self.assertEqual([('is_exec', mode, False)], state._log) |
557 | self.assertEqual(None, link_or_sha1) |
558 | - self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED, |
559 | + self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
560 | state._dirblock_state) |
561 | self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT), |
562 | entry[1][0]) |
563 | @@ -909,6 +910,8 @@ |
564 | self.assertEqual([('is_exec', mode, False)], state._log) |
565 | self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT), |
566 | entry[1][0]) |
567 | + self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
568 | + state._dirblock_state) |
569 | |
570 | # If the file is no longer new, and the clock has been moved forward |
571 | # sufficiently, it will cache the sha. |
572 | @@ -1005,12 +1008,25 @@ |
573 | self.build_tree(['a/']) |
574 | state.adjust_time(+20) |
575 | self.assertIs(None, self.do_update_entry(state, entry, 'a')) |
576 | + # a/ used to be a file, but is now a directory, worth saving |
577 | self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED, |
578 | state._dirblock_state) |
579 | state.save() |
580 | self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
581 | state._dirblock_state) |
582 | - self.assertIs(None, self.do_update_entry(state, entry, 'a')) |
583 | + # No changes to a/ means not worth saving. |
584 | + self.assertIs(None, self.do_update_entry(state, entry, 'a')) |
585 | + self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
586 | + state._dirblock_state) |
587 | + # Change the last-modified time for the directory |
588 | + t = time.time() - 100.0 |
589 | + os.utime('a', (t, t)) |
590 | + saved_packed_stat = entry[1][0][-1] |
591 | + self.assertIs(None, self.do_update_entry(state, entry, 'a')) |
592 | + # We *do* go ahead and update the information in the dirblocks, but we |
593 | + # don't bother setting IN_MEMORY_MODIFIED because it is trivial to |
594 | + # recompute. |
595 | + self.assertNotEqual(saved_packed_stat, entry[1][0][-1]) |
596 | self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED, |
597 | state._dirblock_state) |
598 | |
599 | |
600 | === modified file 'bzrlib/tests/test_dirstate.py' |
601 | --- bzrlib/tests/test_dirstate.py 2011-01-25 22:54:08 +0000 |
602 | +++ bzrlib/tests/test_dirstate.py 2011-05-03 10:31:11 +0000 |
603 | @@ -527,6 +527,19 @@ |
604 | |
605 | class TestDirStateOnFile(TestCaseWithDirState): |
606 | |
607 | + def create_updated_dirstate(self): |
608 | + self.build_tree(['a-file']) |
609 | + tree = self.make_branch_and_tree('.') |
610 | + tree.add(['a-file'], ['a-id']) |
611 | + tree.commit('add a-file') |
612 | + # Save and unlock the state, re-open it in readonly mode |
613 | + state = dirstate.DirState.from_tree(tree, 'dirstate') |
614 | + state.save() |
615 | + state.unlock() |
616 | + state = dirstate.DirState.on_file('dirstate') |
617 | + state.lock_read() |
618 | + return state |
619 | + |
620 | def test_construct_with_path(self): |
621 | tree = self.make_branch_and_tree('tree') |
622 | state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree') |
623 | @@ -561,36 +574,24 @@ |
624 | state.unlock() |
625 | |
626 | def test_can_save_in_read_lock(self): |
627 | - self.build_tree(['a-file']) |
628 | - state = dirstate.DirState.initialize('dirstate') |
629 | - try: |
630 | - # No stat and no sha1 sum. |
631 | - state.add('a-file', 'a-file-id', 'file', None, '') |
632 | - state.save() |
633 | - finally: |
634 | - state.unlock() |
635 | - |
636 | - # Now open in readonly mode |
637 | - state = dirstate.DirState.on_file('dirstate') |
638 | - state.lock_read() |
639 | + state = self.create_updated_dirstate() |
640 | try: |
641 | entry = state._get_entry(0, path_utf8='a-file') |
642 | # The current size should be 0 (default) |
643 | self.assertEqual(0, entry[1][0][2]) |
644 | # We should have a real entry. |
645 | self.assertNotEqual((None, None), entry) |
646 | - # Make sure everything is old enough |
647 | + # Set the cutoff-time into the future, so things look cacheable |
648 | state._sha_cutoff_time() |
649 | - state._cutoff_time += 10 |
650 | - # Change the file length |
651 | - self.build_tree_contents([('a-file', 'shorter')]) |
652 | - sha1sum = dirstate.update_entry(state, entry, 'a-file', |
653 | - os.lstat('a-file')) |
654 | - # new file, no cached sha: |
655 | - self.assertEqual(None, sha1sum) |
656 | + state._cutoff_time += 10.0 |
657 | + st = os.lstat('a-file') |
658 | + sha1sum = dirstate.update_entry(state, entry, 'a-file', st) |
659 | + # We updated the current sha1sum because the file is cacheable |
660 | + self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3', |
661 | + sha1sum) |
662 | |
663 | # The dirblock has been updated |
664 | - self.assertEqual(7, entry[1][0][2]) |
665 | + self.assertEqual(st.st_size, entry[1][0][2]) |
666 | self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED, |
667 | state._dirblock_state) |
668 | |
669 | @@ -606,29 +607,24 @@ |
670 | state.lock_read() |
671 | try: |
672 | entry = state._get_entry(0, path_utf8='a-file') |
673 | - self.assertEqual(7, entry[1][0][2]) |
674 | + self.assertEqual(st.st_size, entry[1][0][2]) |
675 | finally: |
676 | state.unlock() |
677 | |
678 | def test_save_fails_quietly_if_locked(self): |
679 | """If dirstate is locked, save will fail without complaining.""" |
680 | - self.build_tree(['a-file']) |
681 | - state = dirstate.DirState.initialize('dirstate') |
682 | - try: |
683 | - # No stat and no sha1 sum. |
684 | - state.add('a-file', 'a-file-id', 'file', None, '') |
685 | - state.save() |
686 | - finally: |
687 | - state.unlock() |
688 | - |
689 | - state = dirstate.DirState.on_file('dirstate') |
690 | - state.lock_read() |
691 | + state = self.create_updated_dirstate() |
692 | try: |
693 | entry = state._get_entry(0, path_utf8='a-file') |
694 | - sha1sum = dirstate.update_entry(state, entry, 'a-file', |
695 | - os.lstat('a-file')) |
696 | - # No sha - too new |
697 | - self.assertEqual(None, sha1sum) |
698 | + # No cached sha1 yet. |
699 | + self.assertEqual('', entry[1][0][1]) |
700 | + # Set the cutoff-time into the future, so things look cacheable |
701 | + state._sha_cutoff_time() |
702 | + state._cutoff_time += 10.0 |
703 | + st = os.lstat('a-file') |
704 | + sha1sum = dirstate.update_entry(state, entry, 'a-file', st) |
705 | + self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3', |
706 | + sha1sum) |
707 | self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED, |
708 | state._dirblock_state) |
709 | |
710 | |
711 | === modified file 'bzrlib/tests/test_revisionspec.py' |
712 | --- bzrlib/tests/test_revisionspec.py 2011-04-14 07:53:38 +0000 |
713 | +++ bzrlib/tests/test_revisionspec.py 2011-05-03 10:31:11 +0000 |
714 | @@ -397,8 +397,7 @@ |
715 | """We can get any revision id in the repository""" |
716 | # XXX: This may change in the future, but for now, it is true |
717 | self.tree2.commit('alt third', rev_id='alt_r3') |
718 | - self.tree.branch.repository.fetch(self.tree2.branch.repository, |
719 | - revision_id='alt_r3') |
720 | + self.tree.branch.fetch(self.tree2.branch, 'alt_r3') |
721 | self.assertInHistoryIs(None, 'alt_r3', 'revid:alt_r3') |
722 | |
723 | def test_unicode(self): |
724 | @@ -475,8 +474,7 @@ |
725 | def test_alt_no_parents(self): |
726 | new_tree = self.make_branch_and_tree('new_tree') |
727 | new_tree.commit('first', rev_id='new_r1') |
728 | - self.tree.branch.repository.fetch(new_tree.branch.repository, |
729 | - revision_id='new_r1') |
730 | + self.tree.branch.fetch(new_tree.branch, 'new_r1') |
731 | self.assertInHistoryIs(0, 'null:', 'before:revid:new_r1') |
732 | |
733 | def test_as_revision_id(self): |
734 | |
735 | === modified file 'bzrlib/workingtree_4.py' |
736 | --- bzrlib/workingtree_4.py 2011-04-17 23:06:22 +0000 |
737 | +++ bzrlib/workingtree_4.py 2011-05-03 10:31:11 +0000 |
738 | @@ -2016,7 +2016,7 @@ |
739 | def make_source_parent_tree(source, target): |
740 | """Change the source tree into a parent of the target.""" |
741 | revid = source.commit('record tree') |
742 | - target.branch.repository.fetch(source.branch.repository, revid) |
743 | + target.branch.fetch(source.branch, revid) |
744 | target.set_parent_ids([revid]) |
745 | return target.basis_tree(), target |
746 | |
747 | |
748 | === modified file 'doc/en/release-notes/bzr-2.4.txt' |
749 | --- doc/en/release-notes/bzr-2.4.txt 2011-04-21 00:20:51 +0000 |
750 | +++ doc/en/release-notes/bzr-2.4.txt 2011-05-03 10:31:11 +0000 |
751 | @@ -111,6 +111,10 @@ |
752 | .. Changes that may require updates in plugins or other code that uses |
753 | bzrlib. |
754 | |
755 | +* ``Branch.update_revisions`` has been made private and should no |
756 | + longer be used by external users. Use ``Branch.pull`` or ``Branch.push`` |
757 | + instead. (Jelmer Vernooij, #771765) |
758 | + |
759 | * Commands now have an `invoked_as` attribute, showing the name under |
760 | which they were called before alias expansion. |
761 | (Martin Pool) |
762 | @@ -304,6 +308,12 @@ |
763 | * ``bzr serve`` no longer crashes when a server_started hook is installed and |
764 | IPv6 support is available on the system. (Jelmer Vernooij, #293697) |
765 | |
766 | +* ``bzr status`` will not rewrite the dirstate file if it only has |
767 | + 'trivial' changes. (Currently limited to dir updates and newly-added |
768 | + files changing state.) This saves a bit of time for regular operations. |
769 | + eg. ``bzr status`` in a 100k tree takes 1.4s to compute the status, but 1s |
770 | + to re-save the dirstate file. (John Arbash Meinel, #765881) |
771 | + |
772 | * ``bzr tags`` will no longer choke on branches with ghost revisions in |
773 | their mainline and tags on revisions not in the branch ancestry. |
774 | (Jelmer Vernooij, #397556) |
775 | @@ -360,7 +370,7 @@ |
776 | (Jelmer Vernooij) |
777 | |
778 | * ``Branch.fetch`` implementations must now accept an optional |
779 | - ``fetch_spec`` keyword argument. (Andrew Bennetts) |
780 | + ``fetch_tags`` keyword argument. (Andrew Bennetts) |
781 | |
782 | * ``Branch.import_last_revision_info`` is deprecated. Use the |
783 | ``import_last_revision_info_and_tags`` method instead. |
Attempt to merge into lp:~jelmer/bzr/foreign failed due to conflicts:
text conflict in doc/en/ release- notes/bzr- 2.4.txt