Merge lp:~spiv/bzr/fragile-do_merge-cleanup-517275-2.1 into lp:bzr/2.1

Proposed by Andrew Bennetts
Status: Merged
Approved by: Martin Pool
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~spiv/bzr/fragile-do_merge-cleanup-517275-2.1
Merge into: lp:bzr/2.1
Diff against target: 205 lines (+59/-58)
3 files modified
NEWS (+9/-0)
bzrlib/cleanup.py (+2/-2)
bzrlib/merge.py (+48/-56)
To merge this branch: bzr merge lp:~spiv/bzr/fragile-do_merge-cleanup-517275-2.1
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+18681@code.launchpad.net

Commit message

(andrew) Replace several fragile try/finally blocks in merge.py using bzrlib.cleanup. (#517275)

To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote :

This replaces a bunch of fragile try/finally blocks with more robust cleanups using bzrlib.cleanup.

Bug 517275 only pointed out one place causing trouble, but I took the liberty of improving most of the other try/finally blocks in the module while I was there (maybe tripling the diff, but it's still not a very large patch).

As with other patches improving cleanups, this one is largely mechanical. I also improved the docstring of the bzrlib.cleanup module slightly.

Revision history for this message
Martin Pool (mbp) wrote :

looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-02-03 14:08:55 +0000
3+++ NEWS 2010-02-05 06:55:20 +0000
4@@ -32,6 +32,15 @@
5 * The new ``merge_file_content`` should now be ok with tests to avoid
6 regressions.
7 (Vincent Ladeuil, #515597)
8+
9+Internals
10+*********
11+
12+* Use ``bzrlib.cleanup`` rather than less robust ``try``/``finally``
13+ blocks in several places in ``bzrlib.merge``. This avoids masking prior
14+ errors when errors like ``ImmortalPendingDeletion`` occur during cleanup
15+ in ``do_merge``.
16+ (Andrew Bennetts, #517275)
17
18 bzr 2.1.0rc2
19 ############
20
21=== modified file 'bzrlib/cleanup.py'
22--- bzrlib/cleanup.py 2010-01-13 23:16:20 +0000
23+++ bzrlib/cleanup.py 2010-02-05 06:55:20 +0000
24@@ -31,9 +31,9 @@
25 If you want to be certain that the first, and only the first, error is raised,
26 then use::
27
28- operation = OperationWithCleanups(lambda operation: do_something())
29+ operation = OperationWithCleanups(do_something)
30 operation.add_cleanup(cleanup_something)
31- operation.run()
32+ operation.run_simple()
33
34 This is more inconvenient (because you need to make every try block a
35 function), but will ensure that the first error encountered is the one raised,
36
37=== modified file 'bzrlib/merge.py'
38--- bzrlib/merge.py 2010-02-01 17:29:44 +0000
39+++ bzrlib/merge.py 2010-02-05 06:55:20 +0000
40@@ -36,6 +36,7 @@
41 ui,
42 versionedfile
43 )
44+from bzrlib.cleanup import OperationWithCleanups
45 from bzrlib.symbol_versioning import (
46 deprecated_in,
47 deprecated_method,
48@@ -45,11 +46,10 @@
49
50 def transform_tree(from_tree, to_tree, interesting_ids=None):
51 from_tree.lock_tree_write()
52- try:
53- merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
54- interesting_ids=interesting_ids, this_tree=from_tree)
55- finally:
56- from_tree.unlock()
57+ operation = OperationWithCleanups(merge_inner)
58+ operation.add_cleanup(from_tree.unlock)
59+ operation.run_simple(from_tree.branch, to_tree, from_tree,
60+ ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
61
62
63 class MergeHooks(hooks.Hooks):
64@@ -455,6 +455,7 @@
65 def _add_parent(self):
66 new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
67 new_parent_trees = []
68+ operation = OperationWithCleanups(self.this_tree.set_parent_trees)
69 for revision_id in new_parents:
70 try:
71 tree = self.revision_tree(revision_id)
72@@ -462,14 +463,9 @@
73 tree = None
74 else:
75 tree.lock_read()
76+ operation.add_cleanup(tree.unlock)
77 new_parent_trees.append((revision_id, tree))
78- try:
79- self.this_tree.set_parent_trees(new_parent_trees,
80- allow_leftmost_as_ghost=True)
81- finally:
82- for _revision_id, tree in new_parent_trees:
83- if tree is not None:
84- tree.unlock()
85+ operation.run_simple(new_parent_trees, allow_leftmost_as_ghost=True)
86
87 def set_other(self, other_revision, possible_transports=None):
88 """Set the revision and tree to merge from.
89@@ -626,7 +622,8 @@
90 change_reporter=self.change_reporter,
91 **kwargs)
92
93- def _do_merge_to(self, merge):
94+ def _do_merge_to(self):
95+ merge = self.make_merger()
96 if self.other_branch is not None:
97 self.other_branch.update_references(self.this_branch)
98 merge.do_merge()
99@@ -646,26 +643,19 @@
100 sub_tree.branch.repository.revision_tree(base_revision)
101 sub_merge.base_rev_id = base_revision
102 sub_merge.do_merge()
103+ return merge
104
105 def do_merge(self):
106+ operation = OperationWithCleanups(self._do_merge_to)
107 self.this_tree.lock_tree_write()
108- try:
109- if self.base_tree is not None:
110- self.base_tree.lock_read()
111- try:
112- if self.other_tree is not None:
113- self.other_tree.lock_read()
114- try:
115- merge = self.make_merger()
116- self._do_merge_to(merge)
117- finally:
118- if self.other_tree is not None:
119- self.other_tree.unlock()
120- finally:
121- if self.base_tree is not None:
122- self.base_tree.unlock()
123- finally:
124- self.this_tree.unlock()
125+ operation.add_cleanup(self.this_tree.unlock)
126+ if self.base_tree is not None:
127+ self.base_tree.lock_read()
128+ operation.add_cleanup(self.base_tree.unlock)
129+ if self.other_tree is not None:
130+ self.other_tree.lock_read()
131+ operation.add_cleanup(self.other_tree.unlock)
132+ merge = operation.run_simple()
133 if len(merge.cooked_conflicts) == 0:
134 if not self.ignore_zero and not trace.is_quiet():
135 trace.note("All changes applied successfully.")
136@@ -765,41 +755,43 @@
137 self.do_merge()
138
139 def do_merge(self):
140+ operation = OperationWithCleanups(self._do_merge)
141+ operation.add_cleanup(self.pb.clear)
142 self.this_tree.lock_tree_write()
143+ operation.add_cleanup(self.this_tree.unlock)
144 self.base_tree.lock_read()
145+ operation.add_cleanup(self.base_tree.unlock)
146 self.other_tree.lock_read()
147+ operation.add_cleanup(self.other_tree.unlock)
148+ operation.run()
149+
150+ def _do_merge(self, operation):
151+ self.tt = transform.TreeTransform(self.this_tree, self.pb)
152+ operation.add_cleanup(self.tt.finalize)
153+ self.pp.next_phase()
154+ self._compute_transform()
155+ self.pp.next_phase()
156+ results = self.tt.apply(no_conflicts=True)
157+ self.write_modified(results)
158 try:
159- self.tt = transform.TreeTransform(self.this_tree, self.pb)
160- try:
161- self.pp.next_phase()
162- self._compute_transform()
163- self.pp.next_phase()
164- results = self.tt.apply(no_conflicts=True)
165- self.write_modified(results)
166- try:
167- self.this_tree.add_conflicts(self.cooked_conflicts)
168- except errors.UnsupportedOperation:
169- pass
170- finally:
171- self.tt.finalize()
172- finally:
173- self.other_tree.unlock()
174- self.base_tree.unlock()
175- self.this_tree.unlock()
176- self.pb.clear()
177+ self.this_tree.add_conflicts(self.cooked_conflicts)
178+ except errors.UnsupportedOperation:
179+ pass
180
181 def make_preview_transform(self):
182+ operation = OperationWithCleanups(self._make_preview_transform)
183+ operation.add_cleanup(self.pb.clear)
184 self.base_tree.lock_read()
185+ operation.add_cleanup(self.base_tree.unlock)
186 self.other_tree.lock_read()
187+ operation.add_cleanup(self.other_tree.unlock)
188+ return operation.run_simple()
189+
190+ def _make_preview_transform(self):
191 self.tt = transform.TransformPreview(self.this_tree)
192- try:
193- self.pp.next_phase()
194- self._compute_transform()
195- self.pp.next_phase()
196- finally:
197- self.other_tree.unlock()
198- self.base_tree.unlock()
199- self.pb.clear()
200+ self.pp.next_phase()
201+ self._compute_transform()
202+ self.pp.next_phase()
203 return self.tt
204
205 def _compute_transform(self):

Subscribers

People subscribed via source and target branches