~bzr-pqm/bzr/bzr.dev

2255.2.217 by Martin Pool
docs
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
17
import os
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
18
from StringIO import StringIO
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
19
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
20
from bzrlib import (
21
    conflicts,
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
22
    errors,
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
23
    knit,
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
24
    merge as _mod_merge,
25
    option,
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
26
    progress,
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
27
    versionedfile,
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
28
    )
974.1.56 by aaron.bentley at utoronto
Added merge test
29
from bzrlib.branch import Branch
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
30
from bzrlib.conflicts import ConflictList, TextConflict
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
31
from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
32
from bzrlib.merge import transform_tree, merge_inner, _PlanMerge
1959.4.6 by Aaron Bentley
Ensure merge works across kind changes
33
from bzrlib.osutils import pathjoin, file_kind
1534.4.28 by Robert Collins
first cut at merge from integration.
34
from bzrlib.revision import common_ancestor
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
35
from bzrlib.tests import TestCaseWithTransport, TestCaseWithMemoryTransport
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
36
from bzrlib.trace import (enable_test_log, disable_test_log)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
37
from bzrlib.workingtree import WorkingTree
38
39
40
class TestMerge(TestCaseWithTransport):
974.1.56 by aaron.bentley at utoronto
Added merge test
41
    """Test appending more than one revision"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
42
974.1.56 by aaron.bentley at utoronto
Added merge test
43
    def test_pending(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
44
        wt = self.make_branch_and_tree('.')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
45
        rev_a = wt.commit("lala!")
46
        self.assertEqual([rev_a], wt.get_parent_ids())
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
47
        self.assertRaises(errors.PointlessMerge, wt.merge_from_branch,
48
                          wt.branch)
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
49
        self.assertEqual([rev_a], wt.get_parent_ids())
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
50
        return wt
974.1.80 by Aaron Bentley
Improved merge error handling and testing
51
1558.4.11 by Aaron Bentley
Allow merge against self, make fetching self a noop
52
    def test_undo(self):
53
        wt = self.make_branch_and_tree('.')
54
        wt.commit("lala!")
55
        wt.commit("haha!")
56
        wt.commit("blabla!")
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
57
        wt.merge_from_branch(wt.branch, wt.branch.get_rev_id(2),
58
                             wt.branch.get_rev_id(1))
1558.4.11 by Aaron Bentley
Allow merge against self, make fetching self a noop
59
974.1.80 by Aaron Bentley
Improved merge error handling and testing
60
    def test_nocommits(self):
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
61
        wt = self.test_pending()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
62
        wt2 = self.make_branch_and_tree('branch2')
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
63
        self.assertRaises(NoCommits, wt.merge_from_branch, wt2.branch)
64
        return wt, wt2
974.1.80 by Aaron Bentley
Improved merge error handling and testing
65
66
    def test_unrelated(self):
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
67
        wt, wt2 = self.test_nocommits()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
68
        wt2.commit("blah")
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
69
        self.assertRaises(UnrelatedBranches, wt.merge_from_branch, wt2.branch)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
70
        return wt2
974.1.80 by Aaron Bentley
Improved merge error handling and testing
71
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
72
    def test_merge_one_file(self):
73
        """Do a partial merge of a tree which should not affect tree parents."""
1645.1.1 by Aaron Bentley
Implement single-file merge
74
        wt1 = self.make_branch_and_tree('branch1')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
75
        tip = wt1.commit('empty commit')
1645.1.1 by Aaron Bentley
Implement single-file merge
76
        wt2 = self.make_branch_and_tree('branch2')
77
        wt2.pull(wt1.branch)
78
        file('branch1/foo', 'wb').write('foo')
79
        file('branch1/bar', 'wb').write('bar')
80
        wt1.add('foo')
81
        wt1.add('bar')
82
        wt1.commit('add foobar')
83
        os.chdir('branch2')
2530.3.1 by Martin Pool
Cleanup old variations on run_bzr in the test suite
84
        self.run_bzr('merge ../branch1/baz', retcode=3)
85
        self.run_bzr('merge ../branch1/foo')
1645.1.1 by Aaron Bentley
Implement single-file merge
86
        self.failUnlessExists('foo')
87
        self.failIfExists('bar')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
88
        wt2 = WorkingTree.open('.') # opens branch2
89
        self.assertEqual([tip], wt2.get_parent_ids())
1908.6.9 by Robert Collins
Fix status to not use pending_merges.
90
        
974.1.88 by Aaron Bentley
Set a pending_merge if the merge base is forced to revno 0
91
    def test_pending_with_null(self):
1551.8.25 by Aaron Bentley
Fix deprecated use of pending_merges
92
        """When base is forced to revno 0, parent_ids are set"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
