Merge lp:~vila/bzr/tree-has-changes into lp:~bzr/bzr/trunk-old
- tree-has-changes
- Merge into trunk-old
Status: | Superseded |
---|---|
Proposed branch: | lp:~vila/bzr/tree-has-changes |
Merge into: | lp:~bzr/bzr/trunk-old |
Diff against target: | 284 lines |
To merge this branch: | bzr merge lp:~vila/bzr/tree-has-changes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
bzr-core | Pending | ||
Review via email: mp+8125@code.launchpad.net |
This proposal has been superseded by a proposal from 2009-07-08.
Commit message
Description of the change
Vincent Ladeuil (vila) wrote : | # |
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Vincent Ladeuil wrote:
> Vincent Ladeuil has proposed merging lp:~vila/bzr/tree-has-changes into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
>
> More an RFC than a merge request, this patch implements a new tree.has_changes()
> method as discussed during bug #284038 reviews.
>
> The way I have to filter the root seems ugly but I couldn't find
> an alternative, so feedback welcome.
>
> I was also surprised to be able to use _has_changes() in so many different places,
> that may have an impact on performances (and for once may be more in real life than
> in benchmarks).
>
> John also asked for some kind of has_changes_
> I also like feedback on that as it seems that there are more use-cases than we discuss
> and/or latent bugs (see XXX marks).
>
Do we want to do this as:
wt.has_
or do we want it as simply
wt.has_changes()
And internally it looks at "self.basis_
For example, RevisionTrees know explicitly that they can't have changes,
arguably calling it is undefined. I would consider making the function
part of the MutableTree api, and not the base Tree api.
Because of lock lifetimes, etc, I'd rather handle it internally.
I'm not sure what is going on in "merge", but I'm pretty sure it is
doing the same thing.
I think it makes the api cleaner as then we don't have to worry about
arbitrary targets for "has_changes()".
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkp
8s0An3XWw0FDMuK
=lGw2
-----END PGP SIGNATURE-----
Robert Collins (lifeless) wrote : | # |
On Thu, 2009-07-02 at 17:00 +0000, John A Meinel wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> Do we want to do this as:
>
> wt.has_
>
> or do we want it as simply
>
> wt.has_changes()
I don't have a strong opinion; if I were deciding I would look at where
it is used: if it is used in operations that take -r parameters, then
the former is probably correct. If its only ever desirable to know if
there are changes without examining them then doing it just on mutable
tree seems ok to me.
-Rob
Preview Diff
1 | === modified file 'bzrlib/builtins.py' |
2 | --- bzrlib/builtins.py 2009-07-02 11:37:38 +0000 |
3 | +++ bzrlib/builtins.py 2009-07-08 12:36:36 +0000 |
4 | @@ -449,18 +449,18 @@ |
5 | except errors.NoWorkingTree: |
6 | raise errors.BzrCommandError("No working tree to remove") |
7 | except errors.NotLocalUrl: |
8 | - raise errors.BzrCommandError("You cannot remove the working tree of a " |
9 | - "remote path") |
10 | + raise errors.BzrCommandError("You cannot remove the working tree" |
11 | + " of a remote path") |
12 | if not force: |
13 | - changes = working.changes_from(working.basis_tree()) |
14 | - if changes.has_changed(): |
15 | + # XXX: What about pending merges ? -- vila 20090629 |
16 | + if working.has_changes(working.basis_tree()): |
17 | raise errors.UncommittedChanges(working) |
18 | |
19 | working_path = working.bzrdir.root_transport.base |
20 | branch_path = working.branch.bzrdir.root_transport.base |
21 | if working_path != branch_path: |
22 | - raise errors.BzrCommandError("You cannot remove the working tree from " |
23 | - "a lightweight checkout") |
24 | + raise errors.BzrCommandError("You cannot remove the working tree" |
25 | + " from a lightweight checkout") |
26 | |
27 | d.destroy_workingtree() |
28 | |
29 | @@ -1114,8 +1114,8 @@ |
30 | revision_id = None |
31 | if (tree is not None and revision_id is None |
32 | and (strict is None or strict)): # Default to True: |
33 | - changes = tree.changes_from(tree.basis_tree()) |
34 | - if changes.has_changed() or len(tree.get_parent_ids()) > 1: |
35 | + if (tree.has_changes(tree.basis_tree()) |
36 | + or len(tree.get_parent_ids()) > 1): |
37 | raise errors.UncommittedChanges( |
38 | tree, more='Use --no-strict to force the push.') |
39 | if tree.last_revision() != tree.branch.last_revision(): |
40 | @@ -3641,8 +3641,7 @@ |
41 | except errors.NoSuchRevision: |
42 | basis_tree = tree.basis_tree() |
43 | if not force: |
44 | - changes = tree.changes_from(basis_tree) |
45 | - if changes.has_changed(): |
46 | + if tree.has_changes(basis_tree): |
47 | raise errors.UncommittedChanges(tree) |
48 | |
49 | view_info = _get_view_info_for_change_reporter(tree) |
50 | |
51 | === modified file 'bzrlib/merge.py' |
52 | --- bzrlib/merge.py 2009-04-14 15:02:46 +0000 |
53 | +++ bzrlib/merge.py 2009-07-08 12:36:36 +0000 |
54 | @@ -243,8 +243,7 @@ |
55 | |
56 | if self.other_rev_id is None: |
57 | other_basis_tree = self.revision_tree(self.other_basis) |
58 | - changes = other_basis_tree.changes_from(self.other_tree) |
59 | - if changes.has_changed(): |
60 | + if other_basis_tree.has_changes(self.other_tree): |
61 | raise WorkingTreeNotRevision(self.this_tree) |
62 | other_rev_id = self.other_basis |
63 | self.other_tree = other_basis_tree |
64 | @@ -276,8 +275,7 @@ |
65 | basis_tree = self.revision_tree(self.this_tree.last_revision()) |
66 | except errors.NoSuchRevision: |
67 | basis_tree = self.this_tree.basis_tree() |
68 | - changes = self.this_tree.changes_from(basis_tree) |
69 | - if not changes.has_changed(): |
70 | + if not self.this_tree.has_changes(basis_tree): |
71 | self.this_rev_id = self.this_basis |
72 | |
73 | def set_interesting_files(self, file_list): |
74 | |
75 | === modified file 'bzrlib/reconfigure.py' |
76 | --- bzrlib/reconfigure.py 2009-07-06 06:46:30 +0000 |
77 | +++ bzrlib/reconfigure.py 2009-07-08 12:36:36 +0000 |
78 | @@ -218,8 +218,8 @@ |
79 | def _check(self): |
80 | """Raise if reconfiguration would destroy local changes""" |
81 | if self._destroy_tree: |
82 | - changes = self.tree.changes_from(self.tree.basis_tree()) |
83 | - if changes.has_changed(): |
84 | + # XXX: What about pending merges ? -- vila 20090629 |
85 | + if self.tree.has_changes(self.tree.basis_tree()): |
86 | raise errors.UncommittedChanges(self.tree) |
87 | if self._create_reference and self.local_branch is not None: |
88 | reference_branch = branch.Branch.open(self._select_bind_location()) |
89 | |
90 | === modified file 'bzrlib/send.py' |
91 | --- bzrlib/send.py 2009-07-01 15:17:33 +0000 |
92 | +++ bzrlib/send.py 2009-07-08 12:36:36 +0000 |
93 | @@ -119,8 +119,8 @@ |
94 | except KeyError: |
95 | strict = None |
96 | if tree is not None and (strict is None or strict): |
97 | - changes = tree.changes_from(tree.basis_tree()) |
98 | - if changes.has_changed() or len(tree.get_parent_ids()) > 1: |
99 | + if (tree.has_changes(tree.basis_tree()) |
100 | + or len(tree.get_parent_ids()) > 1): |
101 | raise errors.UncommittedChanges( |
102 | tree, more='Use --no-strict to force the send.') |
103 | if tree.last_revision() != tree.branch.last_revision(): |
104 | |
105 | === modified file 'bzrlib/tests/intertree_implementations/test_compare.py' |
106 | --- bzrlib/tests/intertree_implementations/test_compare.py 2009-04-09 20:23:07 +0000 |
107 | +++ bzrlib/tests/intertree_implementations/test_compare.py 2009-07-08 12:36:36 +0000 |
108 | @@ -367,6 +367,15 @@ |
109 | tree1.unlock() |
110 | tree2.unlock() |
111 | |
112 | + def do_has_changes(self, tree1, tree2, **extra_args): |
113 | + tree1.lock_read() |
114 | + tree2.lock_read() |
115 | + try: |
116 | + return tree1.has_changes(tree2) |
117 | + finally: |
118 | + tree1.unlock() |
119 | + tree2.unlock() |
120 | + |
121 | def mutable_trees_to_locked_test_trees(self, tree1, tree2): |
122 | """Convert the working trees into test trees. |
123 | |
124 | @@ -436,6 +445,7 @@ |
125 | tree2 = self.get_tree_no_parents_no_content(tree2) |
126 | tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2) |
127 | self.assertEqual([], self.do_iter_changes(tree1, tree2)) |
128 | + self.assertEquals(False, self.do_has_changes(tree1, tree2)) |
129 | |
130 | def added(self, tree, file_id): |
131 | path, entry = self.get_path_entry(tree, file_id) |
132 | @@ -519,6 +529,7 @@ |
133 | self.added(tree2, 'c-id'), |
134 | self.deleted(tree1, 'empty-root-id')]) |
135 | self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2)) |
136 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
137 | |
138 | def test_empty_specific_files(self): |
139 | tree1 = self.make_branch_and_tree('1') |
140 | @@ -542,6 +553,7 @@ |
141 | self.added(tree2, 'c-id'), |
142 | self.deleted(tree1, 'empty-root-id')]) |
143 | self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2)) |
144 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
145 | |
146 | def test_empty_to_abc_content_a_only(self): |
147 | tree1 = self.make_branch_and_tree('1') |
148 | @@ -593,6 +605,7 @@ |
149 | self.assertEqual( |
150 | expected_results, |
151 | self.do_iter_changes(tree1, tree2)) |
152 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
153 | |
154 | def test_content_modification(self): |
155 | tree1 = self.make_branch_and_tree('1') |
156 | @@ -605,6 +618,7 @@ |
157 | (root_id, root_id), ('a', 'a'), |
158 | ('file', 'file'), (False, False))], |
159 | self.do_iter_changes(tree1, tree2)) |
160 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
161 | |
162 | def test_meta_modification(self): |
163 | tree1 = self.make_branch_and_tree('1') |
164 | @@ -834,6 +848,7 @@ |
165 | self.content_changed(tree2, 'c-id'), |
166 | ]) |
167 | self.assertEqual(expected, self.do_iter_changes(tree1, tree2)) |
168 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
169 | |
170 | def test_unversioned_paths_in_tree(self): |
171 | tree1 = self.make_branch_and_tree('tree1') |
172 | @@ -1051,6 +1066,7 @@ |
173 | self.assertEqual(expected, |
174 | self.do_iter_changes(tree1, tree2, include_unchanged=True, |
175 | want_unversioned=True)) |
176 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
177 | |
178 | def test_versioned_symlinks_specific_files(self): |
179 | self.requireFeature(tests.SymlinkFeature) |
180 | @@ -1072,17 +1088,20 @@ |
181 | self.assertEqual(expected, self.do_iter_changes(tree1, tree2, |
182 | specific_files=['added', 'changed', 'fromdir', 'fromfile', |
183 | 'removed', 'unchanged', 'todir', 'tofile'])) |
184 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
185 | |
186 | def test_tree_with_special_names(self): |
187 | tree1, tree2, paths, path_ids = self.make_tree_with_special_names() |
188 | expected = sorted(self.added(tree2, f_id) for f_id in path_ids) |
189 | self.assertEqual(expected, self.do_iter_changes(tree1, tree2)) |
190 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
191 | |
192 | def test_trees_with_special_names(self): |
193 | tree1, tree2, paths, path_ids = self.make_trees_with_special_names() |
194 | expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids |
195 | if f_id.endswith('_f-id')) |
196 | self.assertEqual(expected, self.do_iter_changes(tree1, tree2)) |
197 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
198 | |
199 | def test_trees_with_deleted_dir(self): |
200 | tree1 = self.make_branch_and_tree('tree1') |
201 | @@ -1106,6 +1125,7 @@ |
202 | self.deleted(tree1, 'e-id'), |
203 | ]) |
204 | self.assertEqual(expected, self.do_iter_changes(tree1, tree2)) |
205 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
206 | |
207 | def test_added_unicode(self): |
208 | tree1 = self.make_branch_and_tree('tree1') |
209 | @@ -1134,6 +1154,7 @@ |
210 | self.assertEqual([self.added(tree2, added_id)], |
211 | self.do_iter_changes(tree1, tree2, |
212 | specific_files=[u'\u03b1'])) |
213 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
214 | |
215 | def test_deleted_unicode(self): |
216 | tree1 = self.make_branch_and_tree('tree1') |
217 | @@ -1162,6 +1183,7 @@ |
218 | self.assertEqual([self.deleted(tree1, deleted_id)], |
219 | self.do_iter_changes(tree1, tree2, |
220 | specific_files=[u'\u03b1'])) |
221 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
222 | |
223 | def test_modified_unicode(self): |
224 | tree1 = self.make_branch_and_tree('tree1') |
225 | @@ -1191,6 +1213,7 @@ |
226 | self.assertEqual([self.content_changed(tree1, mod_id)], |
227 | self.do_iter_changes(tree1, tree2, |
228 | specific_files=[u'\u03b1'])) |
229 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
230 | |
231 | def test_renamed_unicode(self): |
232 | tree1 = self.make_branch_and_tree('tree1') |
233 | @@ -1221,6 +1244,7 @@ |
234 | self.assertEqual([self.renamed(tree1, tree2, rename_id, False)], |
235 | self.do_iter_changes(tree1, tree2, |
236 | specific_files=[u'\u03b1'])) |
237 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
238 | |
239 | def test_unchanged_unicode(self): |
240 | tree1 = self.make_branch_and_tree('tree1') |
241 | @@ -1309,6 +1333,7 @@ |
242 | want_unversioned=True)) |
243 | self.assertEqual([], # Without want_unversioned we should get nothing |
244 | self.do_iter_changes(tree1, tree2)) |
245 | + self.assertEquals(False, self.do_has_changes(tree1, tree2)) |
246 | |
247 | # We should also be able to select just a subset |
248 | expected = sorted([ |
249 | @@ -1389,6 +1414,7 @@ |
250 | ]) |
251 | self.assertEqual(expected, |
252 | self.do_iter_changes(tree1, tree2)) |
253 | + self.assertEquals(True, self.do_has_changes(tree1, tree2)) |
254 | |
255 | def test_deleted_and_unknown(self): |
256 | """Test a file marked removed, but still present on disk.""" |
257 | |
258 | === modified file 'bzrlib/tree.py' |
259 | --- bzrlib/tree.py 2009-07-01 10:56:49 +0000 |
260 | +++ bzrlib/tree.py 2009-07-08 12:36:36 +0000 |
261 | @@ -102,6 +102,24 @@ |
262 | return intertree.iter_changes(include_unchanged, specific_files, pb, |
263 | extra_trees, require_versioned, want_unversioned=want_unversioned) |
264 | |
265 | + |
266 | + @needs_read_lock |
267 | + def has_changes(self, from_tree): |
268 | + """Quickly check that the tree contains at least one change. |
269 | + |
270 | + :return: True if a change is found. False otherwise |
271 | + """ |
272 | + changes = self.iter_changes(from_tree) |
273 | + try: |
274 | + change = changes.next() |
275 | + # Exclude root (talk about black magic... --vila 20090629) |
276 | + if change[4] == (None, None): |
277 | + change = changes.next() |
278 | + return True |
279 | + except StopIteration: |
280 | + # No changes |
281 | + return False |
282 | + |
283 | def conflicts(self): |
284 | """Get a list of the conflicts in the tree. |
285 |
More an RFC than a merge request, this patch implements a new tree.has_changes()
method as discussed during bug #284038 reviews.
The way I have to filter the root seems ugly but I couldn't find
an alternative, so feedback welcome.
I was also surprised to be able to use _has_changes() in so many different places,
that may have an impact on performances (and for once may be more in real life than
in benchmarks).
John also asked for some kind of has_changes_ strictly( ) to share between send and push --strict.
I also like feedback on that as it seems that there are more use-cases than we discuss
and/or latent bugs (see XXX marks).