Merge lp:~spiv/bzr/merge-minor-refactoring into lp:bzr

Proposed by Andrew Bennetts
Status: Merged
Merged at revision: not available
Proposed branch: lp:~spiv/bzr/merge-minor-refactoring
Merge into: lp:bzr
Diff against target: 456 lines (+198/-167)
5 files modified
bzrlib/merge.py (+1/-2)
bzrlib/tests/__init__.py (+1/-0)
bzrlib/tests/per_merger.py (+191/-0)
bzrlib/tests/test_merge.py (+1/-155)
bzrlib/tests/test_merge_core.py (+4/-10)
To merge this branch: bzr merge lp:~spiv/bzr/merge-minor-refactoring
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+16326@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote :

This has a few minor changes in preparation for <lp:~spiv/bzr/per-file-merge-hook-491711>:

 * adds a per_merger test module for testing bzrlib.merge.Merger implementations, replacing the ad hoc subclassing in test_merge.
 * removes some duplication in merge.py
 * removes some unused imports

Revision history for this message
Martin Pool (mbp) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/merge.py'
2--- bzrlib/merge.py 2009-12-10 17:16:19 +0000
3+++ bzrlib/merge.py 2009-12-18 08:25:25 +0000
4@@ -25,7 +25,6 @@
5 osutils,
6 patiencediff,
7 progress,
8- registry,
9 revision as _mod_revision,
10 textfile,
11 trace,
12@@ -240,7 +239,7 @@
13 if self.other_rev_id is None:
14 other_basis_tree = self.revision_tree(self.other_basis)
15 if other_basis_tree.has_changes(self.other_tree):
16- raise WorkingTreeNotRevision(self.this_tree)
17+ raise errors.WorkingTreeNotRevision(self.this_tree)
18 other_rev_id = self.other_basis
19 self.other_tree = other_basis_tree
20
21
22=== modified file 'bzrlib/tests/__init__.py'
23--- bzrlib/tests/__init__.py 2009-12-08 21:46:07 +0000
24+++ bzrlib/tests/__init__.py 2009-12-18 08:25:25 +0000
25@@ -3746,6 +3746,7 @@
26 'bzrlib.tests.per_inventory',
27 'bzrlib.tests.per_interbranch',
28 'bzrlib.tests.per_lock',
29+ 'bzrlib.tests.per_merger',
30 'bzrlib.tests.per_transport',
31 'bzrlib.tests.per_tree',
32 'bzrlib.tests.per_pack_repository',
33
34=== added file 'bzrlib/tests/per_merger.py'
35--- bzrlib/tests/per_merger.py 1970-01-01 00:00:00 +0000
36+++ bzrlib/tests/per_merger.py 2009-12-18 08:25:25 +0000
37@@ -0,0 +1,191 @@
38+# Copyright (C) 2009 Canonical Ltd
39+#
40+# This program is free software; you can redistribute it and/or modify
41+# it under the terms of the GNU General Public License as published by
42+# the Free Software Foundation; either version 2 of the License, or
43+# (at your option) any later version.
44+#
45+# This program is distributed in the hope that it will be useful,
46+# but WITHOUT ANY WARRANTY; without even the implied warranty of
47+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48+# GNU General Public License for more details.
49+#
50+# You should have received a copy of the GNU General Public License
51+# along with this program; if not, write to the Free Software
52+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53+
54+"""Implementation tests for bzrlib.merge.Merger."""
55+
56+import os
57+
58+from bzrlib import (
59+ errors,
60+ merge as _mod_merge,
61+ option,
62+ progress,
63+ )
64+from bzrlib.tests import (
65+ multiply_tests,
66+ TestCaseWithTransport,
67+ )
68+from bzrlib.transform import TreeTransform
69+
70+
71+
72+def load_tests(standard_tests, module, loader):
73+ """Multiply tests for tranport implementations."""
74+ result = loader.suiteClass()
75+ scenarios = [
76+ (name, {'merge_type': merger})
77+ for name, merger in option._merge_type_registry.items()]
78+ return multiply_tests(standard_tests, scenarios, result)
79+
80+
81+class TestMergeImplementation(TestCaseWithTransport):
82+
83+ def do_merge(self, target_tree, source_tree, **kwargs):
84+ merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
85+ target_tree, source_tree.last_revision(),
86+ other_branch=source_tree.branch)
87+ merger.merge_type=self.merge_type
88+ for name, value in kwargs.items():
89+ setattr(merger, name, value)
90+ merger.do_merge()
91+
92+ def test_merge_specific_file(self):
93+ this_tree = self.make_branch_and_tree('this')
94+ this_tree.lock_write()
95+ self.addCleanup(this_tree.unlock)
96+ self.build_tree_contents([
97+ ('this/file1', 'a\nb\n'),
98+ ('this/file2', 'a\nb\n')
99+ ])
100+ this_tree.add(['file1', 'file2'])
101+ this_tree.commit('Added files')
102+ other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
103+ self.build_tree_contents([
104+ ('other/file1', 'a\nb\nc\n'),
105+ ('other/file2', 'a\nb\nc\n')
106+ ])
107+ other_tree.commit('modified both')
108+ self.build_tree_contents([
109+ ('this/file1', 'd\na\nb\n'),
110+ ('this/file2', 'd\na\nb\n')
111+ ])
112+ this_tree.commit('modified both')
113+ self.do_merge(this_tree, other_tree, interesting_files=['file1'])
114+ self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
115+ self.assertFileEqual('d\na\nb\n', 'this/file2')
116+
117+ def test_merge_move_and_change(self):
118+ this_tree = self.make_branch_and_tree('this')
119+ this_tree.lock_write()
120+ self.addCleanup(this_tree.unlock)
121+ self.build_tree_contents([
122+ ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
123+ ])
124+ this_tree.add('file1',)
125+ this_tree.commit('Added file')
126+ other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
127+ self.build_tree_contents([
128+ ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
129+ ])
130+ other_tree.commit('Changed 2 to 2.1')
131+ self.build_tree_contents([
132+ ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
133+ ])
134+ this_tree.commit('Swapped 2 & 3')
135+ self.do_merge(this_tree, other_tree)
136+ if self.merge_type is _mod_merge.LCAMerger:
137+ self.expectFailure(
138+ "lca merge doesn't conflict for move and change",
139+ self.assertFileEqual,
140+ 'line 1\n'
141+ '<<<<<<< TREE\n'
142+ 'line 3\n'
143+ 'line 2\n'
144+ '=======\n'
145+ 'line 2 to 2.1\n'
146+ 'line 3\n'
147+ '>>>>>>> MERGE-SOURCE\n'
148+ 'line 4\n', 'this/file1')
149+ else:
150+ self.assertFileEqual('line 1\n'
151+ '<<<<<<< TREE\n'
152+ 'line 3\n'
153+ 'line 2\n'
154+ '=======\n'
155+ 'line 2 to 2.1\n'
156+ 'line 3\n'
157+ '>>>>>>> MERGE-SOURCE\n'
158+ 'line 4\n', 'this/file1')
159+
160+ def test_modify_conflicts_with_delete(self):
161+ # If one side deletes a line, and the other modifies that line, then
162+ # the modification should be considered a conflict
163+ builder = self.make_branch_builder('test')
164+ builder.start_series()
165+ builder.build_snapshot('BASE-id', None,
166+ [('add', ('', None, 'directory', None)),
167+ ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
168+ ])
169+ # Delete 'b\n'
170+ builder.build_snapshot('OTHER-id', ['BASE-id'],
171+ [('modify', ('foo-id', 'a\nc\nd\ne\n'))])
172+ # Modify 'b\n', add 'X\n'
173+ builder.build_snapshot('THIS-id', ['BASE-id'],
174+ [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))])
175+ builder.finish_series()
176+ branch = builder.get_branch()
177+ this_tree = branch.bzrdir.create_workingtree()
178+ this_tree.lock_write()
179+ self.addCleanup(this_tree.unlock)
180+ other_tree = this_tree.bzrdir.sprout('other', 'OTHER-id').open_workingtree()
181+ self.do_merge(this_tree, other_tree)
182+ if self.merge_type is _mod_merge.LCAMerger:
183+ self.expectFailure("lca merge doesn't track deleted lines",
184+ self.assertFileEqual,
185+ 'a\n'
186+ '<<<<<<< TREE\n'
187+ 'b2\n'
188+ '=======\n'
189+ '>>>>>>> MERGE-SOURCE\n'
190+ 'c\n'
191+ 'd\n'
192+ 'X\n'
193+ 'e\n', 'test/foo')
194+ else:
195+ self.assertFileEqual(
196+ 'a\n'
197+ '<<<<<<< TREE\n'
198+ 'b2\n'
199+ '=======\n'
200+ '>>>>>>> MERGE-SOURCE\n'
201+ 'c\n'
202+ 'd\n'
203+ 'X\n'
204+ 'e\n', 'test/foo')
205+
206+ def get_limbodir_deletiondir(self, wt):
207+ transform = TreeTransform(wt)
208+ limbodir = transform._limbodir
209+ deletiondir = transform._deletiondir
210+ transform.finalize()
211+ return (limbodir, deletiondir)
212+
213+ def test_merge_with_existing_limbo(self):
214+ wt = self.make_branch_and_tree('this')
215+ (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
216+ os.mkdir(limbodir)
217+ self.assertRaises(errors.ExistingLimbo, self.do_merge, wt, wt)
218+ self.assertRaises(errors.LockError, wt.unlock)
219+
220+ def test_merge_with_pending_deletion(self):
221+ wt = self.make_branch_and_tree('this')
222+ (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
223+ os.mkdir(deletiondir)
224+ self.assertRaises(errors.ExistingPendingDeletion, self.do_merge, wt, wt)
225+ self.assertRaises(errors.LockError, wt.unlock)
226+
227+
228+
229
230=== modified file 'bzrlib/tests/test_merge.py'
231--- bzrlib/tests/test_merge.py 2009-12-10 17:16:19 +0000
232+++ bzrlib/tests/test_merge.py 2009-12-18 08:25:25 +0000
233@@ -29,14 +29,12 @@
234 transform,
235 versionedfile,
236 )
237-from bzrlib.branch import Branch
238 from bzrlib.conflicts import ConflictList, TextConflict
239-from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
240+from bzrlib.errors import UnrelatedBranches, NoCommits
241 from bzrlib.merge import transform_tree, merge_inner, _PlanMerge
242 from bzrlib.osutils import pathjoin, file_kind
243 from bzrlib.tests import TestCaseWithTransport, TestCaseWithMemoryTransport
244 from bzrlib.workingtree import WorkingTree
245-from bzrlib.transform import TreeTransform
246
247
248 class TestMerge(TestCaseWithTransport):
249@@ -1144,158 +1142,6 @@
250 ], list(plan))
251
252
253-class TestMergeImplementation(object):
254-
255- def do_merge(self, target_tree, source_tree, **kwargs):
256- merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
257- target_tree, source_tree.last_revision(),
258- other_branch=source_tree.branch)
259- merger.merge_type=self.merge_type
260- for name, value in kwargs.items():
261- setattr(merger, name, value)
262- merger.do_merge()
263-
264- def test_merge_specific_file(self):
265- this_tree = self.make_branch_and_tree('this')
266- this_tree.lock_write()
267- self.addCleanup(this_tree.unlock)
268- self.build_tree_contents([
269- ('this/file1', 'a\nb\n'),
270- ('this/file2', 'a\nb\n')
271- ])
272- this_tree.add(['file1', 'file2'])
273- this_tree.commit('Added files')
274- other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
275- self.build_tree_contents([
276- ('other/file1', 'a\nb\nc\n'),
277- ('other/file2', 'a\nb\nc\n')
278- ])
279- other_tree.commit('modified both')
280- self.build_tree_contents([
281- ('this/file1', 'd\na\nb\n'),
282- ('this/file2', 'd\na\nb\n')
283- ])
284- this_tree.commit('modified both')
285- self.do_merge(this_tree, other_tree, interesting_files=['file1'])
286- self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
287- self.assertFileEqual('d\na\nb\n', 'this/file2')
288-
289- def test_merge_move_and_change(self):
290- this_tree = self.make_branch_and_tree('this')
291- this_tree.lock_write()
292- self.addCleanup(this_tree.unlock)
293- self.build_tree_contents([
294- ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
295- ])
296- this_tree.add('file1',)
297- this_tree.commit('Added file')
298- other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
299- self.build_tree_contents([
300- ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
301- ])
302- other_tree.commit('Changed 2 to 2.1')
303- self.build_tree_contents([
304- ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
305- ])
306- this_tree.commit('Swapped 2 & 3')
307- self.do_merge(this_tree, other_tree)
308- self.assertFileEqual('line 1\n'
309- '<<<<<<< TREE\n'
310- 'line 3\n'
311- 'line 2\n'
312- '=======\n'
313- 'line 2 to 2.1\n'
314- 'line 3\n'
315- '>>>>>>> MERGE-SOURCE\n'
316- 'line 4\n', 'this/file1')
317-
318- def test_modify_conflicts_with_delete(self):
319- # If one side deletes a line, and the other modifies that line, then
320- # the modification should be considered a conflict
321- builder = self.make_branch_builder('test')
322- builder.start_series()
323- builder.build_snapshot('BASE-id', None,
324- [('add', ('', None, 'directory', None)),
325- ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
326- ])
327- # Delete 'b\n'
328- builder.build_snapshot('OTHER-id', ['BASE-id'],
329- [('modify', ('foo-id', 'a\nc\nd\ne\n'))])
330- # Modify 'b\n', add 'X\n'
331- builder.build_snapshot('THIS-id', ['BASE-id'],
332- [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))])
333- builder.finish_series()
334- branch = builder.get_branch()
335- this_tree = branch.bzrdir.create_workingtree()
336- this_tree.lock_write()
337- self.addCleanup(this_tree.unlock)
338- other_tree = this_tree.bzrdir.sprout('other', 'OTHER-id').open_workingtree()
339- self.do_merge(this_tree, other_tree)
340- if self.merge_type is _mod_merge.LCAMerger:
341- self.expectFailure("lca merge doesn't track deleted lines",
342- self.assertFileEqual,
343- 'a\n'
344- '<<<<<<< TREE\n'
345- 'b2\n'
346- '=======\n'
347- '>>>>>>> MERGE-SOURCE\n'
348- 'c\n'
349- 'd\n'
350- 'X\n'
351- 'e\n', 'test/foo')
352- else:
353- self.assertFileEqual(
354- 'a\n'
355- '<<<<<<< TREE\n'
356- 'b2\n'
357- '=======\n'
358- '>>>>>>> MERGE-SOURCE\n'
359- 'c\n'
360- 'd\n'
361- 'X\n'
362- 'e\n', 'test/foo')
363-
364- def get_limbodir_deletiondir(self, wt):
365- transform = TreeTransform(wt)
366- limbodir = transform._limbodir
367- deletiondir = transform._deletiondir
368- transform.finalize()
369- return (limbodir, deletiondir)
370-
371- def test_merge_with_existing_limbo(self):
372- wt = self.make_branch_and_tree('this')
373- (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
374- os.mkdir(limbodir)
375- self.assertRaises(errors.ExistingLimbo, self.do_merge, wt, wt)
376- self.assertRaises(errors.LockError, wt.unlock)
377-
378- def test_merge_with_pending_deletion(self):
379- wt = self.make_branch_and_tree('this')
380- (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
381- os.mkdir(deletiondir)
382- self.assertRaises(errors.ExistingPendingDeletion, self.do_merge, wt, wt)
383- self.assertRaises(errors.LockError, wt.unlock)
384-
385-
386-class TestMerge3Merge(TestCaseWithTransport, TestMergeImplementation):
387-
388- merge_type = _mod_merge.Merge3Merger
389-
390-
391-class TestWeaveMerge(TestCaseWithTransport, TestMergeImplementation):
392-
393- merge_type = _mod_merge.WeaveMerger
394-
395-
396-class TestLCAMerge(TestCaseWithTransport, TestMergeImplementation):
397-
398- merge_type = _mod_merge.LCAMerger
399-
400- def test_merge_move_and_change(self):
401- self.expectFailure("lca merge doesn't conflict for move and change",
402- super(TestLCAMerge, self).test_merge_move_and_change)
403-
404-
405 class LoggingMerger(object):
406 # These seem to be the required attributes
407 requires_base = False
408
409=== modified file 'bzrlib/tests/test_merge_core.py'
410--- bzrlib/tests/test_merge_core.py 2009-12-08 21:04:07 +0000
411+++ bzrlib/tests/test_merge_core.py 2009-12-18 08:25:25 +0000
412@@ -15,32 +15,26 @@
413 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
414
415 import os
416-import stat
417 import sys
418
419 import bzrlib
420 from bzrlib import (
421+ errors,
422 generate_ids,
423 merge_directive,
424 osutils,
425 )
426 from bzrlib.conflicts import ContentsConflict, TextConflict, PathConflict
427-from bzrlib import errors
428-from bzrlib.errors import (NotBranchError, NotVersionedError,
429- WorkingTreeNotRevision, BzrCommandError, NoDiff3)
430-from bzrlib import inventory
431 from bzrlib.merge import (
432 Merge3Merger,
433 Diff3Merger,
434 WeaveMerger,
435 Merger,
436 )
437-from bzrlib.osutils import (file_kind, getcwd, pathjoin, rename,
438- sha_file,
439- )
440+from bzrlib.osutils import getcwd, pathjoin
441 from bzrlib import progress
442 from bzrlib.transform import TreeTransform
443-from bzrlib.tests import TestCaseWithTransport, TestCase, TestSkipped
444+from bzrlib.tests import TestCaseWithTransport, TestSkipped
445 from bzrlib.workingtree import WorkingTree
446
447
448@@ -277,7 +271,7 @@
449 " and therefore always fails on win32")
450 try:
451 self.do_contents_test(Diff3Merger)
452- except NoDiff3:
453+ except errors.NoDiff3:
454 raise TestSkipped("diff3 not available")
455
456 def test_contents_merge3(self):