93
        wt2 = self.test_unrelated()
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
94
        wt1 = WorkingTree.open('.')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
95
        br1 = wt1.branch
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
96
        br1.fetch(wt2.branch)
1390 by Robert Collins
pair programming worx... merge integration and weave
97
        # merge all of branch 2 into branch 1 even though they 
98
        # are not related.
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
99
        wt1.merge_from_branch(wt2.branch, wt2.last_revision(), 'null:')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
100
        self.assertEqual([br1.last_revision(), wt2.branch.last_revision()],
101
            wt1.get_parent_ids())
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
102
        return (wt1, wt2.branch)
974.1.89 by Aaron Bentley
Fixed merging with multiple roots, by using null as graph root.
103
104
    def test_two_roots(self):
105
        """Merge base is sane when two unrelated branches are merged"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
106
        wt1, br2 = self.test_pending_with_null()
107
        wt1.commit("blah")
108
        last = wt1.branch.last_revision()
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
109
        self.assertEqual(common_ancestor(last, last, wt1.branch.repository), last)
1185.46.1 by Aaron Bentley
Test case when file to be renamed is also deleted
110
111
    def test_create_rename(self):
112
        """Rename an inventory entry while creating the file"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
113
        tree =self.make_branch_and_tree('.')
1185.46.1 by Aaron Bentley
Test case when file to be renamed is also deleted
114
        file('name1', 'wb').write('Hello')
115
        tree.add('name1')
116
        tree.commit(message="hello")
117
        tree.rename_one('name1', 'name2')
118
        os.unlink('name2')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
119
        transform_tree(tree, tree.branch.basis_tree())
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
120
121
    def test_layered_rename(self):
