Merge lp:~jelmer/bzr/private-update-revisions into lp:~jelmer/bzr/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
Reviewer Review Type Date Requested Status
Jelmer Vernooij Pending
Review via email: mp+59754@code.launchpad.net

Description of the change

Make InterBranch.update_revisions() private.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Attempt to merge into lp:~jelmer/bzr/foreign failed due to conflicts:

text conflict in doc/en/release-notes/bzr-2.4.txt

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.

Subscribers

People subscribed via source and target branches

to all changes: