Merge lp:~jelmer/brz/plan-merge into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: Vincent Ladeuil
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/plan-merge
Merge into: lp:brz
Diff against target: 318 lines (+84/-74)
6 files modified
breezy/builtins.py (+10/-1)
breezy/bzr/inventorytree.py (+57/-0)
breezy/bzr/versionedfile.py (+1/-1)
breezy/merge.py (+14/-15)
breezy/tests/per_tree/test_tree.py (+2/-0)
breezy/tree.py (+0/-57)
To merge this branch: bzr merge lp:~jelmer/brz/plan-merge
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+339452@code.launchpad.net

Commit message

Move Tree.plan_file_merge to InventoryTree.plan_file_merge.

Description of the change

Move Tree.plan_file_merge to InventoryTree.plan_file_merge.

Also, mark merge types that require a merge plan as such so that 'bzr merge'
can error out when trying to use --weave or --lca merge with git trees.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) 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 'breezy/builtins.py'
2--- breezy/builtins.py 2018-02-21 13:19:44 +0000
3+++ breezy/builtins.py 2018-02-24 18:57:47 +0000
4@@ -4381,7 +4381,7 @@
5 committed to record the result of the merge.
6
7 merge refuses to run if there are any uncommitted changes, unless
8- --force is given. If --force is given, then the changes from the source
9+ --force is given. If --force is given, then the changes from the source
10 will be merged with the current working tree, including any uncommitted
11 changes in the tree. The --force option can also be used to create a
12 merge revision which has more than two parents.
13@@ -4610,6 +4610,15 @@
14 raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
15 " show base."))
16
17+ if (merger.merge_type.requires_file_merge_plan and
18+ (not getattr(merger.this_tree, 'plan_file_merge', None) or
19+ not getattr(merger.other_tree, 'plan_file_merge', None) or
20+ (merger.base_tree is not None and
21+ not getattr(merger.base_tree, 'plan_file_merge', None)))):
22+ raise errors.BzrCommandError(
23+ gettext('Plan file merge unsupported: '
24+ 'Merge type incompatible with tree formats.'))
25+
26 def _get_merger_from_branch(self, tree, location, revision, remember,
27 possible_transports, pb):
28 """Produce a merger from a location, assuming it refers to a branch."""
29
30=== modified file 'breezy/bzr/inventorytree.py'
31--- breezy/bzr/inventorytree.py 2018-01-12 08:52:43 +0000
32+++ breezy/bzr/inventorytree.py 2018-02-24 18:57:47 +0000
33@@ -258,6 +258,63 @@
34 for child in viewvalues(getattr(entry, 'children', {})):
35 yield child.file_id
36
37+ def _get_plan_merge_data(self, file_id, other, base):
38+ from . import versionedfile
39+ vf = versionedfile._PlanMergeVersionedFile(file_id)
40+ last_revision_a = self._get_file_revision(
41+ self.id2path(file_id), file_id, vf, 'this:')
42+ last_revision_b = other._get_file_revision(
43+ other.id2path(file_id), file_id, vf, 'other:')
44+ if base is None:
45+ last_revision_base = None
46+ else:
47+ last_revision_base = base._get_file_revision(
48+ base.id2path(file_id), file_id, vf, 'base:')
49+ return vf, last_revision_a, last_revision_b, last_revision_base
50+
51+ def plan_file_merge(self, file_id, other, base=None):
52+ """Generate a merge plan based on annotations.
53+
54+ If the file contains uncommitted changes in this tree, they will be
55+ attributed to the 'current:' pseudo-revision. If the file contains
56+ uncommitted changes in the other tree, they will be assigned to the
57+ 'other:' pseudo-revision.
58+ """
59+ data = self._get_plan_merge_data(file_id, other, base)
60+ vf, last_revision_a, last_revision_b, last_revision_base = data
61+ return vf.plan_merge(last_revision_a, last_revision_b,
62+ last_revision_base)
63+
64+ def plan_file_lca_merge(self, file_id, other, base=None):
65+ """Generate a merge plan based lca-newness.
66+
67+ If the file contains uncommitted changes in this tree, they will be
68+ attributed to the 'current:' pseudo-revision. If the file contains
69+ uncommitted changes in the other tree, they will be assigned to the
70+ 'other:' pseudo-revision.
71+ """
72+ data = self._get_plan_merge_data(file_id, other, base)
73+ vf, last_revision_a, last_revision_b, last_revision_base = data
74+ return vf.plan_lca_merge(last_revision_a, last_revision_b,
75+ last_revision_base)
76+
77+ def _get_file_revision(self, path, file_id, vf, tree_revision):
78+ """Ensure that file_id, tree_revision is in vf to plan the merge."""
79+ if getattr(self, '_repository', None) is None:
80+ last_revision = tree_revision
81+ parent_keys = [(file_id, t.get_file_revision(path, file_id)) for t in
82+ self._iter_parent_trees()]
83+ vf.add_lines((file_id, last_revision), parent_keys,
84+ self.get_file_lines(path, file_id))
85+ repo = self.branch.repository
86+ base_vf = repo.texts
87+ else:
88+ last_revision = self.get_file_revision(path, file_id)
89+ base_vf = self._repository.texts
90+ if base_vf not in vf.fallback_versionedfiles:
91+ vf.fallback_versionedfiles.append(base_vf)
92+ return last_revision
93+
94
95 class MutableInventoryTree(MutableTree, InventoryTree):
96
97
98=== modified file 'breezy/bzr/versionedfile.py'
99--- breezy/bzr/versionedfile.py 2017-12-04 23:25:45 +0000
100+++ breezy/bzr/versionedfile.py 2018-02-24 18:57:47 +0000
101@@ -669,7 +669,7 @@
102 """
103 raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
104
105- def plan_merge(self, ver_a, ver_b):
106+ def plan_merge(self, ver_a, ver_b, base=None):
107 """Return pseudo-annotation indicating how the two versions merge.
108
109 This is computed between versions a and b and their common
110
111=== modified file 'breezy/merge.py'
112--- breezy/merge.py 2017-11-13 22:51:34 +0000
113+++ breezy/merge.py 2018-02-24 18:57:47 +0000
114@@ -94,7 +94,7 @@
115 """PerFileMerger objects are used by plugins extending merge for breezy.
116
117 See ``breezy.plugins.news_merge.news_merge`` for an example concrete class.
118-
119+
120 :ivar merger: The Merge3Merger performing the merge.
121 """
122
123@@ -104,7 +104,7 @@
124
125 def merge_contents(self, merge_params):
126 """Attempt to merge the contents of a single file.
127-
128+
129 :param merge_params: A breezy.merge.MergeFileHookParams
130 :return: A tuple of (status, chunks), where status is one of
131 'not_applicable', 'success', 'conflicted', or 'delete'. If status
132@@ -174,13 +174,13 @@
133 classes should implement ``merge_text``.
134
135 See ``breezy.plugins.news_merge.news_merge`` for an example concrete class.
136-
137+
138 :ivar affected_files: The configured file paths to merge.
139
140 :cvar name_prefix: The prefix to use when looking up configuration
141 details. <name_prefix>_merge_files describes the files targeted by the
142 hook for example.
143-
144+
145 :cvar default_files: The default file paths to merge when no configuration
146 is present.
147 """
148@@ -718,6 +718,7 @@
149 supports_reverse_cherrypick = True
150 winner_idx = {"this": 2, "other": 1, "conflict": 1}
151 supports_lca_trees = True
152+ requires_file_merge_plan = False
153
154 def __init__(self, working_tree, this_tree, base_tree, other_tree,
155 interesting_ids=None, reprocess=False, show_base=False,
156@@ -1408,7 +1409,7 @@
157 def merge_contents(self, merge_hook_params):
158 """Fallback merge logic after user installed hooks."""
159 # This function is used in merge hooks as the fallback instance.
160- # Perhaps making this function and the functions it calls be a
161+ # Perhaps making this function and the functions it calls be a
162 # a separate class would be better.
163 if merge_hook_params.winner == 'other':
164 # OTHER is a straight winner, so replace this contents with other
165@@ -1483,7 +1484,6 @@
166 other_lines)
167 file_group.append(trans_id)
168
169-
170 def _get_filter_tree_path(self, file_id):
171 if self.this_tree.supports_content_filtering():
172 # We get the path from the working tree if it exists.
173@@ -1667,6 +1667,7 @@
174 supports_show_base = False
175 supports_reverse_cherrypick = False
176 history_based = True
177+ requires_file_merge_plan = True
178
179 def _generate_merge_plan(self, file_id, base):
180 return self.this_tree.plan_file_merge(file_id, self.other_tree,
181@@ -1721,6 +1722,8 @@
182
183 class LCAMerger(WeaveMerger):
184
185+ requires_file_merge_plan = True
186+
187 def _generate_merge_plan(self, file_id, base):
188 return self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
189 base=base)
190@@ -1728,15 +1731,14 @@
191 class Diff3Merger(Merge3Merger):
192 """Three-way merger using external diff3 for text merging"""
193
194+ requires_file_merge_plan = False
195+
196 def dump_file(self, temp_dir, name, tree, file_id):
197 out_path = osutils.pathjoin(temp_dir, name)
198- out_file = open(out_path, "wb")
199- try:
200+ with open(out_path, "wb") as out_file:
201 in_file = tree.get_file(tree.id2path(file_id), file_id)
202 for line in in_file:
203 out_file.write(line)
204- finally:
205- out_file.close()
206 return out_path
207
208 def text_merge(self, file_id, trans_id):
209@@ -1754,11 +1756,8 @@
210 status = breezy.patch.diff3(new_file, this, base, other)
211 if status not in (0, 1):
212 raise errors.BzrError("Unhandled diff3 exit code")
213- f = open(new_file, 'rb')
214- try:
215+ with open(new_file, 'rb') as f:
216 self.tt.create_file(f, trans_id)
217- finally:
218- f.close()
219 if status == 1:
220 name = self.tt.final_name(trans_id)
221 parent_id = self.tt.final_parent(trans_id)
222@@ -1837,7 +1836,7 @@
223
224 class _MergeTypeParameterizer(object):
225 """Wrap a merge-type class to provide extra parameters.
226-
227+
228 This is hack used by MergeIntoMerger to pass some extra parameters to its
229 merge_type. Merger.do_merge() sets up its own set of parameters to pass to
230 the 'merge_type' member. It is difficult override do_merge without
231
232=== modified file 'breezy/tests/per_tree/test_tree.py'
233--- breezy/tests/per_tree/test_tree.py 2018-02-16 19:38:39 +0000
234+++ breezy/tests/per_tree/test_tree.py 2018-02-24 18:57:47 +0000
235@@ -56,6 +56,8 @@
236 work_b = work_a.controldir.sprout('wtb').open_workingtree()
237 self.build_tree_contents([('wta/file', 'b\nc\nd\ne\n')])
238 tree_a = self.workingtree_to_test_tree(work_a)
239+ if getattr(tree_a, 'plan_file_merge', None) is None:
240+ raise tests.TestNotApplicable('Tree does not support plan_file_merge')
241 tree_a.lock_read()
242 self.addCleanup(tree_a.unlock)
243 self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
244
245=== modified file 'breezy/tree.py'
246--- breezy/tree.py 2018-02-17 02:57:14 +0000
247+++ breezy/tree.py 2018-02-24 18:57:47 +0000
248@@ -476,46 +476,6 @@
249 """
250 raise NotImplementedError(self.annotate_iter)
251
252- def _get_plan_merge_data(self, file_id, other, base):
253- from .bzr import versionedfile
254- vf = versionedfile._PlanMergeVersionedFile(file_id)
255- last_revision_a = self._get_file_revision(
256- self.id2path(file_id), file_id, vf, 'this:')
257- last_revision_b = other._get_file_revision(
258- other.id2path(file_id), file_id, vf, 'other:')
259- if base is None:
260- last_revision_base = None
261- else:
262- last_revision_base = base._get_file_revision(
263- base.id2path(file_id), file_id, vf, 'base:')
264- return vf, last_revision_a, last_revision_b, last_revision_base
265-
266- def plan_file_merge(self, file_id, other, base=None):
267- """Generate a merge plan based on annotations.
268-
269- If the file contains uncommitted changes in this tree, they will be
270- attributed to the 'current:' pseudo-revision. If the file contains
271- uncommitted changes in the other tree, they will be assigned to the
272- 'other:' pseudo-revision.
273- """
274- data = self._get_plan_merge_data(file_id, other, base)
275- vf, last_revision_a, last_revision_b, last_revision_base = data
276- return vf.plan_merge(last_revision_a, last_revision_b,
277- last_revision_base)
278-
279- def plan_file_lca_merge(self, file_id, other, base=None):
280- """Generate a merge plan based lca-newness.
281-
282- If the file contains uncommitted changes in this tree, they will be
283- attributed to the 'current:' pseudo-revision. If the file contains
284- uncommitted changes in the other tree, they will be assigned to the
285- 'other:' pseudo-revision.
286- """
287- data = self._get_plan_merge_data(file_id, other, base)
288- vf, last_revision_a, last_revision_b, last_revision_base = data
289- return vf.plan_lca_merge(last_revision_a, last_revision_b,
290- last_revision_base)
291-
292 def _iter_parent_trees(self):
293 """Iterate through parent trees, defaulting to Tree.revision_tree."""
294 for revision_id in self.get_parent_ids():
295@@ -524,23 +484,6 @@
296 except errors.NoSuchRevisionInTree:
297 yield self.repository.revision_tree(revision_id)
298
299- def _get_file_revision(self, path, file_id, vf, tree_revision):
300- """Ensure that file_id, tree_revision is in vf to plan the merge."""
301- if getattr(self, '_repository', None) is None:
302- last_revision = tree_revision
303- parent_keys = [(file_id, t.get_file_revision(path, file_id)) for t in
304- self._iter_parent_trees()]
305- vf.add_lines((file_id, last_revision), parent_keys,
306- self.get_file_lines(path, file_id))
307- repo = self.branch.repository
308- base_vf = repo.texts
309- else:
310- last_revision = self.get_file_revision(path, file_id)
311- base_vf = self._repository.texts
312- if base_vf not in vf.fallback_versionedfiles:
313- vf.fallback_versionedfiles.append(base_vf)
314- return last_revision
315-
316 def _check_retrieved(self, ie, f):
317 if not __debug__:
318 return

Subscribers

People subscribed via source and target branches