122
        """Rename both child and parent at same time"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
123
        tree =self.make_branch_and_tree('.')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
124
        os.mkdir('dirname1')
125
        tree.add('dirname1')
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
126
        filename = pathjoin('dirname1', 'name1')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
127
        file(filename, 'wb').write('Hello')
128
        tree.add(filename)
129
        tree.commit(message="hello")
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
130
        filename2 = pathjoin('dirname1', 'name2')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
131
        tree.rename_one(filename, filename2)
132
        tree.rename_one('dirname1', 'dirname2')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
133
        transform_tree(tree, tree.branch.basis_tree())
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
134
135
    def test_ignore_zero_merge_inner(self):
1907.4.1 by Matthieu Moy
Fixed merge to work nicely with -r revno:N:path
136
        # Test that merge_inner's ignore zero parameter is effective
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
137
        tree_a =self.make_branch_and_tree('a')
138
        tree_a.commit(message="hello")
139
        dir_b = tree_a.bzrdir.sprout('b')
140
        tree_b = dir_b.open_workingtree()
141
        tree_a.commit(message="hello again")
142
        log = StringIO()
143
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(), 
144
                    this_tree=tree_b, ignore_zero=True)
1927.3.1 by Carl Friedrich Bolz
Throw away on-disk logfile when possible.
145
        log = self._get_log(keep_log_file=True)
1551.4.1 by Aaron Bentley
Workaround for silly _get_log behaviour in test
146
        self.failUnless('All changes applied successfully.\n' not in log)
2796.1.3 by Aaron Bentley
update new test case
147
        tree_b.revert()
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
148
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(), 
149
                    this_tree=tree_b, ignore_zero=False)
1927.3.1 by Carl Friedrich Bolz
Throw away on-disk logfile when possible.
150
        log = self._get_log(keep_log_file=True)
1551.4.1 by Aaron Bentley
Workaround for silly _get_log behaviour in test
151
        self.failUnless('All changes applied successfully.\n' in log)
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
152
153
    def test_merge_inner_conflicts(self):
154
        tree_a = self.make_branch_and_tree('a')
155
        tree_a.set_conflicts(ConflictList([TextConflict('patha')]))
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
156
        merge_inner(tree_a.branch, tree_a, tree_a, this_tree=tree_a)
1551.7.13 by Aaron Bentley
Switched from actual, expected to expected, actual, for John.
157
        self.assertEqual(1, len(tree_a.conflicts()))
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
158
159
    def test_rmdir_conflict(self):
160
        tree_a = self.make_branch_and_tree('a')
161
        self.build_tree(['a/b/'])
162
        tree_a.add('b', 'b-id')
163
        tree_a.commit('added b')
2255.7.12 by John Arbash Meinel
Some comments on merge code, fix merge tests that
164
        # basis_tree() is only guaranteed to be valid as long as it is actually
165
        # the basis tree. This mutates the tree after grabbing basis, so go to
166
        # the repository.
167
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
168
        tree_z = tree_a.bzrdir.sprout('z').open_workingtree()
169
        self.build_tree(['a/b/c'])
170
        tree_a.add('b/c')
171
        tree_a.commit('added c')
172
        os.rmdir('z/b')
173
        tree_z.commit('removed b')
174
        merge_inner(tree_z.branch, tree_a, base_tree, this_tree=tree_z)
175
        self.assertEqual([
176
            conflicts.MissingParent('Created directory', 'b', 'b-id'),
177
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
178
            tree_z.conflicts())
2255.2.216 by Robert Collins
simplify merge_nested tests.
179
        merge_inner(tree_a.branch, tree_z.basis_tree(), base_tree,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
180
                    this_tree=tree_a)
181
        self.assertEqual([
182
            conflicts.DeletingParent('Not deleting', 'b', 'b-id'),
183
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
184
            tree_a.conflicts())
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
185
2100.3.29 by Aaron Bentley
Get merge working initially
186
    def test_nested_merge(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
187
        tree = self.make_branch_and_tree('tree',
188
            format='dirstate-with-subtree')
2100.3.29 by Aaron Bentley
Get merge working initially
189
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
190
            format='dirstate-with-subtree')
2100.3.29 by Aaron Bentley
Get merge working initially
191
        sub_tree.set_root_id('sub-tree-root')
192
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
193
        sub_tree.add('file')
194
        sub_tree.commit('foo')
195
        tree.add_reference(sub_tree)
196
        tree.commit('set text to 1')
197
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2255.2.217 by Martin Pool
docs
198
        # modify the file in the subtree
2100.3.29 by Aaron Bentley
Get merge working initially
199
        self.build_tree_contents([('tree2/sub-tree/file', 'text2')])
2255.2.217 by Martin Pool
docs
200
        # and merge the changes from the diverged subtree into the containing
201
        # tree
2255.2.216 by Robert Collins
simplify merge_nested tests.
202
        tree2.commit('changed file text')
2100.3.29 by Aaron Bentley
Get merge working initially
203
        tree.merge_from_branch(tree2.branch)
204
        self.assertFileEqual('text2', 'tree/sub-tree/file')
205
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
206
    def test_merge_with_missing(self):
207
        tree_a = self.make_branch_and_tree('tree_a')
208
        self.build_tree_contents([('tree_a/file', 'content_1')])
209
        tree_a.add('file')
210
        tree_a.commit('commit base')
2255.7.12 by John Arbash Meinel
Some comments on merge code, fix merge tests that
211
        # basis_tree() is only guaranteed to be valid as long as it is actually
212
        # the basis tree. This mutates the tree after grabbing basis, so go to
213
        # the repository.
214
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
215
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
216
        self.build_tree_contents([('tree_a/file', 'content_2')])
217
        tree_a.commit('commit other')
218
        other_tree = tree_a.basis_tree()
219
        os.unlink('tree_b/file')
220
        merge_inner(tree_b.branch, other_tree, base_tree, this_tree=tree_b)
1959.4.6 by Aaron Bentley
Ensure merge works across kind changes
221
222
    def test_merge_kind_change(self):
223
        tree_a = self.make_branch_and_tree('tree_a')
224
        self.build_tree_contents([('tree_a/file', 'content_1')])
225
        tree_a.add('file', 'file-id')
226
        tree_a.commit('added file')
227
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
228
        os.unlink('tree_a/file')
229
        self.build_tree(['tree_a/file/'])
230
        tree_a.commit('changed file to directory')
231
        tree_b.merge_from_branch(tree_a.branch)
232
        self.assertEqual('directory', file_kind('tree_b/file'))
2748.3.2 by Aaron Bentley
Fix revert, remove-tree, and various tests to use None for 'no files specified'
233
        tree_b.revert()
1959.4.6 by Aaron Bentley
Ensure merge works across kind changes
234
        self.assertEqual('file', file_kind('tree_b/file'))
235
        self.build_tree_contents([('tree_b/file', 'content_2')])
236
        tree_b.commit('content change')
237
        tree_b.merge_from_branch(tree_a.branch)
238
        self.assertEqual(tree_b.conflicts(),
239
                         [conflicts.ContentsConflict('file',
240
                          file_id='file-id')])
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
241
    
242
    def test_merge_type_registry(self):
243
        merge_type_option = option.Option.OPTIONS['merge-type']
244
        self.assertFalse('merge4' in [x[0] for x in 
245
                        merge_type_option.iter_switches()])
246
        registry = _mod_merge.get_merge_type_registry()
247
        registry.register_lazy('merge4', 'bzrlib.merge', 'Merge4Merger',
248
                               'time-travelling merge')
249
        self.assertTrue('merge4' in [x[0] for x in 
250
                        merge_type_option.iter_switches()])
251
        registry.remove('merge4')
252
        self.assertFalse('merge4' in [x[0] for x in 
253
                        merge_type_option.iter_switches()])
1551.16.2 by Aaron Bentley
Don't crash on merging renamed deleted files (#110279)
254
1551.16.3 by Aaron Bentley
Rename test case
255
    def test_merge_other_moves_we_deleted(self):
1551.16.2 by Aaron Bentley
Don't crash on merging renamed deleted files (#110279)
256
        tree_a = self.make_branch_and_tree('A')
257
        tree_a.lock_write()
258
        self.addCleanup(tree_a.unlock)
259
        self.build_tree(['A/a'])
260
        tree_a.add('a')
261
        tree_a.commit('1', rev_id='rev-1')
262
        tree_a.flush()
263
        tree_a.rename_one('a', 'b')
264
        tree_a.commit('2')
265
        bzrdir_b = tree_a.bzrdir.sprout('B', revision_id='rev-1')
266
        tree_b = bzrdir_b.open_workingtree()
267
        tree_b.lock_write()
268
        self.addCleanup(tree_b.unlock)
269
        os.unlink('B/a')
270
        tree_b.commit('3')
271
        try:
272
            tree_b.merge_from_branch(tree_a.branch)
273
        except AttributeError:
274
            self.fail('tried to join a path when name was None')
2644.1.1 by Wouter van Heyst
Fix bug #127115 by checking for self.other_rev_id being None in Merger.set_pending()
275
2644.1.2 by Wouter van Heyst
As Aaron explained #127115 is more general, failing whenever other's basis is an ancestor of this' basis.
276
    def test_merge_uncommitted_otherbasis_ancestor_of_thisbasis(self):
2644.1.1 by Wouter van Heyst
Fix bug #127115 by checking for self.other_rev_id being None in Merger.set_pending()
277
        tree_a = self.make_branch_and_tree('a')
2644.1.2 by Wouter van Heyst
As Aaron explained #127115 is more general, failing whenever other's basis is an ancestor of this' basis.
278
        self.build_tree(['a/file_1', 'a/file_2'])
2644.1.1 by Wouter van Heyst
Fix bug #127115 by checking for self.other_rev_id being None in Merger.set_pending()
279
        tree_a.add(['file_1'])
280
        tree_a.commit('commit 1')
2644.1.2 by Wouter van Heyst
As Aaron explained #127115 is more general, failing whenever other's basis is an ancestor of this' basis.
281
        tree_a.add(['file_2'])
282
        tree_a.commit('commit 2')
2644.1.1 by Wouter van Heyst
Fix bug #127115 by checking for self.other_rev_id being None in Merger.set_pending()
283
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
2644.1.2 by Wouter van Heyst
As Aaron explained #127115 is more general, failing whenever other's basis is an ancestor of this' basis.
284
        tree_b.rename_one('file_1', 'renamed')
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
285
        merger = _mod_merge.Merger.from_uncommitted(tree_a, tree_b,
286
                                                    progress.DummyProgress())
287
        merger.merge_type = _mod_merge.Merge3Merger
288
        merger.do_merge()
2644.1.2 by Wouter van Heyst
As Aaron explained #127115 is more general, failing whenever other's basis is an ancestor of this' basis.
289
        self.assertEqual(tree_a.get_parent_ids(), [tree_b.last_revision()])
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
290
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
291
    def prepare_cherrypick(self):
3062.2.13 by Aaron Bentley
Update prepare_cherrypick docstring
292
        """Prepare a pair of trees for cherrypicking tests.
293
294
        Both trees have a file, 'file'.
295
        rev1 sets content to 'a'.
296
        rev2b adds 'b'.
297
        rev3b adds 'c'.
298
        A full merge of rev2b and rev3b into this_tree would add both 'b' and
299
        'c'.  A successful cherrypick of rev2b-rev3b into this_tree will add
300
        'c', but not 'b'.
301
        """
3062.2.6 by Aaron Bentley
Get cherrypick-on-weave working
302
        this_tree = self.make_branch_and_tree('this')
303
        self.build_tree_contents([('this/file', "a\n")])
304
        this_tree.add('file')
305
        this_tree.commit('rev1')
306
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
307
        self.build_tree_contents([('other/file', "a\nb\n")])
308
        other_tree.commit('rev2b', rev_id='rev2b')
309
        self.build_tree_contents([('other/file', "c\na\nb\n")])
310
        other_tree.commit('rev3b', rev_id='rev3b')
311
        this_tree.lock_write()
312
        self.addCleanup(this_tree.unlock)
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
313
        return this_tree, other_tree
314
315
    def test_weave_cherrypick(self):
316
        this_tree, other_tree = self.prepare_cherrypick()
3062.2.6 by Aaron Bentley
Get cherrypick-on-weave working
317
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
318
            this_tree, 'rev3b', 'rev2b', other_tree.branch)
319
        merger.merge_type = _mod_merge.WeaveMerger
320
        merger.do_merge()
321
        self.assertFileEqual('c\na\n', 'this/file')
322
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
323
    def test_weave_cannot_reverse_cherrypick(self):
324
        this_tree, other_tree = self.prepare_cherrypick()
325
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
326
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
327
        merger.merge_type = _mod_merge.WeaveMerger
328
        self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
329
330
    def test_merge3_can_reverse_cherrypick(self):
331
        this_tree, other_tree = self.prepare_cherrypick()
332
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
333
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
334
        merger.merge_type = _mod_merge.Merge3Merger
335
        merger.do_merge()
336
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
337
338
class TestPlanMerge(TestCaseWithMemoryTransport):
339
340
    def setUp(self):
341
        TestCaseWithMemoryTransport.setUp(self)
342
        self.vf = knit.KnitVersionedFile('root', self.get_transport(),
343
                                         create=True)
344
        self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root',
345
                                                                   [self.vf])
346
347
    def add_version(self, version_id, parents, text):
348
        self.vf.add_lines(version_id, parents, [c+'\n' for c in text])
349
350
    def add_uncommitted_version(self, version_id, parents, text):
351
        self.plan_merge_vf.add_lines(version_id, parents,
352
                                     [c+'\n' for c in text])
353
354
    def setup_plan_merge(self):
355
        self.add_version('A', [], 'abc')
356
        self.add_version('B', ['A'], 'acehg')
357
        self.add_version('C', ['A'], 'fabg')
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
358
        return _PlanMerge('B', 'C', self.plan_merge_vf)
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
359
360
    def setup_plan_merge_uncommitted(self):
361
        self.add_version('A', [], 'abc')
362
        self.add_uncommitted_version('B:', ['A'], 'acehg')
363
        self.add_uncommitted_version('C:', ['A'], 'fabg')
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
364
        return _PlanMerge('B:', 'C:', self.plan_merge_vf)
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
365
366
    def test_unique_lines(self):
367
        plan = self.setup_plan_merge()
368
        self.assertEqual(plan._unique_lines(
369
            plan._get_matching_blocks('B', 'C')),
370
            ([1, 2, 3], [0, 2]))
371
372
    def test_find_new(self):
373
        plan = self.setup_plan_merge()
374
        self.assertEqual(set([2, 3, 4]), plan._find_new('B'))
375
        self.assertEqual(set([0, 3]), plan._find_new('C'))
376
377
    def test_find_new2(self):
378
        self.add_version('A', [], 'abc')
379
        self.add_version('B', ['A'], 'abcde')
380
        self.add_version('C', ['A'], 'abcefg')
381
        self.add_version('D', ['A', 'B', 'C'], 'abcdegh')
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
382
        my_plan = _PlanMerge('B', 'D', self.plan_merge_vf)
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
383
        self.assertEqual(set([5, 6]), my_plan._find_new('D'))
384
        self.assertEqual(set(), my_plan._find_new('A'))
385
386
    def test_find_new_no_ancestors(self):
387
        self.add_version('A', [], 'abc')
388
        self.add_version('B', [], 'xyz')
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
389
        my_plan = _PlanMerge('A', 'B', self.vf)
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
390
        self.assertEqual(set([0, 1, 2]), my_plan._find_new('A'))
391
392
    def test_plan_merge(self):
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
393
        self.setup_plan_merge()
394
        plan = self.plan_merge_vf.plan_merge('B', 'C')
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
395
        self.assertEqual([
396
                          ('new-b', 'f\n'),
397
                          ('unchanged', 'a\n'),
398
                          ('killed-b', 'c\n'),
399
                          ('new-a', 'e\n'),
400
                          ('new-a', 'h\n'),
401
                          ('killed-a', 'b\n'),
402
                          ('unchanged', 'g\n')],
403
                         list(plan))
404
405
    def test_plan_merge_uncommitted_files(self):
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
406
        self.setup_plan_merge_uncommitted()
407
        plan = self.plan_merge_vf.plan_merge('B:', 'C:')
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
408
        self.assertEqual([
409
                          ('new-b', 'f\n'),
410
                          ('unchanged', 'a\n'),
411
                          ('killed-b', 'c\n'),
412
                          ('new-a', 'e\n'),
413
                          ('new-a', 'h\n'),
414
                          ('killed-a', 'b\n'),
415
                          ('unchanged', 'g\n')],
416
                         list(plan))
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
417
418
    def test_subtract_plans(self):
419
        old_plan = [
420
        ('unchanged', 'a\n'),
421
        ('new-a', 'b\n'),
422
        ('killed-a', 'c\n'),
423
        ('new-b', 'd\n'),
424
        ('new-b', 'e\n'),
425
        ('killed-b', 'f\n'),
426
        ('killed-b', 'g\n'),
427
        ]
428
        new_plan = [
429
        ('unchanged', 'a\n'),
430
        ('new-a', 'b\n'),
431
        ('killed-a', 'c\n'),
432
        ('new-b', 'd\n'),
433
        ('new-b', 'h\n'),
434
        ('killed-b', 'f\n'),
435
        ('killed-b', 'i\n'),
436
        ]
437
        subtracted_plan = [
438
        ('unchanged', 'a\n'),
439
        ('new-a', 'b\n'),
440
        ('killed-a', 'c\n'),
441
        ('new-b', 'h\n'),
442
        ('unchanged', 'f\n'),
443
        ('killed-b', 'i\n'),
444
        ]
445
        self.assertEqual(subtracted_plan,
3062.2.3 by Aaron Bentley
Sync up with bzr.dev API changes
446
            list(_PlanMerge._subtract_plans(old_plan, new_plan)))
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
447
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
448
    def setup_merge_with_base(self):
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
449
        self.add_version('COMMON', [], 'abc')
450
        self.add_version('THIS', ['COMMON'], 'abcd')
451
        self.add_version('BASE', ['COMMON'], 'eabc')
452
        self.add_version('OTHER', ['BASE'], 'eafb')
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
453
454
    def test_plan_merge_with_base(self):
455
        self.setup_merge_with_base()
3062.2.3 by Aaron Bentley
Sync up with bzr.dev API changes
456
        plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
457
        self.assertEqual([('unchanged', 'a\n'),
458
                          ('new-b', 'f\n'),
459
                          ('unchanged', 'b\n'),
460
                          ('killed-b', 'c\n'),
461
                          ('new-a', 'd\n')
462
                         ], list(plan))
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
463
464
    def test_plan_lca_merge(self):
465
        self.setup_plan_merge()
466
        plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
467
        self.assertEqual([
468
                          ('new-b', 'f\n'),
469
                          ('unchanged', 'a\n'),
470
                          ('killed-b', 'c\n'),
471
                          ('new-a', 'e\n'),
472
                          ('new-a', 'h\n'),
473
                          ('killed-a', 'b\n'),
474
                          ('unchanged', 'g\n')],
475
                         list(plan))
476
477
    def test_plan_lca_merge_uncommitted_files(self):
478
        self.setup_plan_merge_uncommitted()
479
        plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
480
        self.assertEqual([
481
                          ('new-b', 'f\n'),
482
                          ('unchanged', 'a\n'),
483
                          ('killed-b', 'c\n'),
484
                          ('new-a', 'e\n'),
485
                          ('new-a', 'h\n'),
486
                          ('killed-a', 'b\n'),
487
                          ('unchanged', 'g\n')],
488
                         list(plan))
489
490
    def test_plan_lca_merge_with_base(self):
491
        self.setup_merge_with_base()
492
        plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
493
        self.assertEqual([('unchanged', 'a\n'),
494
                          ('new-b', 'f\n'),
495
                          ('unchanged', 'b\n'),
496
                          ('killed-b', 'c\n'),
497
                          ('new-a', 'd\n')
498
                         ], list(plan))
499
500
    def test_plan_lca_merge_with_criss_cross(self):
501
        self.add_version('ROOT', [], 'abc')
502
        # each side makes a change
503
        self.add_version('REV1', ['ROOT'], 'abcd')
504
        self.add_version('REV2', ['ROOT'], 'abce')
505
        # both sides merge, discarding others' changes
506
        self.add_version('LCA1', ['REV1', 'REV2'], 'abcd')
507
        self.add_version('LCA2', ['REV1', 'REV2'], 'abce')
508
        plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
509
        self.assertEqual([('unchanged', 'a\n'),
510
                          ('unchanged', 'b\n'),
511
                          ('unchanged', 'c\n'),
3144.3.3 by Aaron Bentley
Update test for new conflicted types
512
                          ('conflicted-a', 'd\n'),
513
                          ('conflicted-b', 'e\n'),
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
514
                         ], list(plan))