~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,
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
24
    memorytree,
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
25
    merge as _mod_merge,
26
    option,
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
27
    progress,
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
28
    tests,
3008.1.21 by Aaron Bentley
Make compute_transform private, test make_preview_transform
29
    transform,
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
30
    versionedfile,
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
31
    )
974.1.56 by aaron.bentley at utoronto
Added merge test
32
from bzrlib.branch import Branch
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
33
from bzrlib.conflicts import ConflictList, TextConflict
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
34
from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
35
from bzrlib.merge import transform_tree, merge_inner, _PlanMerge
1959.4.6 by Aaron Bentley
Ensure merge works across kind changes
36
from bzrlib.osutils import pathjoin, file_kind
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
37
from bzrlib.tests import TestCaseWithTransport, TestCaseWithMemoryTransport
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
38
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.
39
from bzrlib.workingtree import WorkingTree
40
41
42
class TestMerge(TestCaseWithTransport):
974.1.56 by aaron.bentley at utoronto
Added merge test
43
    """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.
44
974.1.56 by aaron.bentley at utoronto
Added merge test
45
    def test_pending(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
46
        wt = self.make_branch_and_tree('.')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
47
        rev_a = wt.commit("lala!")
48
        self.assertEqual([rev_a], wt.get_parent_ids())
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
49
        self.assertRaises(errors.PointlessMerge, wt.merge_from_branch,
50
                          wt.branch)
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
51
        self.assertEqual([rev_a], wt.get_parent_ids())
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
52
        return wt
974.1.80 by Aaron Bentley
Improved merge error handling and testing
53
1558.4.11 by Aaron Bentley
Allow merge against self, make fetching self a noop
54
    def test_undo(self):
55
        wt = self.make_branch_and_tree('.')
56
        wt.commit("lala!")
57
        wt.commit("haha!")
58
        wt.commit("blabla!")
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
59
        wt.merge_from_branch(wt.branch, wt.branch.get_rev_id(2),
60
                             wt.branch.get_rev_id(1))
1558.4.11 by Aaron Bentley
Allow merge against self, make fetching self a noop
61
974.1.80 by Aaron Bentley
Improved merge error handling and testing
62
    def test_nocommits(self):
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
63
        wt = self.test_pending()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
64
        wt2 = self.make_branch_and_tree('branch2')
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
65
        self.assertRaises(NoCommits, wt.merge_from_branch, wt2.branch)
66
        return wt, wt2
974.1.80 by Aaron Bentley
Improved merge error handling and testing
67
68
    def test_unrelated(self):
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
69
        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.
70
        wt2.commit("blah")
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
71
        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.
72
        return wt2
974.1.80 by Aaron Bentley
Improved merge error handling and testing
73
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
74
    def test_merge_one_file(self):
75
        """Do a partial merge of a tree which should not affect tree parents."""
1645.1.1 by Aaron Bentley
Implement single-file merge
76
        wt1 = self.make_branch_and_tree('branch1')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
77
        tip = wt1.commit('empty commit')
1645.1.1 by Aaron Bentley
Implement single-file merge
78
        wt2 = self.make_branch_and_tree('branch2')
79
        wt2.pull(wt1.branch)
80
        file('branch1/foo', 'wb').write('foo')
81
        file('branch1/bar', 'wb').write('bar')
82
        wt1.add('foo')
83
        wt1.add('bar')
84
        wt1.commit('add foobar')
85
        os.chdir('branch2')
2530.3.1 by Martin Pool
Cleanup old variations on run_bzr in the test suite
86
        self.run_bzr('merge ../branch1/baz', retcode=3)
87
        self.run_bzr('merge ../branch1/foo')
1645.1.1 by Aaron Bentley
Implement single-file merge
88
        self.failUnlessExists('foo')
89
        self.failIfExists('bar')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
90
        wt2 = WorkingTree.open('.') # opens branch2
91
        self.assertEqual([tip], wt2.get_parent_ids())
1908.6.9 by Robert Collins
Fix status to not use pending_merges.
92
        
974.1.88 by Aaron Bentley
Set a pending_merge if the merge base is forced to revno 0
93
    def test_pending_with_null(self):
1551.8.25 by Aaron Bentley
Fix deprecated use of pending_merges
94
        """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.
95
        wt2 = self.test_unrelated()
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
96
        wt1 = WorkingTree.open('.')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
97
        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.
98
        br1.fetch(wt2.branch)
1390 by Robert Collins
pair programming worx... merge integration and weave
99
        # merge all of branch 2 into branch 1 even though they 
100
        # are not related.
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
101
        wt1.merge_from_branch(wt2.branch, wt2.last_revision(), 'null:')
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
102
        self.assertEqual([br1.last_revision(), wt2.branch.last_revision()],
103
            wt1.get_parent_ids())
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
104
        return (wt1, wt2.branch)
974.1.89 by Aaron Bentley
Fixed merging with multiple roots, by using null as graph root.
105
106
    def test_two_roots(self):
107
        """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.
108
        wt1, br2 = self.test_pending_with_null()
109
        wt1.commit("blah")
3228.4.15 by John Arbash Meinel
Stop using common_ancestor
110
        wt1.lock_read()
111
        try:
112
            last = wt1.branch.last_revision()
113
            last2 = br2.last_revision()
114
            graph = wt1.branch.repository.get_graph()
115
            self.assertEqual(last2, graph.find_unique_lca(last, last2))
116
        finally:
117
            wt1.unlock()
1185.46.1 by Aaron Bentley
Test case when file to be renamed is also deleted
118
119
    def test_create_rename(self):
120
        """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.
121
        tree =self.make_branch_and_tree('.')
1185.46.1 by Aaron Bentley
Test case when file to be renamed is also deleted
122
        file('name1', 'wb').write('Hello')
123
        tree.add('name1')
124
        tree.commit(message="hello")
125
        tree.rename_one('name1', 'name2')
126
        os.unlink('name2')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
127
        transform_tree(tree, tree.branch.basis_tree())
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
128
129
    def test_layered_rename(self):
130
        """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.
131
        tree =self.make_branch_and_tree('.')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
132
        os.mkdir('dirname1')
133
        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 \
134
        filename = pathjoin('dirname1', 'name1')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
135
        file(filename, 'wb').write('Hello')
136
        tree.add(filename)
137
        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 \
138
        filename2 = pathjoin('dirname1', 'name2')
1185.46.2 by Aaron Bentley
Added test for renaming both parent and child
139
        tree.rename_one(filename, filename2)
140
        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.
141
        transform_tree(tree, tree.branch.basis_tree())
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
142
143
    def test_ignore_zero_merge_inner(self):
1907.4.1 by Matthieu Moy
Fixed merge to work nicely with -r revno:N:path
144
        # Test that merge_inner's ignore zero parameter is effective
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
145
        tree_a =self.make_branch_and_tree('a')
146
        tree_a.commit(message="hello")
147
        dir_b = tree_a.bzrdir.sprout('b')
148
        tree_b = dir_b.open_workingtree()
3146.4.4 by Aaron Bentley
Add write lock, so merge_inner works properly
149
        tree_b.lock_write()
150
        self.addCleanup(tree_b.unlock)
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
151
        tree_a.commit(message="hello again")
152
        log = StringIO()
153
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(), 
154
                    this_tree=tree_b, ignore_zero=True)
1927.3.1 by Carl Friedrich Bolz
Throw away on-disk logfile when possible.
155
        log = self._get_log(keep_log_file=True)
1551.4.1 by Aaron Bentley
Workaround for silly _get_log behaviour in test
156
        self.failUnless('All changes applied successfully.\n' not in log)
2796.1.3 by Aaron Bentley
update new test case
157
        tree_b.revert()
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
158
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(), 
159
                    this_tree=tree_b, ignore_zero=False)
1927.3.1 by Carl Friedrich Bolz
Throw away on-disk logfile when possible.
160
        log = self._get_log(keep_log_file=True)
1551.4.1 by Aaron Bentley
Workaround for silly _get_log behaviour in test
161
        self.failUnless('All changes applied successfully.\n' in log)
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
162
163
    def test_merge_inner_conflicts(self):
164
        tree_a = self.make_branch_and_tree('a')
165
        tree_a.set_conflicts(ConflictList([TextConflict('patha')]))
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
166
        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.
167
        self.assertEqual(1, len(tree_a.conflicts()))
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
168
169
    def test_rmdir_conflict(self):
170
        tree_a = self.make_branch_and_tree('a')
171
        self.build_tree(['a/b/'])
172
        tree_a.add('b', 'b-id')
173
        tree_a.commit('added b')
2255.7.12 by John Arbash Meinel
Some comments on merge code, fix merge tests that
174
        # basis_tree() is only guaranteed to be valid as long as it is actually
175
        # the basis tree. This mutates the tree after grabbing basis, so go to
176
        # the repository.
177
        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
178
        tree_z = tree_a.bzrdir.sprout('z').open_workingtree()
179
        self.build_tree(['a/b/c'])
180
        tree_a.add('b/c')
181
        tree_a.commit('added c')
182
        os.rmdir('z/b')
183
        tree_z.commit('removed b')
184
        merge_inner(tree_z.branch, tree_a, base_tree, this_tree=tree_z)
185
        self.assertEqual([
186
            conflicts.MissingParent('Created directory', 'b', 'b-id'),
187
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
188
            tree_z.conflicts())
2255.2.216 by Robert Collins
simplify merge_nested tests.
189
        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
190
                    this_tree=tree_a)
191
        self.assertEqual([
192
            conflicts.DeletingParent('Not deleting', 'b', 'b-id'),
193
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
194
            tree_a.conflicts())
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
195
2100.3.29 by Aaron Bentley
Get merge working initially
196
    def test_nested_merge(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
197
        tree = self.make_branch_and_tree('tree',
198
            format='dirstate-with-subtree')
2100.3.29 by Aaron Bentley
Get merge working initially
199
        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.
200
            format='dirstate-with-subtree')
2100.3.29 by Aaron Bentley
Get merge working initially
201
        sub_tree.set_root_id('sub-tree-root')
202
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
203
        sub_tree.add('file')
204
        sub_tree.commit('foo')
205
        tree.add_reference(sub_tree)
206
        tree.commit('set text to 1')
207
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2255.2.217 by Martin Pool
docs
208
        # modify the file in the subtree
2100.3.29 by Aaron Bentley
Get merge working initially
209
        self.build_tree_contents([('tree2/sub-tree/file', 'text2')])
2255.2.217 by Martin Pool
docs
210
        # and merge the changes from the diverged subtree into the containing
211
        # tree
2255.2.216 by Robert Collins
simplify merge_nested tests.
212
        tree2.commit('changed file text')
2100.3.29 by Aaron Bentley
Get merge working initially
213
        tree.merge_from_branch(tree2.branch)
214
        self.assertFileEqual('text2', 'tree/sub-tree/file')
215
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
216
    def test_merge_with_missing(self):
217
        tree_a = self.make_branch_and_tree('tree_a')
218
        self.build_tree_contents([('tree_a/file', 'content_1')])
219
        tree_a.add('file')
220
        tree_a.commit('commit base')
2255.7.12 by John Arbash Meinel
Some comments on merge code, fix merge tests that
221
        # basis_tree() is only guaranteed to be valid as long as it is actually
222
        # the basis tree. This mutates the tree after grabbing basis, so go to
223
        # the repository.
224
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
1551.10.2 by Aaron Bentley
Handle merge with dangling inventory entries
225
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
226
        self.build_tree_contents([('tree_a/file', 'content_2')])
227
        tree_a.commit('commit other')
228
        other_tree = tree_a.basis_tree()
229
        os.unlink('tree_b/file')
230
        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
231
232
    def test_merge_kind_change(self):
233
        tree_a = self.make_branch_and_tree('tree_a')
234
        self.build_tree_contents([('tree_a/file', 'content_1')])
235
        tree_a.add('file', 'file-id')
236
        tree_a.commit('added file')
237
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
238
        os.unlink('tree_a/file')
239
        self.build_tree(['tree_a/file/'])
240
        tree_a.commit('changed file to directory')
241
        tree_b.merge_from_branch(tree_a.branch)
242
        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'
243
        tree_b.revert()
1959.4.6 by Aaron Bentley
Ensure merge works across kind changes
244
        self.assertEqual('file', file_kind('tree_b/file'))
245
        self.build_tree_contents([('tree_b/file', 'content_2')])
246
        tree_b.commit('content change')
247
        tree_b.merge_from_branch(tree_a.branch)
248
        self.assertEqual(tree_b.conflicts(),
249
                         [conflicts.ContentsConflict('file',
250
                          file_id='file-id')])
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
251
    
252
    def test_merge_type_registry(self):
253
        merge_type_option = option.Option.OPTIONS['merge-type']
254
        self.assertFalse('merge4' in [x[0] for x in 
255
                        merge_type_option.iter_switches()])
256
        registry = _mod_merge.get_merge_type_registry()
257
        registry.register_lazy('merge4', 'bzrlib.merge', 'Merge4Merger',
258
                               'time-travelling merge')
259
        self.assertTrue('merge4' in [x[0] for x in 
260
                        merge_type_option.iter_switches()])
261
        registry.remove('merge4')
262
        self.assertFalse('merge4' in [x[0] for x in 
263
                        merge_type_option.iter_switches()])
1551.16.2 by Aaron Bentley
Don't crash on merging renamed deleted files (#110279)
264
1551.16.3 by Aaron Bentley
Rename test case
265
    def test_merge_other_moves_we_deleted(self):
1551.16.2 by Aaron Bentley
Don't crash on merging renamed deleted files (#110279)
266
        tree_a = self.make_branch_and_tree('A')
267
        tree_a.lock_write()
268
        self.addCleanup(tree_a.unlock)
269
        self.build_tree(['A/a'])
270
        tree_a.add('a')
271
        tree_a.commit('1', rev_id='rev-1')
272
        tree_a.flush()
273
        tree_a.rename_one('a', 'b')
274
        tree_a.commit('2')
275
        bzrdir_b = tree_a.bzrdir.sprout('B', revision_id='rev-1')
276
        tree_b = bzrdir_b.open_workingtree()
277
        tree_b.lock_write()
278
        self.addCleanup(tree_b.unlock)
279
        os.unlink('B/a')
280
        tree_b.commit('3')
281
        try:
282
            tree_b.merge_from_branch(tree_a.branch)
283
        except AttributeError:
284
            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()
285
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.
286
    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()
287
        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.
288
        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()
289
        tree_a.add(['file_1'])
290
        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.
291
        tree_a.add(['file_2'])
292
        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()
293
        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.
294
        tree_b.rename_one('file_1', 'renamed')
1551.15.72 by Aaron Bentley
remove builtins._merge_helper
295
        merger = _mod_merge.Merger.from_uncommitted(tree_a, tree_b,
296
                                                    progress.DummyProgress())
297
        merger.merge_type = _mod_merge.Merge3Merger
298
        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.
299
        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
300
3146.5.1 by Aaron Bentley
Make merge --uncommitted work with merge-type weave
301
    def test_merge_uncommitted_otherbasis_ancestor_of_thisbasis_weave(self):
302
        tree_a = self.make_branch_and_tree('a')
303
        self.build_tree(['a/file_1', 'a/file_2'])
304
        tree_a.add(['file_1'])
305
        tree_a.commit('commit 1')
306
        tree_a.add(['file_2'])
307
        tree_a.commit('commit 2')
308
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
309
        tree_b.rename_one('file_1', 'renamed')
310
        merger = _mod_merge.Merger.from_uncommitted(tree_a, tree_b,
311
                                                    progress.DummyProgress())
312
        merger.merge_type = _mod_merge.WeaveMerger
313
        merger.do_merge()
314
        self.assertEqual(tree_a.get_parent_ids(), [tree_b.last_revision()])
315
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
316
    def prepare_cherrypick(self):
3062.2.13 by Aaron Bentley
Update prepare_cherrypick docstring
317
        """Prepare a pair of trees for cherrypicking tests.
318
319
        Both trees have a file, 'file'.
320
        rev1 sets content to 'a'.
321
        rev2b adds 'b'.
322
        rev3b adds 'c'.
323
        A full merge of rev2b and rev3b into this_tree would add both 'b' and
324
        'c'.  A successful cherrypick of rev2b-rev3b into this_tree will add
325
        'c', but not 'b'.
326
        """
3062.2.6 by Aaron Bentley
Get cherrypick-on-weave working
327
        this_tree = self.make_branch_and_tree('this')
328
        self.build_tree_contents([('this/file', "a\n")])
329
        this_tree.add('file')
330
        this_tree.commit('rev1')
331
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
332
        self.build_tree_contents([('other/file', "a\nb\n")])
333
        other_tree.commit('rev2b', rev_id='rev2b')
334
        self.build_tree_contents([('other/file', "c\na\nb\n")])
335
        other_tree.commit('rev3b', rev_id='rev3b')
336
        this_tree.lock_write()
337
        self.addCleanup(this_tree.unlock)
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
338
        return this_tree, other_tree
339
340
    def test_weave_cherrypick(self):
341
        this_tree, other_tree = self.prepare_cherrypick()
3062.2.6 by Aaron Bentley
Get cherrypick-on-weave working
342
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
343
            this_tree, 'rev3b', 'rev2b', other_tree.branch)
344
        merger.merge_type = _mod_merge.WeaveMerger
345
        merger.do_merge()
346
        self.assertFileEqual('c\na\n', 'this/file')
347
3062.2.7 by Aaron Bentley
Prevent reverse cherry-picking with weave
348
    def test_weave_cannot_reverse_cherrypick(self):
349
        this_tree, other_tree = self.prepare_cherrypick()
350
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
351
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
352
        merger.merge_type = _mod_merge.WeaveMerger
353
        self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
354
355
    def test_merge3_can_reverse_cherrypick(self):
356
        this_tree, other_tree = self.prepare_cherrypick()
357
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
358
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
359
        merger.merge_type = _mod_merge.Merge3Merger
360
        merger.do_merge()
361
3249.3.1 by John Arbash Meinel
Implement cherrypick support for Merge3
362
    def test_merge3_will_detect_cherrypick(self):
363
        this_tree = self.make_branch_and_tree('this')
364
        self.build_tree_contents([('this/file', "a\n")])
365
        this_tree.add('file')
366
        this_tree.commit('rev1')
367
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
368
        self.build_tree_contents([('other/file', "a\nb\n")])
369
        other_tree.commit('rev2b', rev_id='rev2b')
370
        self.build_tree_contents([('other/file', "a\nb\nc\n")])
371
        other_tree.commit('rev3b', rev_id='rev3b')
372
        this_tree.lock_write()
373
        self.addCleanup(this_tree.unlock)
374
375
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
376
            this_tree, 'rev3b', 'rev2b', other_tree.branch)
377
        merger.merge_type = _mod_merge.Merge3Merger
378
        merger.do_merge()
379
        self.assertFileEqual('a\n'
380
                             '<<<<<<< TREE\n'
381
                             '=======\n'
382
                             'c\n'
383
                             '>>>>>>> MERGE-SOURCE\n',
384
                             'this/file')
385
3008.1.21 by Aaron Bentley
Make compute_transform private, test make_preview_transform
386
    def test_make_merger(self):
387
        this_tree = self.make_branch_and_tree('this')
388
        this_tree.commit('rev1', rev_id='rev1')
389
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
390
        this_tree.commit('rev2', rev_id='rev2a')
391
        other_tree.commit('rev2', rev_id='rev2b')
392
        this_tree.lock_write()
393
        self.addCleanup(this_tree.unlock)
394
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress,
395
            this_tree, 'rev2b', other_branch=other_tree.branch)
396
        merger.merge_type = _mod_merge.Merge3Merger
397
        tree_merger = merger.make_merger()
398
        self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
399
        self.assertEqual('rev2b', tree_merger.other_tree.get_revision_id())
400
        self.assertEqual('rev1', tree_merger.base_tree.get_revision_id())
401
402
    def test_make_preview_transform(self):
403
        this_tree = self.make_branch_and_tree('this')
404
        self.build_tree_contents([('this/file', '1\n')])
405
        this_tree.add('file', 'file-id')
406
        this_tree.commit('rev1', rev_id='rev1')
407
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
408
        self.build_tree_contents([('this/file', '1\n2a\n')])
409
        this_tree.commit('rev2', rev_id='rev2a')
410
        self.build_tree_contents([('other/file', '2b\n1\n')])
411
        other_tree.commit('rev2', rev_id='rev2b')
412
        this_tree.lock_write()
413
        self.addCleanup(this_tree.unlock)
414
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
415
            this_tree, 'rev2b', other_branch=other_tree.branch)
416
        merger.merge_type = _mod_merge.Merge3Merger
417
        tree_merger = merger.make_merger()
418
        tt = tree_merger.make_preview_transform()
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
419
        self.addCleanup(tt.finalize)
3008.1.21 by Aaron Bentley
Make compute_transform private, test make_preview_transform
420
        preview_tree = tt.get_preview_tree()
421
        tree_file = this_tree.get_file('file-id')
422
        try:
423
            self.assertEqual('1\n2a\n', tree_file.read())
424
        finally:
425
            tree_file.close()
426
        preview_file = preview_tree.get_file('file-id')
427
        try:
428
            self.assertEqual('2b\n1\n2a\n', preview_file.read())
429
        finally:
430
            preview_file.close()
431
3008.1.22 by Aaron Bentley
Get do_merge under test
432
    def test_do_merge(self):
433
        this_tree = self.make_branch_and_tree('this')
434
        self.build_tree_contents([('this/file', '1\n')])
435
        this_tree.add('file', 'file-id')
436
        this_tree.commit('rev1', rev_id='rev1')
437
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
438
        self.build_tree_contents([('this/file', '1\n2a\n')])
439
        this_tree.commit('rev2', rev_id='rev2a')
440
        self.build_tree_contents([('other/file', '2b\n1\n')])
441
        other_tree.commit('rev2', rev_id='rev2b')
442
        this_tree.lock_write()
443
        self.addCleanup(this_tree.unlock)
444
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
445
            this_tree, 'rev2b', other_branch=other_tree.branch)
446
        merger.merge_type = _mod_merge.Merge3Merger
447
        tree_merger = merger.make_merger()
448
        tt = tree_merger.do_merge()
449
        tree_file = this_tree.get_file('file-id')
450
        try:
451
            self.assertEqual('2b\n1\n2a\n', tree_file.read())
452
        finally:
453
            tree_file.close()
454
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
455
    def test_merge_add_into_deleted_root(self):
456
        # Yes, people actually do this.  And report bugs if it breaks.
457
        source = self.make_branch_and_tree('source', format='rich-root-pack')
458
        self.build_tree(['source/foo/'])
459
        source.add('foo', 'foo-id')
460
        source.commit('Add foo')
461
        target = source.bzrdir.sprout('target').open_workingtree()
462
        subtree = target.extract('foo-id')
463
        subtree.commit('Delete root')
464
        self.build_tree(['source/bar'])
465
        source.add('bar', 'bar-id')
466
        source.commit('Add bar')
467
        subtree.merge_from_branch(source.branch)
468
3649.3.1 by Jelmer Vernooij
Merging from a previously joined branch will no longer cause a traceback.
469
    def test_merge_joined_branch(self):
470
        source = self.make_branch_and_tree('source', format='rich-root-pack')
471
        self.build_tree(['source/foo'])
472
        source.add('foo')
473
        source.commit('Add foo')
474
        target = self.make_branch_and_tree('target', format='rich-root-pack')
475
        self.build_tree(['target/bla'])
476
        target.add('bla')
477
        target.commit('Add bla')
478
        nested = source.bzrdir.sprout('target/subtree').open_workingtree()
479
        target.subsume(nested)
480
        target.commit('Join nested')
481
        self.build_tree(['source/bar'])
482
        source.add('bar')
483
        source.commit('Add bar')
484
        target.merge_from_branch(source.branch)
485
        target.commit('Merge source')
486
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
487
488
class TestPlanMerge(TestCaseWithMemoryTransport):
489
490
    def setUp(self):
491
        TestCaseWithMemoryTransport.setUp(self)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
492
        mapper = versionedfile.PrefixMapper()
493
        factory = knit.make_file_factory(True, mapper)
494
        self.vf = factory(self.get_transport())
495
        self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root')
496
        self.plan_merge_vf.fallback_versionedfiles.append(self.vf)
497
498
    def add_version(self, key, parents, text):
499
        self.vf.add_lines(key, parents, [c+'\n' for c in text])
500
3514.2.10 by John Arbash Meinel
Handle more edge cases.
501
    def add_rev(self, prefix, revision_id, parents, text):
502
        self.add_version((prefix, revision_id), [(prefix, p) for p in parents],
503
                         text)
504
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
505
    def add_uncommitted_version(self, key, parents, text):
506
        self.plan_merge_vf.add_lines(key, parents,
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
507
                                     [c+'\n' for c in text])
508
509
    def setup_plan_merge(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
510
        self.add_rev('root', 'A', [], 'abc')
511
        self.add_rev('root', 'B', ['A'], 'acehg')
512
        self.add_rev('root', 'C', ['A'], 'fabg')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
513
        return _PlanMerge('B', 'C', self.plan_merge_vf, ('root',))
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
514
515
    def setup_plan_merge_uncommitted(self):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
516
        self.add_version(('root', 'A'), [], 'abc')
517
        self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
518
        self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
519
        return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
520
521
    def test_unique_lines(self):
522
        plan = self.setup_plan_merge()
523
        self.assertEqual(plan._unique_lines(
524
            plan._get_matching_blocks('B', 'C')),
525
            ([1, 2, 3], [0, 2]))
526
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
527
    def test_plan_merge(self):
528
        self.setup_plan_merge()
529
        plan = self.plan_merge_vf.plan_merge('B', 'C')
530
        self.assertEqual([
531
                          ('new-b', 'f\n'),
532
                          ('unchanged', 'a\n'),
533
                          ('killed-a', 'b\n'),
534
                          ('killed-b', 'c\n'),
535
                          ('new-a', 'e\n'),
536
                          ('new-a', 'h\n'),
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
537
                          ('new-a', 'g\n'),
538
                          ('new-b', 'g\n')],
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
539
                         list(plan))
540
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
541
    def test_plan_merge_cherrypick(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
542
        self.add_rev('root', 'A', [], 'abc')
543
        self.add_rev('root', 'B', ['A'], 'abcde')
544
        self.add_rev('root', 'C', ['A'], 'abcefg')
545
        self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
546
        my_plan = _PlanMerge('B', 'D', self.plan_merge_vf, ('root',))
3514.2.11 by John Arbash Meinel
Shortcut the case when one revision is in the ancestry of the other.
547
        # We shortcut when one text supersedes the other in the per-file graph.
548
        # We don't actually need to compare the texts at this point.
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
549
        self.assertEqual([
3514.2.11 by John Arbash Meinel
Shortcut the case when one revision is in the ancestry of the other.
550
                          ('new-b', 'a\n'),
551
                          ('new-b', 'b\n'),
552
                          ('new-b', 'c\n'),
553
                          ('new-b', 'd\n'),
554
                          ('new-b', 'e\n'),
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
555
                          ('new-b', 'g\n'),
556
                          ('new-b', 'h\n')],
557
                          list(my_plan.plan_merge()))
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
558
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
559
    def test_plan_merge_no_common_ancestor(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
560
        self.add_rev('root', 'A', [], 'abc')
561
        self.add_rev('root', 'B', [], 'xyz')
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
562
        my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
563
        self.assertEqual([
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
564
                          ('new-a', 'a\n'),
565
                          ('new-a', 'b\n'),
566
                          ('new-a', 'c\n'),
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
567
                          ('new-b', 'x\n'),
568
                          ('new-b', 'y\n'),
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
569
                          ('new-b', 'z\n')],
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
570
                          list(my_plan.plan_merge()))
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
571
3514.2.10 by John Arbash Meinel
Handle more edge cases.
572
    def test_plan_merge_tail_ancestors(self):
573
        # The graph looks like this:
574
        #       A       # Common to all ancestors
575
        #      / \
576
        #     B   C     # Ancestors of E, only common to one side
577
        #     |\ /|
578
        #     D E F     # D, F are unique to G, H respectively
579
        #     |/ \|     # E is the LCA for G & H, and the unique LCA for
580
        #     G   H     # I, J
581
        #     |\ /|
582
        #     | X |
583
        #     |/ \|
584
        #     I   J     # criss-cross merge of G, H
585
        #
586
        # In this situation, a simple pruning of ancestors of E will leave D &
587
        # F "dangling", which looks like they introduce lines different from
588
        # the ones in E, but in actuality C&B introduced the lines, and they
589
        # are already present in E
590
591
        # Introduce the base text
592
        self.add_rev('root', 'A', [], 'abc')
593
        # Introduces a new line B
594
        self.add_rev('root', 'B', ['A'], 'aBbc')
595
        # Introduces a new line C
596
        self.add_rev('root', 'C', ['A'], 'abCc')
597
        # Introduce new line D
598
        self.add_rev('root', 'D', ['B'], 'DaBbc')
599
        # Merges B and C by just incorporating both
600
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
601
        # Introduce new line F
602
        self.add_rev('root', 'F', ['C'], 'abCcF')
603
        # Merge D & E by just combining the texts
604
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
605
        # Merge F & E by just combining the texts
606
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
607
        # Merge G & H by just combining texts
608
        self.add_rev('root', 'I', ['G', 'H'], 'DaBbCcF')
609
        # Merge G & H but supersede an old line in B
610
        self.add_rev('root', 'J', ['H', 'G'], 'DaJbCcF')
611
        plan = self.plan_merge_vf.plan_merge('I', 'J')
612
        self.assertEqual([
613
                          ('unchanged', 'D\n'),
614
                          ('unchanged', 'a\n'),
615
                          ('killed-b', 'B\n'),
616
                          ('new-b', 'J\n'),
617
                          ('unchanged', 'b\n'),
618
                          ('unchanged', 'C\n'),
619
                          ('unchanged', 'c\n'),
620
                          ('unchanged', 'F\n')],
621
                         list(plan))
622
623
    def test_plan_merge_tail_triple_ancestors(self):
624
        # The graph looks like this:
625
        #       A       # Common to all ancestors
626
        #      / \
627
        #     B   C     # Ancestors of E, only common to one side
628
        #     |\ /|
629
        #     D E F     # D, F are unique to G, H respectively
630
        #     |/|\|     # E is the LCA for G & H, and the unique LCA for
631
        #     G Q H     # I, J
632
        #     |\ /|     # Q is just an extra node which is merged into both
633
        #     | X |     # I and J
634
        #     |/ \|
635
        #     I   J     # criss-cross merge of G, H
636
        #
637
        # This is the same as the test_plan_merge_tail_ancestors, except we add
638
        # a third LCA that doesn't add new lines, but will trigger our more
639
        # involved ancestry logic
640
641
        self.add_rev('root', 'A', [], 'abc')
642
        self.add_rev('root', 'B', ['A'], 'aBbc')
643
        self.add_rev('root', 'C', ['A'], 'abCc')
644
        self.add_rev('root', 'D', ['B'], 'DaBbc')
645
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
646
        self.add_rev('root', 'F', ['C'], 'abCcF')
647
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
648
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
649
        self.add_rev('root', 'Q', ['E'], 'aBbCc')
650
        self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DaBbCcF')
651
        # Merge G & H but supersede an old line in B
652
        self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
653
        plan = self.plan_merge_vf.plan_merge('I', 'J')
654
        self.assertEqual([
655
                          ('unchanged', 'D\n'),
656
                          ('unchanged', 'a\n'),
657
                          ('killed-b', 'B\n'),
658
                          ('new-b', 'J\n'),
659
                          ('unchanged', 'b\n'),
660
                          ('unchanged', 'C\n'),
661
                          ('unchanged', 'c\n'),
662
                          ('unchanged', 'F\n')],
663
                         list(plan))
664
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
665
    def test_plan_merge_2_tail_triple_ancestors(self):
666
        # The graph looks like this:
667
        #     A   B     # 2 tails going back to NULL
668
        #     |\ /|
669
        #     D E F     # D, is unique to G, F to H
670
        #     |/|\|     # E is the LCA for G & H, and the unique LCA for
671
        #     G Q H     # I, J
672
        #     |\ /|     # Q is just an extra node which is merged into both
673
        #     | X |     # I and J
674
        #     |/ \|
675
        #     I   J     # criss-cross merge of G, H (and Q)
676
        #
677
678
        # This is meant to test after hitting a 3-way LCA, and multiple tail
679
        # ancestors (only have NULL_REVISION in common)
680
681
        self.add_rev('root', 'A', [], 'abc')
682
        self.add_rev('root', 'B', [], 'def')
683
        self.add_rev('root', 'D', ['A'], 'Dabc')
684
        self.add_rev('root', 'E', ['A', 'B'], 'abcdef')
685
        self.add_rev('root', 'F', ['B'], 'defF')
686
        self.add_rev('root', 'G', ['D', 'E'], 'Dabcdef')
687
        self.add_rev('root', 'H', ['F', 'E'], 'abcdefF')
688
        self.add_rev('root', 'Q', ['E'], 'abcdef')
689
        self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DabcdefF')
690
        # Merge G & H but supersede an old line in B
691
        self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DabcdJfF')
692
        plan = self.plan_merge_vf.plan_merge('I', 'J')
693
        self.assertEqual([
694
                          ('unchanged', 'D\n'),
695
                          ('unchanged', 'a\n'),
696
                          ('unchanged', 'b\n'),
697
                          ('unchanged', 'c\n'),
698
                          ('unchanged', 'd\n'),
699
                          ('killed-b', 'e\n'),
700
                          ('new-b', 'J\n'),
701
                          ('unchanged', 'f\n'),
702
                          ('unchanged', 'F\n')],
703
                         list(plan))
704
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
705
    def test_plan_merge_uncommitted_files(self):
3062.1.13 by Aaron Bentley
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
706
        self.setup_plan_merge_uncommitted()
707
        plan = self.plan_merge_vf.plan_merge('B:', 'C:')
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
708
        self.assertEqual([
709
                          ('new-b', 'f\n'),
710
                          ('unchanged', 'a\n'),
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
711
                          ('killed-a', 'b\n'),
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
712
                          ('killed-b', 'c\n'),
713
                          ('new-a', 'e\n'),
714
                          ('new-a', 'h\n'),
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
715
                          ('new-a', 'g\n'),
716
                          ('new-b', 'g\n')],
717
                         list(plan))
718
719
    def test_plan_merge_insert_order(self):
720
        """Weave merges are sensitive to the order of insertion.
721
        
722
        Specifically for overlapping regions, it effects which region gets put
723
        'first'. And when a user resolves an overlapping merge, if they use the
724
        same ordering, then the lines match the parents, if they don't only
725
        *some* of the lines match.
726
        """
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
727
        self.add_rev('root', 'A', [], 'abcdef')
728
        self.add_rev('root', 'B', ['A'], 'abwxcdef')
729
        self.add_rev('root', 'C', ['A'], 'abyzcdef')
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
730
        # Merge, and resolve the conflict by adding *both* sets of lines
731
        # If we get the ordering wrong, these will look like new lines in D,
732
        # rather than carried over from B, C
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
733
        self.add_rev('root', 'D', ['B', 'C'],
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
734
                         'abwxyzcdef')
735
        # Supersede the lines in B and delete the lines in C, which will
736
        # conflict if they are treated as being in D
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
737
        self.add_rev('root', 'E', ['C', 'B'],
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
738
                         'abnocdef')
739
        # Same thing for the lines in C
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
740
        self.add_rev('root', 'F', ['C'], 'abpqcdef')
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
741
        plan = self.plan_merge_vf.plan_merge('D', 'E')
742
        self.assertEqual([
743
                          ('unchanged', 'a\n'),
744
                          ('unchanged', 'b\n'),
745
                          ('killed-b', 'w\n'),
746
                          ('killed-b', 'x\n'),
747
                          ('killed-b', 'y\n'),
748
                          ('killed-b', 'z\n'),
749
                          ('new-b', 'n\n'),
750
                          ('new-b', 'o\n'),
751
                          ('unchanged', 'c\n'),
752
                          ('unchanged', 'd\n'),
753
                          ('unchanged', 'e\n'),
754
                          ('unchanged', 'f\n')],
755
                         list(plan))
756
        plan = self.plan_merge_vf.plan_merge('E', 'D')
757
        # Going in the opposite direction shows the effect of the opposite plan
758
        self.assertEqual([
759
                          ('unchanged', 'a\n'),
760
                          ('unchanged', 'b\n'),
761
                          ('new-b', 'w\n'),
762
                          ('new-b', 'x\n'),
763
                          ('killed-a', 'y\n'),
764
                          ('killed-a', 'z\n'),
765
                          ('killed-both', 'w\n'),
766
                          ('killed-both', 'x\n'),
767
                          ('new-a', 'n\n'),
768
                          ('new-a', 'o\n'),
769
                          ('unchanged', 'c\n'),
770
                          ('unchanged', 'd\n'),
771
                          ('unchanged', 'e\n'),
772
                          ('unchanged', 'f\n')],
3062.1.9 by Aaron Bentley
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
773
                         list(plan))
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
774
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
775
    def test_plan_merge_criss_cross(self):
776
        # This is specificly trying to trigger problems when using limited
777
        # ancestry and weaves. The ancestry graph looks like:
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
778
        #       XX      unused ancestor, should not show up in the weave
779
        #       |
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
780
        #       A       Unique LCA
781
        #       |\
782
        #       B \     Introduces a line 'foo'
783
        #      / \ \
784
        #     C   D E   C & D both have 'foo', E has different changes
785
        #     |\ /| |
786
        #     | X | |
787
        #     |/ \|/
788
        #     F   G      All of C, D, E are merged into F and G, so they are
789
        #                all common ancestors.
790
        #
791
        # The specific issue with weaves:
792
        #   B introduced a text ('foo') that is present in both C and D.
793
        #   If we do not include B (because it isn't an ancestor of E), then
794
        #   the A=>C and A=>D look like both sides independently introduce the
795
        #   text ('foo'). If F does not modify the text, it would still appear
796
        #   to have deleted on of the versions from C or D. If G then modifies
797
        #   'foo', it should appear as superseding the value in F (since it
798
        #   came from B), rather than conflict because of the resolution during
799
        #   C & D.
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
800
        self.add_rev('root', 'XX', [], 'qrs')
801
        self.add_rev('root', 'A', ['XX'], 'abcdef')
802
        self.add_rev('root', 'B', ['A'], 'axcdef')
803
        self.add_rev('root', 'C', ['B'], 'axcdefg')
804
        self.add_rev('root', 'D', ['B'], 'haxcdef')
805
        self.add_rev('root', 'E', ['A'], 'abcdyf')
806
        # Simple combining of all texts
807
        self.add_rev('root', 'F', ['C', 'D', 'E'], 'haxcdyfg')
808
        # combine and supersede 'x'
809
        self.add_rev('root', 'G', ['C', 'D', 'E'], 'hazcdyfg')
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
810
        plan = self.plan_merge_vf.plan_merge('F', 'G')
811
        self.assertEqual([
812
                          ('unchanged', 'h\n'),
813
                          ('unchanged', 'a\n'),
3514.2.7 by John Arbash Meinel
Fix the failing test by implementing the fallback logic.
814
                          ('killed-base', 'b\n'),
3514.2.6 by John Arbash Meinel
Write a (failing) test for complex ancestry.
815
                          ('killed-b', 'x\n'),
816
                          ('new-b', 'z\n'),
817
                          ('unchanged', 'c\n'),
818
                          ('unchanged', 'd\n'),
819
                          ('killed-base', 'e\n'),
820
                          ('unchanged', 'y\n'),
821
                          ('unchanged', 'f\n'),
822
                          ('unchanged', 'g\n')],
823
                         list(plan))
824
3514.2.12 by John Arbash Meinel
Start refactoring into helper functions
825
    def assertRemoveExternalReferences(self, filtered_parent_map,
826
                                       child_map, tails, parent_map):
827
        """Assert results for _PlanMerge._remove_external_references."""
828
        (act_filtered_parent_map, act_child_map,
829
         act_tails) = _PlanMerge._remove_external_references(parent_map)
830
831
        # The parent map *should* preserve ordering, but the ordering of
832
        # children is not strictly defined
833
        # child_map = dict((k, sorted(children))
834
        #                  for k, children in child_map.iteritems())
835
        # act_child_map = dict(k, sorted(children)
836
        #                      for k, children in act_child_map.iteritems())
837
        self.assertEqual(filtered_parent_map, act_filtered_parent_map)
838
        self.assertEqual(child_map, act_child_map)
839
        self.assertEqual(sorted(tails), sorted(act_tails))
840
841
    def test__remove_external_references(self):
842
        # First, nothing to remove
843
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
844
            {1: [2], 2: [3], 3: []}, [1], {3: [2], 2: [1], 1: []})
845
        # The reverse direction
846
        self.assertRemoveExternalReferences({1: [2], 2: [3], 3: []},
847
            {3: [2], 2: [1], 1: []}, [3], {1: [2], 2: [3], 3: []})
848
        # Extra references
849
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
850
            {1: [2], 2: [3], 3: []}, [1], {3: [2, 4], 2: [1, 5], 1: [6]})
851
        # Multiple tails
852
        self.assertRemoveExternalReferences(
853
            {4: [2, 3], 3: [], 2: [1], 1: []},
854
            {1: [2], 2: [4], 3: [4], 4: []},
855
            [1, 3],
856
            {4: [2, 3], 3: [5], 2: [1], 1: [6]})
857
        # Multiple children
858
        self.assertRemoveExternalReferences(
859
            {1: [3], 2: [3, 4], 3: [], 4: []},
860
            {1: [], 2: [], 3: [1, 2], 4: [2]},
861
            [3, 4],
862
            {1: [3], 2: [3, 4], 3: [5], 4: []})
863
3514.2.13 by John Arbash Meinel
Add the ability to prune extra tails from the parent_map.
864
    def assertPruneTails(self, pruned_map, tails, parent_map):
865
        child_map = {}
866
        for key, parent_keys in parent_map.iteritems():
867
            child_map.setdefault(key, [])
868
            for pkey in parent_keys:
869
                child_map.setdefault(pkey, []).append(key)
870
        _PlanMerge._prune_tails(parent_map, child_map, tails)
871
        self.assertEqual(pruned_map, parent_map)
872
873
    def test__prune_tails(self):
874
        # Nothing requested to prune
875
        self.assertPruneTails({1: [], 2: [], 3: []}, [],
876
                              {1: [], 2: [], 3: []})
877
        # Prune a single entry
878
        self.assertPruneTails({1: [], 3: []}, [2],
879
                              {1: [], 2: [], 3: []})
880
        # Prune a chain
881
        self.assertPruneTails({1: []}, [3],
882
                              {1: [], 2: [3], 3: []})
883
        # Prune a chain with a diamond
884
        self.assertPruneTails({1: []}, [5],
885
                              {1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
886
        # Prune a partial chain
887
        self.assertPruneTails({1: [6], 6:[]}, [5],
888
                              {1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
889
                               6: []})
890
        # Prune a chain with multiple tips, that pulls out intermediates
891
        self.assertPruneTails({1:[3], 3:[]}, [4, 5],
892
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
893
        self.assertPruneTails({1:[3], 3:[]}, [5, 4],
894
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
895
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
896
    def test_subtract_plans(self):
897
        old_plan = [
898
        ('unchanged', 'a\n'),
899
        ('new-a', 'b\n'),
900
        ('killed-a', 'c\n'),
901
        ('new-b', 'd\n'),
902
        ('new-b', 'e\n'),
903
        ('killed-b', 'f\n'),
904
        ('killed-b', 'g\n'),
905
        ]
906
        new_plan = [
907
        ('unchanged', 'a\n'),
908
        ('new-a', 'b\n'),
909
        ('killed-a', 'c\n'),
910
        ('new-b', 'd\n'),
911
        ('new-b', 'h\n'),
912
        ('killed-b', 'f\n'),
913
        ('killed-b', 'i\n'),
914
        ]
915
        subtracted_plan = [
916
        ('unchanged', 'a\n'),
917
        ('new-a', 'b\n'),
918
        ('killed-a', 'c\n'),
919
        ('new-b', 'h\n'),
920
        ('unchanged', 'f\n'),
921
        ('killed-b', 'i\n'),
922
        ]
923
        self.assertEqual(subtracted_plan,
3062.2.3 by Aaron Bentley
Sync up with bzr.dev API changes
924
            list(_PlanMerge._subtract_plans(old_plan, new_plan)))
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
925
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
926
    def setup_merge_with_base(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
927
        self.add_rev('root', 'COMMON', [], 'abc')
928
        self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
929
        self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
930
        self.add_rev('root', 'OTHER', ['BASE'], 'eafb')
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
931
932
    def test_plan_merge_with_base(self):
933
        self.setup_merge_with_base()
3062.2.3 by Aaron Bentley
Sync up with bzr.dev API changes
934
        plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
3062.2.1 by Aaron Bentley
Add support for plan-merge with a base
935
        self.assertEqual([('unchanged', 'a\n'),
936
                          ('new-b', 'f\n'),
937
                          ('unchanged', 'b\n'),
938
                          ('killed-b', 'c\n'),
939
                          ('new-a', 'd\n')
940
                         ], list(plan))
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
941
942
    def test_plan_lca_merge(self):
943
        self.setup_plan_merge()
944
        plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
945
        self.assertEqual([
946
                          ('new-b', 'f\n'),
947
                          ('unchanged', 'a\n'),
948
                          ('killed-b', 'c\n'),
949
                          ('new-a', 'e\n'),
950
                          ('new-a', 'h\n'),
951
                          ('killed-a', 'b\n'),
952
                          ('unchanged', 'g\n')],
953
                         list(plan))
954
955
    def test_plan_lca_merge_uncommitted_files(self):
956
        self.setup_plan_merge_uncommitted()
957
        plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
958
        self.assertEqual([
959
                          ('new-b', 'f\n'),
960
                          ('unchanged', 'a\n'),
961
                          ('killed-b', 'c\n'),
962
                          ('new-a', 'e\n'),
963
                          ('new-a', 'h\n'),
964
                          ('killed-a', 'b\n'),
965
                          ('unchanged', 'g\n')],
966
                         list(plan))
967
968
    def test_plan_lca_merge_with_base(self):
969
        self.setup_merge_with_base()
970
        plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
971
        self.assertEqual([('unchanged', 'a\n'),
972
                          ('new-b', 'f\n'),
973
                          ('unchanged', 'b\n'),
974
                          ('killed-b', 'c\n'),
975
                          ('new-a', 'd\n')
976
                         ], list(plan))
977
978
    def test_plan_lca_merge_with_criss_cross(self):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
979
        self.add_version(('root', 'ROOT'), [], 'abc')
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
980
        # each side makes a change
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
981
        self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
982
        self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
983
        # both sides merge, discarding others' changes
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
984
        self.add_version(('root', 'LCA1'),
985
            [('root', 'REV1'), ('root', 'REV2')], 'abcd')
986
        self.add_version(('root', 'LCA2'),
987
            [('root', 'REV1'), ('root', 'REV2')], 'fabce')
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
988
        plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
3144.3.10 by Aaron Bentley
Use correct index when emitting conflicted-b
989
        self.assertEqual([('new-b', 'f\n'),
990
                          ('unchanged', 'a\n'),
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
991
                          ('unchanged', 'b\n'),
992
                          ('unchanged', 'c\n'),
3144.3.3 by Aaron Bentley
Update test for new conflicted types
993
                          ('conflicted-a', 'd\n'),
994
                          ('conflicted-b', 'e\n'),
3144.3.1 by Aaron Bentley
Implement LCA merge, with problematic conflict markers
995
                         ], list(plan))
3144.5.3 by Aaron Bentley
Test interesting_files for LCA merge
996
3287.17.1 by John Arbash Meinel
Fix bug #235715 by using the empty list as the text for a base of NULL_REVISION.
997
    def test_plan_lca_merge_with_null(self):
3350.6.5 by Robert Collins
Update to bzr.dev.
998
        self.add_version(('root', 'A'), [], 'ab')
999
        self.add_version(('root', 'B'), [], 'bc')
3287.17.1 by John Arbash Meinel
Fix bug #235715 by using the empty list as the text for a base of NULL_REVISION.
1000
        plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
1001
        self.assertEqual([('new-a', 'a\n'),
1002
                          ('unchanged', 'b\n'),
1003
                          ('new-b', 'c\n'),
1004
                         ], list(plan))
1005
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1006
    def test_plan_merge_with_delete_and_change(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
1007
        self.add_rev('root', 'C', [], 'a')
1008
        self.add_rev('root', 'A', ['C'], 'b')
1009
        self.add_rev('root', 'B', ['C'], '')
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1010
        plan = self.plan_merge_vf.plan_merge('A', 'B')
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1011
        self.assertEqual([('killed-both', 'a\n'),
1012
                          ('new-a', 'b\n'),
1013
                         ], list(plan))
1014
1015
    def test_plan_merge_with_move_and_change(self):
3514.2.17 by John Arbash Meinel
On Ian's suggestion, change the 'plan_merge' tests to use the clearer 'add_rev' instead of 'add_version'
1016
        self.add_rev('root', 'C', [], 'abcd')
1017
        self.add_rev('root', 'A', ['C'], 'acbd')
1018
        self.add_rev('root', 'B', ['C'], 'aBcd')
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1019
        plan = self.plan_merge_vf.plan_merge('A', 'B')
1020
        self.assertEqual([('unchanged', 'a\n'),
1021
                          ('new-a', 'c\n'),
1022
                          ('killed-b', 'b\n'),
1023
                          ('new-b', 'B\n'),
1024
                          ('killed-a', 'c\n'),
1025
                          ('unchanged', 'd\n'),
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1026
                         ], list(plan))
1027
3144.5.3 by Aaron Bentley
Test interesting_files for LCA merge
1028
1029
class TestMergeImplementation(object):
1030
1031
    def do_merge(self, target_tree, source_tree, **kwargs):
1032
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
1033
            target_tree, source_tree.last_revision(),
1034
            other_branch=source_tree.branch)
1035
        merger.merge_type=self.merge_type
1036
        for name, value in kwargs.items():
1037
            setattr(merger, name, value)
1038
        merger.do_merge()
1039
1040
    def test_merge_specific_file(self):
1041
        this_tree = self.make_branch_and_tree('this')
1042
        this_tree.lock_write()
1043
        self.addCleanup(this_tree.unlock)
1044
        self.build_tree_contents([
1045
            ('this/file1', 'a\nb\n'),
1046
            ('this/file2', 'a\nb\n')
1047
        ])
1048
        this_tree.add(['file1', 'file2'])
1049
        this_tree.commit('Added files')
1050
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
1051
        self.build_tree_contents([
1052
            ('other/file1', 'a\nb\nc\n'),
1053
            ('other/file2', 'a\nb\nc\n')
1054
        ])
1055
        other_tree.commit('modified both')
1056
        self.build_tree_contents([
1057
            ('this/file1', 'd\na\nb\n'),
1058
            ('this/file2', 'd\na\nb\n')
1059
        ])
1060
        this_tree.commit('modified both')
1061
        self.do_merge(this_tree, other_tree, interesting_files=['file1'])
1062
        self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
1063
        self.assertFileEqual('d\na\nb\n', 'this/file2')
1064
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1065
    def test_merge_move_and_change(self):
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1066
        this_tree = self.make_branch_and_tree('this')
1067
        this_tree.lock_write()
1068
        self.addCleanup(this_tree.unlock)
1069
        self.build_tree_contents([
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1070
            ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1071
        ])
1072
        this_tree.add('file1',)
1073
        this_tree.commit('Added file')
1074
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
1075
        self.build_tree_contents([
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1076
            ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1077
        ])
3514.2.16 by John Arbash Meinel
Review feedback from Ian.
1078
        other_tree.commit('Changed 2 to 2.1')
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1079
        self.build_tree_contents([
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1080
            ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1081
        ])
3514.2.16 by John Arbash Meinel
Review feedback from Ian.
1082
        this_tree.commit('Swapped 2 & 3')
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1083
        self.do_merge(this_tree, other_tree)
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1084
        self.assertFileEqual('line 1\n'
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1085
            '<<<<<<< TREE\n'
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1086
            'line 3\n'
1087
            'line 2\n'
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1088
            '=======\n'
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1089
            'line 2 to 2.1\n'
1090
            'line 3\n'
1091
            '>>>>>>> MERGE-SOURCE\n'
1092
            'line 4\n', 'this/file1')
3514.2.1 by Aaron Bentley
Test for correct conflicts on delete + change
1093
3144.5.3 by Aaron Bentley
Test interesting_files for LCA merge
1094
1095
class TestMerge3Merge(TestCaseWithTransport, TestMergeImplementation):
1096
1097
    merge_type = _mod_merge.Merge3Merger
1098
1099
1100
class TestWeaveMerge(TestCaseWithTransport, TestMergeImplementation):
1101
1102
    merge_type = _mod_merge.WeaveMerger
1103
1104
1105
class TestLCAMerge(TestCaseWithTransport, TestMergeImplementation):
1106
1107
    merge_type = _mod_merge.LCAMerger
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1108
1109
    def test_merge_move_and_change(self):
1110
        self.expectFailure("lca merge doesn't conflict for move and change",
1111
            super(TestLCAMerge, self).test_merge_move_and_change)
3514.4.6 by John Arbash Meinel
Merge in the BranchBuilder updates, which brings in a newer bzr.dev
1112
3514.4.1 by John Arbash Meinel
Update Merger to set a flag when we encounter a criss-cross merge.
1113
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1114
class LoggingMerger(object):
1115
    # These seem to be the required attributes
1116
    requires_base = False
1117
    supports_reprocess = False
1118
    supports_show_base = False
1119
    supports_cherrypick = False
1120
    # We intentionally do not define supports_lca_trees
1121
1122
    def __init__(self, *args, **kwargs):
1123
        self.args = args
1124
        self.kwargs = kwargs
1125
1126
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1127
class TestMergerBase(TestCaseWithMemoryTransport):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1128
    """Common functionality for Merger tests that don't write to disk."""
3514.4.1 by John Arbash Meinel
Update Merger to set a flag when we encounter a criss-cross merge.
1129
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1130
    def get_builder(self):
1131
        builder = self.make_branch_builder('path')
1132
        builder.start_series()
1133
        self.addCleanup(builder.finish_series)
1134
        return builder
1135
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1136
    def setup_simple_graph(self):
1137
        """Create a simple 3-node graph.
1138
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1139
        :return: A BranchBuilder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1140
        """
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1141
        #
1142
        #  A
1143
        #  |\
1144
        #  B C
1145
        #
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1146
        builder = self.get_builder()
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1147
        builder.build_snapshot('A-id', None,
1148
            [('add', ('', None, 'directory', None))])
1149
        builder.build_snapshot('C-id', ['A-id'], [])
1150
        builder.build_snapshot('B-id', ['A-id'], [])
1151
        return builder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1152
1153
    def setup_criss_cross_graph(self):
1154
        """Create a 5-node graph with a criss-cross.
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1155
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1156
        :return: A BranchBuilder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1157
        """
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1158
        # A
1159
        # |\
1160
        # B C
1161
        # |X|
1162
        # D E
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1163
        builder = self.setup_simple_graph()
1164
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1165
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1166
        return builder
1167
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1168
    def make_Merger(self, builder, other_revision_id,
1169
                    interesting_files=None, interesting_ids=None):
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1170
        """Make a Merger object from a branch builder"""
1171
        mem_tree = memorytree.MemoryTree.create_on_branch(builder.get_branch())
1172
        mem_tree.lock_write()
1173
        self.addCleanup(mem_tree.unlock)
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1174
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1175
            mem_tree, other_revision_id)
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1176
        merger.set_interesting_files(interesting_files)
1177
        # It seems there is no matching function for set_interesting_ids
1178
        merger.interesting_ids = interesting_ids
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1179
        merger.merge_type = _mod_merge.Merge3Merger
1180
        return merger
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1181
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1182
1183
class TestMergerInMemory(TestMergerBase):
1184
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1185
    def test_find_base(self):
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1186
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1187
        self.assertEqual('A-id', merger.base_rev_id)
1188
        self.assertFalse(merger._is_criss_cross)
1189
        self.assertIs(None, merger._lca_trees)
1190
1191
    def test_find_base_criss_cross(self):
3514.4.14 by John Arbash Meinel
Add a test which shows that the ordering switches when you pick the other parent.
1192
        builder = self.setup_criss_cross_graph()
1193
        merger = self.make_Merger(builder, 'E-id')
3514.4.1 by John Arbash Meinel
Update Merger to set a flag when we encounter a criss-cross merge.
1194
        self.assertEqual('A-id', merger.base_rev_id)
1195
        self.assertTrue(merger._is_criss_cross)
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1196
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1197
                                            for t in merger._lca_trees])
3514.4.14 by John Arbash Meinel
Add a test which shows that the ordering switches when you pick the other parent.
1198
        # If we swap the order, we should get a different lca order
1199
        builder.build_snapshot('F-id', ['E-id'], [])
1200
        merger = self.make_Merger(builder, 'D-id')
1201
        self.assertEqual(['C-id', 'B-id'], [t.get_revision_id()
1202
                                            for t in merger._lca_trees])
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1203
3514.4.23 by John Arbash Meinel
Handle when there are more than 2 LCAs while searching for the unique lca.
1204
    def test_find_base_triple_criss_cross(self):
1205
        #       A-.
1206
        #      / \ \
1207
        #     B   C F # F is merged into both branches
1208
        #     |\ /| |
1209
        #     | X | |\
1210
        #     |/ \| | :
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1211
        #   : D   E |
1212
        #    \|   |/
3514.4.23 by John Arbash Meinel
Handle when there are more than 2 LCAs while searching for the unique lca.
1213
        #     G   H
1214
        builder = self.setup_criss_cross_graph()
1215
        builder.build_snapshot('F-id', ['A-id'], [])
1216
        builder.build_snapshot('H-id', ['E-id', 'F-id'], [])
1217
        builder.build_snapshot('G-id', ['D-id', 'F-id'], [])
1218
        merger = self.make_Merger(builder, 'H-id')
1219
        self.assertEqual(['B-id', 'C-id', 'F-id'],
1220
                         [t.get_revision_id() for t in merger._lca_trees])
1221
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1222
    def test_no_criss_cross_passed_to_merge_type(self):
1223
        class LCATreesMerger(LoggingMerger):
1224
            supports_lca_trees = True
1225
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1226
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1227
        merger.merge_type = LCATreesMerger
1228
        merge_obj = merger.make_merger()
1229
        self.assertIsInstance(merge_obj, LCATreesMerger)
1230
        self.assertFalse('lca_trees' in merge_obj.kwargs)
1231
1232
    def test_criss_cross_passed_to_merge_type(self):
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1233
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1234
        merger.merge_type = _mod_merge.Merge3Merger
1235
        merge_obj = merger.make_merger()
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1236
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1237
                                            for t in merger._lca_trees])
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1238
1239
    def test_criss_cross_not_supported_merge_type(self):
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1240
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1241
        # We explicitly do not define supports_lca_trees
1242
        merger.merge_type = LoggingMerger
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1243
        merge_obj = merger.make_merger()
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1244
        self.assertIsInstance(merge_obj, LoggingMerger)
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1245
        self.assertFalse('lca_trees' in merge_obj.kwargs)
1246
1247
    def test_criss_cross_unsupported_merge_type(self):
1248
        class UnsupportedLCATreesMerger(LoggingMerger):
1249
            supports_lca_trees = False
1250
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1251
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1252
        merger.merge_type = UnsupportedLCATreesMerger
1253
        merge_obj = merger.make_merger()
1254
        self.assertIsInstance(merge_obj, UnsupportedLCATreesMerger)
1255
        self.assertFalse('lca_trees' in merge_obj.kwargs)
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1256
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1257
1258
class TestMergerEntriesLCA(TestMergerBase):
1259
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1260
    def make_merge_obj(self, builder, other_revision_id,
1261
                       interesting_files=None, interesting_ids=None):
1262
        merger = self.make_Merger(builder, other_revision_id,
1263
            interesting_files=interesting_files,
1264
            interesting_ids=interesting_ids)
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1265
        return merger.make_merger()
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1266
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1267
    def test_simple(self):
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1268
        builder = self.get_builder()
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1269
        builder.build_snapshot('A-id', None,
1270
            [('add', (u'', 'a-root-id', 'directory', None)),
1271
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1272
        builder.build_snapshot('C-id', ['A-id'],
1273
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
1274
        builder.build_snapshot('B-id', ['A-id'],
1275
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
1276
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1277
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1278
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1279
            [('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1280
        merge_obj = self.make_merge_obj(builder, 'E-id')
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1281
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1282
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1283
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1284
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1285
        entries = list(merge_obj._entries_lca())
1286
1287
        # (file_id, changed, parents, names, executable)
1288
        # BASE, lca1, lca2, OTHER, THIS
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1289
        root_id = 'a-root-id'
3514.4.12 by John Arbash Meinel
add more filtering for when a directory hasn't actually changed.
1290
        self.assertEqual([('a-id', True,
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1291
                           ((root_id, [root_id, root_id]), root_id, root_id),
1292
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1293
                           ((False, [False, False]), False, False)),
1294
                         ], entries)
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1295
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1296
    def test_not_in_base(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1297
        # LCAs all have the same last-modified revision for the file, as do
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1298
        # the tips, but the base has something different
1299
        #       A    base, doesn't have the file
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1300
        #       |\
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1301
        #       B C  B introduces 'foo', C introduces 'bar'
1302
        #       |X|
1303
        #       D E  D and E now both have 'foo' and 'bar'
1304
        #       |X|
1305
        #       F G  the files are now in F, G, D and E, but not in A
1306
        #            G modifies 'bar'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1307
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1308
        builder = self.get_builder()
1309
        builder.build_snapshot('A-id', None,
1310
            [('add', (u'', 'a-root-id', 'directory', None))])
1311
        builder.build_snapshot('B-id', ['A-id'],
1312
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1313
        builder.build_snapshot('C-id', ['A-id'],
1314
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1315
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1316
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1317
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1318
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1319
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1320
            [('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1321
        builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1322
        merge_obj = self.make_merge_obj(builder, 'G-id')
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1323
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1324
        self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1325
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1326
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1327
        entries = list(merge_obj._entries_lca())
1328
        root_id = 'a-root-id'
1329
        self.assertEqual([('bar-id', True,
1330
                           ((None, [root_id, root_id]), root_id, root_id),
1331
                           ((None, [u'bar', u'bar']), u'bar', u'bar'),
1332
                           ((None, [False, False]), False, False)),
1333
                         ], entries)
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1334
1335
    def test_not_in_this(self):
1336
        builder = self.get_builder()
1337
        builder.build_snapshot('A-id', None,
1338
            [('add', (u'', 'a-root-id', 'directory', None)),
1339
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1340
        builder.build_snapshot('B-id', ['A-id'],
1341
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1342
        builder.build_snapshot('C-id', ['A-id'],
1343
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
1344
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1345
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1346
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1347
            [('unversion', 'a-id')])
1348
        merge_obj = self.make_merge_obj(builder, 'E-id')
1349
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1350
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1351
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1352
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1353
1354
        entries = list(merge_obj._entries_lca())
1355
        root_id = 'a-root-id'
1356
        self.assertEqual([('a-id', True,
1357
                           ((root_id, [root_id, root_id]), root_id, None),
1358
                           ((u'a', [u'a', u'a']), u'a', None),
1359
                           ((False, [False, False]), False, None)),
1360
                         ], entries)
1361
3514.4.20 by John Arbash Meinel
Use the _lca_multi_way to work out if there is actually a kind/parent/name/content change.
1362
    def test_file_not_in_one_lca(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1363
        #   A   # just root
1364
        #   |\
1365
        #   B C # B no file, C introduces a file
1366
        #   |X|
1367
        #   D E # D and E both have the file, unchanged from C
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1368
        builder = self.get_builder()
1369
        builder.build_snapshot('A-id', None,
1370
            [('add', (u'', 'a-root-id', 'directory', None))])
1371
        builder.build_snapshot('B-id', ['A-id'], [])
1372
        builder.build_snapshot('C-id', ['A-id'],
1373
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
3514.4.20 by John Arbash Meinel
Use the _lca_multi_way to work out if there is actually a kind/parent/name/content change.
1374
        builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1375
        builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1376
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1377
        merge_obj = self.make_merge_obj(builder, 'E-id')
1378
1379
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1380
                                            for t in merge_obj._lca_trees])
1381
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1382
1383
        entries = list(merge_obj._entries_lca())
1384
        root_id = 'a-root-id'
3514.4.20 by John Arbash Meinel
Use the _lca_multi_way to work out if there is actually a kind/parent/name/content change.
1385
        self.assertEqual([], entries)
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1386
3514.4.15 by John Arbash Meinel
Handle when OTHER doesn't have the entry.
1387
    def test_not_in_other(self):
1388
        builder = self.get_builder()
1389
        builder.build_snapshot('A-id', None,
1390
            [('add', (u'', 'a-root-id', 'directory', None)),
1391
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1392
        builder.build_snapshot('B-id', ['A-id'], [])
1393
        builder.build_snapshot('C-id', ['A-id'], [])
1394
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1395
            [('unversion', 'a-id')])
1396
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1397
        merge_obj = self.make_merge_obj(builder, 'E-id')
1398
1399
        entries = list(merge_obj._entries_lca())
1400
        root_id = 'a-root-id'
1401
        self.assertEqual([('a-id', True,
1402
                           ((root_id, [root_id, root_id]), None, root_id),
1403
                           ((u'a', [u'a', u'a']), None, u'a'),
1404
                           ((False, [False, False]), None, False)),
1405
                         ], entries)
1406
3514.4.30 by John Arbash Meinel
Several updates.
1407
    def test_not_in_other_or_lca(self):
1408
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1409
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1410
        #       B C  B nothing, C deletes foo
1411
        #       |X|
1412
        #       D E  D restores foo (same as B), E leaves it deleted
1413
        # We should emit an entry for this
1414
        builder = self.get_builder()
1415
        builder.build_snapshot('A-id', None,
1416
            [('add', (u'', 'a-root-id', 'directory', None)),
1417
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1418
        builder.build_snapshot('B-id', ['A-id'], [])
1419
        builder.build_snapshot('C-id', ['A-id'],
1420
            [('unversion', 'foo-id')])
1421
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1422
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1423
        merge_obj = self.make_merge_obj(builder, 'E-id')
1424
1425
        entries = list(merge_obj._entries_lca())
1426
        root_id = 'a-root-id'
1427
        self.assertEqual([('foo-id', True,
1428
                           ((root_id, [root_id, None]), None, root_id),
1429
                           ((u'foo', [u'foo', None]), None, 'foo'),
1430
                           ((False, [False, None]), None, False)),
1431
                         ], entries)
1432
3514.4.20 by John Arbash Meinel
Use the _lca_multi_way to work out if there is actually a kind/parent/name/content change.
1433
    def test_only_in_one_lca(self):
1434
        builder = self.get_builder()
1435
        builder.build_snapshot('A-id', None,
1436
            [('add', (u'', 'a-root-id', 'directory', None))])
1437
        builder.build_snapshot('B-id', ['A-id'], [])
1438
        builder.build_snapshot('C-id', ['A-id'],
1439
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1440
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1441
            [('unversion', 'a-id')])
1442
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1443
        merge_obj = self.make_merge_obj(builder, 'E-id')
1444
1445
        entries = list(merge_obj._entries_lca())
1446
        root_id = 'a-root-id'
1447
        self.assertEqual([('a-id', True,
1448
                           ((None, [None, root_id]), None, None),
1449
                           ((None, [None, u'a']), None, None),
1450
                           ((None, [None, False]), None, None)),
1451
                         ], entries)
1452
3514.4.16 by John Arbash Meinel
another test for only in OTHER
1453
    def test_only_in_other(self):
1454
        builder = self.get_builder()
1455
        builder.build_snapshot('A-id', None,
1456
            [('add', (u'', 'a-root-id', 'directory', None))])
1457
        builder.build_snapshot('B-id', ['A-id'], [])
1458
        builder.build_snapshot('C-id', ['A-id'], [])
1459
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1460
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1461
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1462
        merge_obj = self.make_merge_obj(builder, 'E-id')
1463
1464
        entries = list(merge_obj._entries_lca())
1465
        root_id = 'a-root-id'
1466
        self.assertEqual([('a-id', True,
1467
                           ((None, [None, None]), root_id, None),
1468
                           ((None, [None, None]), u'a', None),
1469
                           ((None, [None, None]), False, None)),
1470
                         ], entries)
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
1471
3514.4.30 by John Arbash Meinel
Several updates.
1472
    def test_one_lca_supersedes(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1473
        # One LCA supersedes the other LCAs last modified value, but the
3514.4.30 by John Arbash Meinel
Several updates.
1474
        # value is not the same as BASE.
1475
        #       A    base, introduces 'foo', last mod A
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1476
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1477
        #       B C  B modifies 'foo' (mod B), C does nothing (mod A)
1478
        #       |X|
1479
        #       D E  D does nothing (mod B), E updates 'foo' (mod E)
1480
        #       |X|
1481
        #       F G  F updates 'foo' (mod F). G does nothing (mod E)
1482
        #
1483
        #   At this point, G should not be considered to modify 'foo', even
1484
        #   though its LCAs disagree. This is because the modification in E
1485
        #   completely supersedes the value in D.
1486
        builder = self.get_builder()
1487
        builder.build_snapshot('A-id', None,
1488
            [('add', (u'', 'a-root-id', 'directory', None)),
1489
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1490
        builder.build_snapshot('C-id', ['A-id'], [])
1491
        builder.build_snapshot('B-id', ['A-id'],
1492
            [('modify', ('foo-id', 'B content\n'))])
1493
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1494
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1495
            [('modify', ('foo-id', 'E content\n'))])
1496
        builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
1497
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1498
            [('modify', ('foo-id', 'F content\n'))])
1499
        merge_obj = self.make_merge_obj(builder, 'G-id')
1500
1501
        self.assertEqual([], list(merge_obj._entries_lca()))
1502
3514.4.31 by John Arbash Meinel
Add expected failures for cases where we should be looking at more than
1503
    def test_one_lca_supersedes_path(self):
1504
        # Double-criss-cross merge, the ultimate base value is different from
1505
        # the intermediate.
1506
        #   A    value 'foo'
1507
        #   |\
1508
        #   B C  B value 'bar', C = 'foo'
1509
        #   |X|
1510
        #   D E  D = 'bar', E supersedes to 'bing'
1511
        #   |X|
1512
        #   F G  F = 'bing', G supersedes to 'barry'
1513
        #
1514
        # In this case, we technically should not care about the value 'bar' for
1515
        # D, because it was clearly superseded by E's 'bing'. The
1516
        # per-file/attribute graph would actually look like:
1517
        #   A
1518
        #   |
1519
        #   B
1520
        #   |
1521
        #   E
1522
        #   |
1523
        #   G
1524
        #
1525
        # Because the other side of the merge never modifies the value, it just
1526
        # takes the value from the merge.
1527
        #
1528
        # ATM this fails because we will prune 'foo' from the LCAs, but we
1529
        # won't prune 'bar'. This is getting far off into edge-case land, so we
1530
        # aren't supporting it yet.
1531
        #
1532
        builder = self.get_builder()
1533
        builder.build_snapshot('A-id', None,
1534
            [('add', (u'', 'a-root-id', 'directory', None)),
1535
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1536
        builder.build_snapshot('C-id', ['A-id'], [])
1537
        builder.build_snapshot('B-id', ['A-id'],
1538
            [('rename', ('foo', 'bar'))])
1539
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1540
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1541
            [('rename', ('foo', 'bing'))]) # override to bing
1542
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1543
            [('rename', ('bing', 'barry'))]) # override to barry
1544
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1545
            [('rename', ('bar', 'bing'))]) # Merge in E's change
1546
        merge_obj = self.make_merge_obj(builder, 'G-id')
1547
1548
        self.expectFailure("We don't do an actual heads() check on lca values,"
1549
            " or use the per-attribute graph",
1550
            self.assertEqual, [], list(merge_obj._entries_lca()))
1551
1552
    def test_one_lca_accidentally_pruned(self):
1553
        # Another incorrect resolution from the same basic flaw:
1554
        #   A    value 'foo'
1555
        #   |\
1556
        #   B C  B value 'bar', C = 'foo'
1557
        #   |X|
1558
        #   D E  D = 'bar', E reverts to 'foo'
1559
        #   |X|
1560
        #   F G  F = 'bing', G switches to 'bar'
1561
        #
1562
        # 'bar' will not be seen as an interesting change, because 'foo' will
1563
        # be pruned from the LCAs, even though it was newly introduced by E
1564
        # (superseding B).
1565
        builder = self.get_builder()
1566
        builder.build_snapshot('A-id', None,
1567
            [('add', (u'', 'a-root-id', 'directory', None)),
1568
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1569
        builder.build_snapshot('C-id', ['A-id'], [])
1570
        builder.build_snapshot('B-id', ['A-id'],
1571
            [('rename', ('foo', 'bar'))])
1572
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1573
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1574
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1575
            [('rename', ('foo', 'bar'))])
1576
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1577
            [('rename', ('bar', 'bing'))]) # should end up conflicting
1578
        merge_obj = self.make_merge_obj(builder, 'G-id')
1579
1580
        entries = list(merge_obj._entries_lca())
1581
        root_id = 'a-root-id'
1582
        self.expectFailure("We prune values from BASE even when relevant.",
1583
            self.assertEqual,
1584
                [('foo-id', False,
1585
                  ((root_id, [root_id, root_id]), root_id, root_id),
1586
                  ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1587
                  ((False, [False, False]), False, False)),
1588
                ], entries)
1589
3514.4.30 by John Arbash Meinel
Several updates.
1590
    def test_both_sides_revert(self):
1591
        # Both sides of a criss-cross revert the text to the lca
1592
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1593
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1594
        #       B C  B modifies 'foo', C modifies 'foo'
1595
        #       |X|
1596
        #       D E  D reverts to B, E reverts to C
1597
        # This should conflict
1598
        builder = self.get_builder()
1599
        builder.build_snapshot('A-id', None,
1600
            [('add', (u'', 'a-root-id', 'directory', None)),
1601
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1602
        builder.build_snapshot('B-id', ['A-id'],
1603
            [('modify', ('foo-id', 'B content\n'))])
1604
        builder.build_snapshot('C-id', ['A-id'],
1605
            [('modify', ('foo-id', 'C content\n'))])
1606
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1607
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1608
        merge_obj = self.make_merge_obj(builder, 'E-id')
1609
1610
        entries = list(merge_obj._entries_lca())
1611
        root_id = 'a-root-id'
1612
        self.assertEqual([('foo-id', True,
1613
                           ((root_id, [root_id, root_id]), root_id, root_id),
1614
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1615
                           ((False, [False, False]), False, False)),
1616
                         ], entries)
1617
1618
    def test_different_lca_resolve_one_side_updates_content(self):
1619
        # Both sides converge, but then one side updates the text.
1620
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1621
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1622
        #       B C  B modifies 'foo', C modifies 'foo'
1623
        #       |X|
1624
        #       D E  D reverts to B, E reverts to C
1625
        #       |
1626
        #       F    F updates to a new value
1627
        # We need to emit an entry for 'foo', because D & E differed on the
1628
        # merge resolution
1629
        builder = self.get_builder()
1630
        builder.build_snapshot('A-id', None,
1631
            [('add', (u'', 'a-root-id', 'directory', None)),
1632
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1633
        builder.build_snapshot('B-id', ['A-id'],
1634
            [('modify', ('foo-id', 'B content\n'))])
1635
        builder.build_snapshot('C-id', ['A-id'],
1636
            [('modify', ('foo-id', 'C content\n'))])
1637
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1638
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1639
        builder.build_snapshot('F-id', ['D-id'],
1640
            [('modify', ('foo-id', 'F content\n'))])
1641
        merge_obj = self.make_merge_obj(builder, 'E-id')
1642
1643
        entries = list(merge_obj._entries_lca())
1644
        root_id = 'a-root-id'
1645
        self.assertEqual([('foo-id', True,
1646
                           ((root_id, [root_id, root_id]), root_id, root_id),
1647
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1648
                           ((False, [False, False]), False, False)),
1649
                         ], entries)
1650
1651
    def test_same_lca_resolution_one_side_updates_content(self):
1652
        # Both sides converge, but then one side updates the text.
1653
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1654
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1655
        #       B C  B modifies 'foo', C modifies 'foo'
1656
        #       |X|
1657
        #       D E  D and E use C's value
1658
        #       |
1659
        #       F    F updates to a new value
1660
        # I think it is a bug that this conflicts, but we don't have a way to
1661
        # detect otherwise. And because of:
1662
        #   test_different_lca_resolve_one_side_updates_content
1663
        # We need to conflict.
1664
1665
        builder = self.get_builder()
1666
        builder.build_snapshot('A-id', None,
1667
            [('add', (u'', 'a-root-id', 'directory', None)),
1668
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1669
        builder.build_snapshot('B-id', ['A-id'],
1670
            [('modify', ('foo-id', 'B content\n'))])
1671
        builder.build_snapshot('C-id', ['A-id'],
1672
            [('modify', ('foo-id', 'C content\n'))])
1673
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1674
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1675
            [('modify', ('foo-id', 'C content\n'))]) # Same as E
1676
        builder.build_snapshot('F-id', ['D-id'],
1677
            [('modify', ('foo-id', 'F content\n'))])
1678
        merge_obj = self.make_merge_obj(builder, 'E-id')
1679
1680
        entries = list(merge_obj._entries_lca())
1681
        root_id = 'a-root-id'
1682
        self.expectFailure("We don't detect that LCA resolution was the"
1683
                           " same on both sides",
1684
            self.assertEqual, [], entries)
1685
3514.4.20 by John Arbash Meinel
Use the _lca_multi_way to work out if there is actually a kind/parent/name/content change.
1686
    def test_only_path_changed(self):
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
1687
        builder = self.get_builder()
1688
        builder.build_snapshot('A-id', None,
1689
            [('add', (u'', 'a-root-id', 'directory', None)),
1690
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1691
        builder.build_snapshot('B-id', ['A-id'], [])
1692
        builder.build_snapshot('C-id', ['A-id'], [])
1693
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1694
            [('rename', (u'a', u'b'))])
1695
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1696
        merge_obj = self.make_merge_obj(builder, 'E-id')
1697
        entries = list(merge_obj._entries_lca())
1698
        root_id = 'a-root-id'
1699
        # The content was not changed, only the path
1700
        self.assertEqual([('a-id', False,
1701
                           ((root_id, [root_id, root_id]), root_id, root_id),
1702
                           ((u'a', [u'a', u'a']), u'b', u'a'),
1703
                           ((False, [False, False]), False, False)),
1704
                         ], entries)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1705
1706
    def test_kind_changed(self):
1707
        # Identical content, except 'D' changes a-id into a directory
1708
        builder = self.get_builder()
1709
        builder.build_snapshot('A-id', None,
1710
            [('add', (u'', 'a-root-id', 'directory', None)),
1711
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1712
        builder.build_snapshot('B-id', ['A-id'], [])
1713
        builder.build_snapshot('C-id', ['A-id'], [])
1714
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1715
            [('unversion', 'a-id'),
1716
             ('add', (u'a', 'a-id', 'directory', None))])
1717
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1718
        merge_obj = self.make_merge_obj(builder, 'E-id')
1719
        entries = list(merge_obj._entries_lca())
1720
        root_id = 'a-root-id'
1721
        # Only the kind was changed (content)
1722
        self.assertEqual([('a-id', True,
1723
                           ((root_id, [root_id, root_id]), root_id, root_id),
1724
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1725
                           ((False, [False, False]), False, False)),
1726
                         ], entries)
1727
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
1728
    def test_this_changed_kind(self):
1729
        # Identical content, but THIS changes a file to a directory
1730
        builder = self.get_builder()
1731
        builder.build_snapshot('A-id', None,
1732
            [('add', (u'', 'a-root-id', 'directory', None)),
1733
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1734
        builder.build_snapshot('B-id', ['A-id'], [])
1735
        builder.build_snapshot('C-id', ['A-id'], [])
1736
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1737
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1738
            [('unversion', 'a-id'),
1739
             ('add', (u'a', 'a-id', 'directory', None))])
1740
        merge_obj = self.make_merge_obj(builder, 'E-id')
1741
        entries = list(merge_obj._entries_lca())
1742
        root_id = 'a-root-id'
1743
        # Only the kind was changed (content)
1744
        self.assertEqual([], entries)
1745
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1746
    def test_interesting_files(self):
1747
        # Two files modified, but we should filter one of them
1748
        builder = self.get_builder()
1749
        builder.build_snapshot('A-id', None,
1750
            [('add', (u'', 'a-root-id', 'directory', None)),
1751
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1752
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1753
        builder.build_snapshot('B-id', ['A-id'], [])
1754
        builder.build_snapshot('C-id', ['A-id'], [])
1755
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1756
            [('modify', ('a-id', 'new-content\n')),
1757
             ('modify', ('b-id', 'new-content\n'))])
1758
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1759
        merge_obj = self.make_merge_obj(builder, 'E-id',
1760
                                        interesting_files=['b'])
1761
        entries = list(merge_obj._entries_lca())
1762
        root_id = 'a-root-id'
1763
        self.assertEqual([('b-id', True,
1764
                           ((root_id, [root_id, root_id]), root_id, root_id),
1765
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1766
                           ((False, [False, False]), False, False)),
1767
                         ], entries)
1768
1769
    def test_interesting_file_in_this(self):
1770
        # This renamed the file, but it should still match the entry in other
1771
        builder = self.get_builder()
1772
        builder.build_snapshot('A-id', None,
1773
            [('add', (u'', 'a-root-id', 'directory', None)),
1774
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1775
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1776
        builder.build_snapshot('B-id', ['A-id'], [])
1777
        builder.build_snapshot('C-id', ['A-id'], [])
1778
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1779
            [('modify', ('a-id', 'new-content\n')),
1780
             ('modify', ('b-id', 'new-content\n'))])
1781
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1782
            [('rename', ('b', 'c'))])
1783
        merge_obj = self.make_merge_obj(builder, 'E-id',
1784
                                        interesting_files=['c'])
1785
        entries = list(merge_obj._entries_lca())
1786
        root_id = 'a-root-id'
1787
        self.assertEqual([('b-id', True,
1788
                           ((root_id, [root_id, root_id]), root_id, root_id),
1789
                           ((u'b', [u'b', u'b']), u'b', u'c'),
1790
                           ((False, [False, False]), False, False)),
1791
                         ], entries)
1792
1793
    def test_interesting_file_in_base(self):
1794
        # This renamed the file, but it should still match the entry in BASE
1795
        builder = self.get_builder()
1796
        builder.build_snapshot('A-id', None,
1797
            [('add', (u'', 'a-root-id', 'directory', None)),
1798
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1799
             ('add', (u'c', 'c-id', 'file', 'content\n'))])
1800
        builder.build_snapshot('B-id', ['A-id'],
1801
            [('rename', ('c', 'b'))])
1802
        builder.build_snapshot('C-id', ['A-id'],
1803
            [('rename', ('c', 'b'))])
1804
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1805
            [('modify', ('a-id', 'new-content\n')),
1806
             ('modify', ('c-id', 'new-content\n'))])
1807
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1808
        merge_obj = self.make_merge_obj(builder, 'E-id',
1809
                                        interesting_files=['c'])
1810
        entries = list(merge_obj._entries_lca())
1811
        root_id = 'a-root-id'
1812
        self.assertEqual([('c-id', True,
1813
                           ((root_id, [root_id, root_id]), root_id, root_id),
1814
                           ((u'c', [u'b', u'b']), u'b', u'b'),
1815
                           ((False, [False, False]), False, False)),
1816
                         ], entries)
1817
1818
    def test_interesting_file_in_lca(self):
1819
        # This renamed the file, but it should still match the entry in LCA
1820
        builder = self.get_builder()
1821
        builder.build_snapshot('A-id', None,
1822
            [('add', (u'', 'a-root-id', 'directory', None)),
1823
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1824
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1825
        builder.build_snapshot('B-id', ['A-id'],
1826
            [('rename', ('b', 'c'))])
1827
        builder.build_snapshot('C-id', ['A-id'], [])
1828
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1829
            [('modify', ('a-id', 'new-content\n')),
1830
             ('modify', ('b-id', 'new-content\n'))])
1831
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1832
            [('rename', ('c', 'b'))])
1833
        merge_obj = self.make_merge_obj(builder, 'E-id',
1834
                                        interesting_files=['c'])
1835
        entries = list(merge_obj._entries_lca())
1836
        root_id = 'a-root-id'
1837
        self.assertEqual([('b-id', True,
1838
                           ((root_id, [root_id, root_id]), root_id, root_id),
1839
                           ((u'b', [u'c', u'b']), u'b', u'b'),
1840
                           ((False, [False, False]), False, False)),
1841
                         ], entries)
1842
1843
    def test_interesting_ids(self):
1844
        # Two files modified, but we should filter one of them
1845
        builder = self.get_builder()
1846
        builder.build_snapshot('A-id', None,
1847
            [('add', (u'', 'a-root-id', 'directory', None)),
1848
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1849
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1850
        builder.build_snapshot('B-id', ['A-id'], [])
1851
        builder.build_snapshot('C-id', ['A-id'], [])
1852
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1853
            [('modify', ('a-id', 'new-content\n')),
1854
             ('modify', ('b-id', 'new-content\n'))])
1855
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1856
        merge_obj = self.make_merge_obj(builder, 'E-id',
1857
                                        interesting_ids=['b-id'])
1858
        entries = list(merge_obj._entries_lca())
1859
        root_id = 'a-root-id'
1860
        self.assertEqual([('b-id', True,
1861
                           ((root_id, [root_id, root_id]), root_id, root_id),
1862
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1863
                           ((False, [False, False]), False, False)),
1864
                         ], entries)
1865
1866
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1867
1868
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
1869
1870
    def get_builder(self):
1871
        builder = self.make_branch_builder('path')
1872
        builder.start_series()
1873
        self.addCleanup(builder.finish_series)
1874
        return builder
1875
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1876
    def get_wt_from_builder(self, builder):
1877
        """Get a real WorkingTree from the builder."""
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1878
        the_branch = builder.get_branch()
1879
        wt = the_branch.bzrdir.create_workingtree()
3514.4.31 by John Arbash Meinel
Add expected failures for cases where we should be looking at more than
1880
        # Note: This is a little bit ugly, but we are holding the branch
1881
        #       write-locked as part of the build process, and we would like to
1882
        #       maintain that. So we just force the WT to re-use the same
1883
        #       branch object.
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1884
        wt._branch = the_branch
1885
        wt.lock_write()
1886
        self.addCleanup(wt.unlock)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1887
        return wt
1888
1889
    def do_merge(self, builder, other_revision_id):
1890
        wt = self.get_wt_from_builder(builder)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1891
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
1892
            wt, other_revision_id)
1893
        merger.merge_type = _mod_merge.Merge3Merger
1894
        return wt, merger.do_merge()
1895
1896
    def test_simple_lca(self):
1897
        builder = self.get_builder()
1898
        builder.build_snapshot('A-id', None,
1899
            [('add', (u'', 'a-root-id', 'directory', None)),
1900
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1901
        builder.build_snapshot('C-id', ['A-id'], [])
1902
        builder.build_snapshot('B-id', ['A-id'], [])
1903
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1904
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1905
            [('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
1906
        wt, conflicts = self.do_merge(builder, 'E-id')
1907
        self.assertEqual(0, conflicts)
1908
        # The merge should have simply update the contents of 'a'
1909
        self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
1910
1911
    def test_conflict_without_lca(self):
1912
        # This test would cause a merge conflict, unless we use the lca trees
1913
        # to determine the real ancestry
1914
        #   A       Path at 'foo'
1915
        #  / \
1916
        # B   C     Path renamed to 'bar' in B
1917
        # |\ /|
1918
        # | X |
1919
        # |/ \|
1920
        # D   E     Path at 'bar' in D and E
1921
        #     |
1922
        #     F     Path at 'baz' in F, which supersedes 'bar' and 'foo'
1923
        builder = self.get_builder()
1924
        builder.build_snapshot('A-id', None,
1925
            [('add', (u'', 'a-root-id', 'directory', None)),
1926
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1927
        builder.build_snapshot('C-id', ['A-id'], [])
1928
        builder.build_snapshot('B-id', ['A-id'],
1929
            [('rename', ('foo', 'bar'))])
1930
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
1931
            [('rename', ('foo', 'bar'))])
1932
        builder.build_snapshot('F-id', ['E-id'],
1933
            [('rename', ('bar', 'baz'))])
1934
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1935
        wt, conflicts = self.do_merge(builder, 'F-id')
1936
        self.assertEqual(0, conflicts)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1937
        # The merge should simply recognize that the final rename takes
1938
        # precedence
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1939
        self.assertEqual('baz', wt.id2path('foo-id'))
1940
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1941
    def test_other_deletes_lca_renames(self):
1942
        # This test would cause a merge conflict, unless we use the lca trees
1943
        # to determine the real ancestry
1944
        #   A       Path at 'foo'
1945
        #  / \
1946
        # B   C     Path renamed to 'bar' in B
1947
        # |\ /|
1948
        # | X |
1949
        # |/ \|
1950
        # D   E     Path at 'bar' in D and E
1951
        #     |
1952
        #     F     F deletes 'bar'
1953
        builder = self.get_builder()
1954
        builder.build_snapshot('A-id', None,
1955
            [('add', (u'', 'a-root-id', 'directory', None)),
1956
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1957
        builder.build_snapshot('C-id', ['A-id'], [])
1958
        builder.build_snapshot('B-id', ['A-id'],
1959
            [('rename', ('foo', 'bar'))])
1960
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
1961
            [('rename', ('foo', 'bar'))])
1962
        builder.build_snapshot('F-id', ['E-id'],
1963
            [('unversion', 'foo-id')])
1964
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1965
        wt, conflicts = self.do_merge(builder, 'F-id')
1966
        self.assertEqual(0, conflicts)
1967
        self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
1968
1969
    def test_executable_changes(self):
1970
        #   A       Path at 'foo'
1971
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1972
        # B   C
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1973
        # |\ /|
1974
        # | X |
1975
        # |/ \|
1976
        # D   E
1977
        #     |
1978
        #     F     Executable bit changed
1979
        builder = self.get_builder()
1980
        builder.build_snapshot('A-id', None,
1981
            [('add', (u'', 'a-root-id', 'directory', None)),
1982
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1983
        builder.build_snapshot('C-id', ['A-id'], [])
1984
        builder.build_snapshot('B-id', ['A-id'], [])
1985
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1986
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1987
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
1988
        wt = self.get_wt_from_builder(builder)
1989
        tt = transform.TreeTransform(wt)
1990
        try:
1991
            tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
1992
            tt.apply()
1993
        except:
1994
            tt.finalize()
1995
            raise
1996
        self.assertTrue(wt.is_executable('foo-id'))
1997
        wt.commit('F-id', rev_id='F-id')
1998
        # Reset to D, so that we can merge F
1999
        wt.set_parent_ids(['D-id'])
2000
        wt.branch.set_last_revision_info(3, 'D-id')
2001
        wt.revert()
2002
        self.assertFalse(wt.is_executable('foo-id'))
2003
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2004
        self.assertEqual(0, conflicts)
2005
        self.assertTrue(wt.is_executable('foo-id'))
2006
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2007
    def test_create_symlink(self):
2008
        self.requireFeature(tests.SymlinkFeature)
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2009
        #   A
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2010
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2011
        # B   C
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2012
        # |\ /|
2013
        # | X |
2014
        # |/ \|
2015
        # D   E
2016
        #     |
2017
        #     F     Add a symlink 'foo' => 'bar'
2018
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2019
        # have symlink support
2020
        builder = self.get_builder()
2021
        builder.build_snapshot('A-id', None,
2022
            [('add', (u'', 'a-root-id', 'directory', None))])
2023
        builder.build_snapshot('C-id', ['A-id'], [])
2024
        builder.build_snapshot('B-id', ['A-id'], [])
2025
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2026
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2027
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2028
        wt = self.get_wt_from_builder(builder)
2029
        os.symlink('bar', 'path/foo')
2030
        wt.add(['foo'], ['foo-id'])
2031
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2032
        wt.commit('add symlink', rev_id='F-id')
2033
        # Reset to D, so that we can merge F
2034
        wt.set_parent_ids(['D-id'])
2035
        wt.branch.set_last_revision_info(3, 'D-id')
2036
        wt.revert()
2037
        self.assertIs(None, wt.path2id('foo'))
2038
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2039
        self.assertEqual(0, conflicts)
2040
        self.assertEqual('foo-id', wt.path2id('foo'))
2041
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2042
3514.4.30 by John Arbash Meinel
Several updates.
2043
    def test_both_sides_revert(self):
2044
        # Both sides of a criss-cross revert the text to the lca
2045
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2046
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
2047
        #       B C  B modifies 'foo', C modifies 'foo'
2048
        #       |X|
2049
        #       D E  D reverts to B, E reverts to C
2050
        # This should conflict
2051
        # This must be done with a real WorkingTree, because normally their
2052
        # inventory contains "None" rather than a real sha1
2053
        builder = self.get_builder()
2054
        builder.build_snapshot('A-id', None,
2055
            [('add', (u'', 'a-root-id', 'directory', None)),
2056
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
2057
        builder.build_snapshot('B-id', ['A-id'],
2058
            [('modify', ('foo-id', 'B content\n'))])
2059
        builder.build_snapshot('C-id', ['A-id'],
2060
            [('modify', ('foo-id', 'C content\n'))])
2061
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2062
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2063
        wt, conflicts = self.do_merge(builder, 'E-id')
2064
        self.assertEqual(1, conflicts)
2065
        self.assertEqualDiff('<<<<<<< TREE\n'
2066
                             'B content\n'
2067
                             '=======\n'
2068
                             'C content\n'
2069
                             '>>>>>>> MERGE-SOURCE\n',
2070
                             wt.get_file_text('foo-id'))
2071
3514.4.28 by John Arbash Meinel
More symlink tests.
2072
    def test_modified_symlink(self):
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2073
        self.requireFeature(tests.SymlinkFeature)
2074
        #   A       Create symlink foo => bar
2075
        #  / \
2076
        # B   C     B relinks foo => baz
2077
        # |\ /|
2078
        # | X |
2079
        # |/ \|
2080
        # D   E     D & E have foo => baz
2081
        #     |
2082
        #     F     F changes it to bing
2083
        #
2084
        # Merging D & F should result in F cleanly overriding D, because D's
3514.4.34 by John Arbash Meinel
Handle symlinks properly when objects are not RevisionTrees
2085
        # value actually comes from B
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2086
2087
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2088
        # have symlink support
2089
        wt = self.make_branch_and_tree('path')
2090
        wt.lock_write()
2091
        self.addCleanup(wt.unlock)
2092
        os.symlink('bar', 'path/foo')
2093
        wt.add(['foo'], ['foo-id'])
2094
        wt.commit('add symlink', rev_id='A-id')
2095
        os.remove('path/foo')
2096
        os.symlink('baz', 'path/foo')
2097
        wt.commit('foo => baz', rev_id='B-id')
2098
        wt.set_last_revision('A-id')
2099
        wt.branch.set_last_revision_info(1, 'A-id')
2100
        wt.revert()
2101
        wt.commit('C', rev_id='C-id')
2102
        wt.merge_from_branch(wt.branch, 'B-id')
2103
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2104
        wt.commit('E merges C & B', rev_id='E-id')
2105
        os.remove('path/foo')
2106
        os.symlink('bing', 'path/foo')
2107
        wt.commit('F foo => bing', rev_id='F-id')
2108
        wt.set_last_revision('B-id')
2109
        wt.branch.set_last_revision_info(2, 'B-id')
2110
        wt.revert()
2111
        wt.merge_from_branch(wt.branch, 'C-id')
2112
        wt.commit('D merges B & C', rev_id='D-id')
2113
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2114
        self.expectFailure("Merge3Merger doesn't use lcas for symlink content",
2115
            self.assertEqual, 0, conflicts)
2116
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2117
3514.4.28 by John Arbash Meinel
More symlink tests.
2118
    def test_renamed_symlink(self):
2119
        self.requireFeature(tests.SymlinkFeature)
2120
        #   A       Create symlink foo => bar
2121
        #  / \
2122
        # B   C     B renames foo => barry
2123
        # |\ /|
2124
        # | X |
2125
        # |/ \|
2126
        # D   E     D & E have barry
2127
        #     |
2128
        #     F     F renames barry to blah
2129
        #
2130
        # Merging D & F should result in F cleanly overriding D, because D's
3514.4.34 by John Arbash Meinel
Handle symlinks properly when objects are not RevisionTrees
2131
        # value actually comes from B
3514.4.28 by John Arbash Meinel
More symlink tests.
2132
2133
        wt = self.make_branch_and_tree('path')
2134
        wt.lock_write()
2135
        self.addCleanup(wt.unlock)
2136
        os.symlink('bar', 'path/foo')
2137
        wt.add(['foo'], ['foo-id'])
2138
        wt.commit('A add symlink', rev_id='A-id')
2139
        wt.rename_one('foo', 'barry')
2140
        wt.commit('B foo => barry', rev_id='B-id')
2141
        wt.set_last_revision('A-id')
2142
        wt.branch.set_last_revision_info(1, 'A-id')
2143
        wt.revert()
2144
        wt.commit('C', rev_id='C-id')
2145
        wt.merge_from_branch(wt.branch, 'B-id')
2146
        self.assertEqual('barry', wt.id2path('foo-id'))
2147
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2148
        wt.commit('E merges C & B', rev_id='E-id')
2149
        wt.rename_one('barry', 'blah')
2150
        wt.commit('F barry => blah', rev_id='F-id')
2151
        wt.set_last_revision('B-id')
2152
        wt.branch.set_last_revision_info(2, 'B-id')
2153
        wt.revert()
2154
        wt.merge_from_branch(wt.branch, 'C-id')
2155
        wt.commit('D merges B & C', rev_id='D-id')
2156
        self.assertEqual('barry', wt.id2path('foo-id'))
2157
        # Check the output of the Merger object directly
2158
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2159
            wt, 'F-id')
2160
        merger.merge_type = _mod_merge.Merge3Merger
2161
        merge_obj = merger.make_merger()
2162
        root_id = wt.path2id('')
2163
        entries = list(merge_obj._entries_lca())
2164
        # No content change, just a path change
2165
        self.assertEqual([('foo-id', False,
2166
                           ((root_id, [root_id, root_id]), root_id, root_id),
2167
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2168
                           ((False, [False, False]), False, False)),
2169
                         ], entries)
2170
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2171
        self.assertEqual(0, conflicts)
2172
        self.assertEqual('blah', wt.id2path('foo-id'))
2173
2174
    def test_symlink_no_content_change(self):
2175
        self.requireFeature(tests.SymlinkFeature)
2176
        #   A       Create symlink foo => bar
2177
        #  / \
2178
        # B   C     B relinks foo => baz
2179
        # |\ /|
2180
        # | X |
2181
        # |/ \|
2182
        # D   E     D & E have foo => baz
2183
        # |
2184
        # F         F has foo => bing
2185
        #
2186
        # Merging E into F should not cause a conflict, because E doesn't have
2187
        # a content change relative to the LCAs (it does relative to A)
2188
        wt = self.make_branch_and_tree('path')
2189
        wt.lock_write()
2190
        self.addCleanup(wt.unlock)
2191
        os.symlink('bar', 'path/foo')
2192
        wt.add(['foo'], ['foo-id'])
2193
        wt.commit('add symlink', rev_id='A-id')
2194
        os.remove('path/foo')
2195
        os.symlink('baz', 'path/foo')
2196
        wt.commit('foo => baz', rev_id='B-id')
2197
        wt.set_last_revision('A-id')
2198
        wt.branch.set_last_revision_info(1, 'A-id')
2199
        wt.revert()
2200
        wt.commit('C', rev_id='C-id')
2201
        wt.merge_from_branch(wt.branch, 'B-id')
2202
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2203
        wt.commit('E merges C & B', rev_id='E-id')
2204
        wt.set_last_revision('B-id')
2205
        wt.branch.set_last_revision_info(2, 'B-id')
2206
        wt.revert()
2207
        wt.merge_from_branch(wt.branch, 'C-id')
2208
        wt.commit('D merges B & C', rev_id='D-id')
2209
        os.remove('path/foo')
2210
        os.symlink('bing', 'path/foo')
2211
        wt.commit('F foo => bing', rev_id='F-id')
2212
2213
        # Check the output of the Merger object directly
2214
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2215
            wt, 'E-id')
2216
        merger.merge_type = _mod_merge.Merge3Merger
2217
        merge_obj = merger.make_merger()
2218
        # Nothing interesting happened in OTHER relative to BASE
2219
        self.assertEqual([], list(merge_obj._entries_lca()))
2220
        # Now do a real merge, just to test the rest of the stack
2221
        conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2222
        self.assertEqual(0, conflicts)
2223
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2224
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
2225
    def test_symlink_this_changed_kind(self):
2226
        self.requireFeature(tests.SymlinkFeature)
2227
        #   A       Nothing
2228
        #  / \
2229
        # B   C     B creates symlink foo => bar
2230
        # |\ /|
2231
        # | X |
2232
        # |/ \|
2233
        # D   E     D changes foo into a file, E has foo => bing
2234
        #
2235
        # Mostly, this is trying to test that we don't try to os.readlink() on
2236
        # a file, or when there is nothing there
2237
        wt = self.make_branch_and_tree('path')
2238
        wt.lock_write()
2239
        self.addCleanup(wt.unlock)
2240
        wt.commit('base', rev_id='A-id')
2241
        os.symlink('bar', 'path/foo')
2242
        wt.add(['foo'], ['foo-id'])
2243
        wt.commit('add symlink foo => bar', rev_id='B-id')
2244
        wt.set_last_revision('A-id')
2245
        wt.branch.set_last_revision_info(1, 'A-id')
2246
        wt.revert()
2247
        wt.commit('C', rev_id='C-id')
2248
        wt.merge_from_branch(wt.branch, 'B-id')
2249
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2250
        os.remove('path/foo')
2251
        # We have to change the link in E, or it won't try to do a comparison
2252
        os.symlink('bing', 'path/foo')
2253
        wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2254
        wt.set_last_revision('B-id')
2255
        wt.branch.set_last_revision_info(2, 'B-id')
2256
        wt.revert()
2257
        wt.merge_from_branch(wt.branch, 'C-id')
2258
        os.remove('path/foo')
2259
        self.build_tree_contents([('path/foo', 'file content\n')])
2260
        # XXX: workaround, WT doesn't detect kind changes unless you do
2261
        # iter_changes()
2262
        list(wt.iter_changes(wt.basis_tree()))
2263
        wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2264
2265
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2266
            wt, 'E-id')
2267
        merger.merge_type = _mod_merge.Merge3Merger
2268
        merge_obj = merger.make_merger()
2269
        entries = list(merge_obj._entries_lca())
2270
        root_id = wt.path2id('')
2271
        self.assertEqual([('foo-id', True,
2272
                           ((None, [root_id, None]), root_id, root_id),
2273
                           ((None, [u'foo', None]), u'foo', u'foo'),
2274
                           ((None, [False, None]), False, False)),
2275
                         ], entries)
2276
3514.4.34 by John Arbash Meinel
Handle symlinks properly when objects are not RevisionTrees
2277
    def test_symlink_all_wt(self):
2278
        """Check behavior if all trees are Working Trees."""
2279
        self.requireFeature(tests.SymlinkFeature)
2280
        # The big issue is that entry.symlink_target is None for WorkingTrees.
2281
        # So we need to make sure we handle that case correctly.
2282
        #   A   foo => bar
2283
        #   |\
2284
        #   B C B relinks foo => baz
2285
        #   |X|
2286
        #   D E D & E have foo => baz
2287
        #     |
2288
        #     F F changes it to bing
2289
        # Merging D & F should result in F cleanly overriding D, because D's
2290
        # value actually comes from B
2291
2292
        wt = self.make_branch_and_tree('path')
2293
        wt.lock_write()
2294
        self.addCleanup(wt.unlock)
2295
        os.symlink('bar', 'path/foo')
2296
        wt.add(['foo'], ['foo-id'])
2297
        wt.commit('add symlink', rev_id='A-id')
2298
        os.remove('path/foo')
2299
        os.symlink('baz', 'path/foo')
2300
        wt.commit('foo => baz', rev_id='B-id')
2301
        wt.set_last_revision('A-id')
2302
        wt.branch.set_last_revision_info(1, 'A-id')
2303
        wt.revert()
2304
        wt.commit('C', rev_id='C-id')
2305
        wt.merge_from_branch(wt.branch, 'B-id')
2306
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2307
        wt.commit('E merges C & B', rev_id='E-id')
2308
        os.remove('path/foo')
2309
        os.symlink('bing', 'path/foo')
2310
        wt.commit('F foo => bing', rev_id='F-id')
2311
        wt.set_last_revision('B-id')
2312
        wt.branch.set_last_revision_info(2, 'B-id')
2313
        wt.revert()
2314
        wt.merge_from_branch(wt.branch, 'C-id')
2315
        wt.commit('D merges B & C', rev_id='D-id')
2316
        wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2317
        wt_base.lock_read()
2318
        self.addCleanup(wt_base.unlock)
2319
        wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2320
        wt_lca1.lock_read()
2321
        self.addCleanup(wt_lca1.unlock)
2322
        wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2323
        wt_lca2.lock_read()
2324
        self.addCleanup(wt_lca2.unlock)
2325
        wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2326
        wt_other.lock_read()
2327
        self.addCleanup(wt_other.unlock)
2328
        merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2329
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2330
        entries = list(merge_obj._entries_lca())
2331
        root_id = wt.path2id('')
2332
        self.assertEqual([('foo-id', True,
2333
                           ((root_id, [root_id, root_id]), root_id, root_id),
2334
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2335
                           ((False, [False, False]), False, False)),
2336
                         ], entries)
2337
3514.4.25 by John Arbash Meinel
A few more merge-level behavior tests.
2338
    def test_other_reverted_path_to_base(self):
2339
        #   A       Path at 'foo'
2340
        #  / \
2341
        # B   C     Path at 'bar' in B
2342
        # |\ /|
2343
        # | X |
2344
        # |/ \|
2345
        # D   E     Path at 'bar'
2346
        #     |
2347
        #     F     Path at 'foo'
2348
        builder = self.get_builder()
2349
        builder.build_snapshot('A-id', None,
2350
            [('add', (u'', 'a-root-id', 'directory', None)),
2351
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2352
        builder.build_snapshot('C-id', ['A-id'], [])
2353
        builder.build_snapshot('B-id', ['A-id'],
2354
            [('rename', ('foo', 'bar'))])
2355
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2356
            [('rename', ('foo', 'bar'))]) # merge the rename
2357
        builder.build_snapshot('F-id', ['E-id'],
2358
            [('rename', ('bar', 'foo'))]) # Rename back to BASE
2359
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2360
        wt, conflicts = self.do_merge(builder, 'F-id')
2361
        self.assertEqual(0, conflicts)
2362
        self.assertEqual('foo', wt.id2path('foo-id'))
2363
2364
    def test_other_reverted_content_to_base(self):
2365
        builder = self.get_builder()
2366
        builder.build_snapshot('A-id', None,
2367
            [('add', (u'', 'a-root-id', 'directory', None)),
2368
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2369
        builder.build_snapshot('C-id', ['A-id'], [])
2370
        builder.build_snapshot('B-id', ['A-id'],
2371
            [('modify', ('foo-id', 'B content\n'))])
2372
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2373
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2374
        builder.build_snapshot('F-id', ['E-id'],
2375
            [('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2376
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2377
        wt, conflicts = self.do_merge(builder, 'F-id')
2378
        self.assertEqual(0, conflicts)
2379
        # TODO: We need to use the per-file graph to properly select a BASE
3514.4.26 by John Arbash Meinel
Clean up comments, only symlink support left.
2380
        #       before this will work. Or at least use the LCA trees to find
2381
        #       the appropriate content base. (which is B, not A).
3514.4.25 by John Arbash Meinel
A few more merge-level behavior tests.
2382
        self.expectFailure("Merge3Merger doesn't recognize reverted content",
2383
            self.assertEqual, 'base content\n', wt.get_file_text('foo-id'))
2384
3514.4.28 by John Arbash Meinel
More symlink tests.
2385
    def test_other_modified_content(self):
2386
        builder = self.get_builder()
2387
        builder.build_snapshot('A-id', None,
2388
            [('add', (u'', 'a-root-id', 'directory', None)),
2389
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2390
        builder.build_snapshot('C-id', ['A-id'], [])
2391
        builder.build_snapshot('B-id', ['A-id'],
2392
            [('modify', ('foo-id', 'B content\n'))])
2393
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2394
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2395
        builder.build_snapshot('F-id', ['E-id'],
2396
            [('modify', ('foo-id', 'F content\n'))]) # Override B content
2397
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2398
        wt, conflicts = self.do_merge(builder, 'F-id')
2399
        self.expectFailure("Merge3Merger only uses BASE for content",
2400
            self.assertEqual, 'F content\n', wt.get_file_text('foo-id'))
2401
        self.assertEqual(0, conflicts)
2402
        self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2403
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2404
    def test_all_wt(self):
2405
        """Check behavior if all trees are Working Trees."""
2406
        # The big issue is that entry.revision is None for WorkingTrees. (as is
2407
        # entry.text_sha1, etc. So we need to make sure we handle that case
2408
        # correctly.
2409
        #   A   Content of 'foo', path of 'a'
2410
        #   |\
2411
        #   B C B modifies content, C renames 'a' => 'b'
2412
        #   |X|
2413
        #   D E E updates content, renames 'b' => 'c'
2414
        builder = self.get_builder()
2415
        builder.build_snapshot('A-id', None,
2416
            [('add', (u'', 'a-root-id', 'directory', None)),
2417
             ('add', (u'a', 'a-id', 'file', 'base content\n')),
2418
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2419
        builder.build_snapshot('B-id', ['A-id'],
2420
            [('modify', ('foo-id', 'B content\n'))])
2421
        builder.build_snapshot('C-id', ['A-id'],
2422
            [('rename', ('a', 'b'))])
2423
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2424
            [('rename', ('b', 'c')),
2425
             ('modify', ('foo-id', 'E content\n'))])
2426
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
2427
            [('rename', ('a', 'b'))]) # merged change
2428
        wt_this = self.get_wt_from_builder(builder)
2429
        wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2430
        wt_base.lock_read()
2431
        self.addCleanup(wt_base.unlock)
2432
        wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2433
        wt_lca1.lock_read()
2434
        self.addCleanup(wt_lca1.unlock)
2435
        wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2436
        wt_lca2.lock_read()
2437
        self.addCleanup(wt_lca2.unlock)
2438
        wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2439
        wt_other.lock_read()
2440
        self.addCleanup(wt_other.unlock)
2441
        merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2442
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2443
        entries = list(merge_obj._entries_lca())
2444
        root_id = 'a-root-id'
2445
        self.assertEqual([('a-id', False,
2446
                           ((root_id, [root_id, root_id]), root_id, root_id),
2447
                           ((u'a', [u'a', u'b']), u'c', u'b'),
2448
                           ((False, [False, False]), False, False)),
2449
                          ('foo-id', True,
2450
                           ((root_id, [root_id, root_id]), root_id, root_id),
2451
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2452
                           ((False, [False, False]), False, False)),
2453
                         ], entries)
2454
3514.4.33 by John Arbash Meinel
Implement support for 'tree-reference'.
2455
    def test_nested_tree_unmodified(self):
2456
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2457
        # 'tree-reference'
2458
        wt = self.make_branch_and_tree('tree',
2459
            format='dirstate-with-subtree')
2460
        wt.lock_write()
2461
        self.addCleanup(wt.unlock)
2462
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2463
            format='dirstate-with-subtree')
2464
        wt.set_root_id('a-root-id')
2465
        sub_tree.set_root_id('sub-tree-root')
2466
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2467
        sub_tree.add('file')
2468
        sub_tree.commit('foo', rev_id='sub-A-id')
2469
        wt.add_reference(sub_tree)
2470
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2471
        # Now create a criss-cross merge in the parent, without modifying the
2472
        # subtree
2473
        wt.commit('B', rev_id='B-id', recursive=None)
2474
        wt.set_last_revision('A-id')
2475
        wt.branch.set_last_revision_info(1, 'A-id')
2476
        wt.commit('C', rev_id='C-id', recursive=None)
2477
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2478
        wt.commit('E', rev_id='E-id', recursive=None)
2479
        wt.set_parent_ids(['B-id', 'C-id'])
2480
        wt.branch.set_last_revision_info(2, 'B-id')
2481
        wt.commit('D', rev_id='D-id', recursive=None)
2482
2483
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2484
            wt, 'E-id')
2485
        merger.merge_type = _mod_merge.Merge3Merger
2486
        merge_obj = merger.make_merger()
2487
        entries = list(merge_obj._entries_lca())
2488
        self.assertEqual([], entries)
2489
2490
    def test_nested_tree_subtree_modified(self):
2491
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2492
        # 'tree-reference'
2493
        wt = self.make_branch_and_tree('tree',
2494
            format='dirstate-with-subtree')
2495
        wt.lock_write()
2496
        self.addCleanup(wt.unlock)
2497
        sub_tree = self.make_branch_and_tree('tree/sub',
2498
            format='dirstate-with-subtree')
2499
        wt.set_root_id('a-root-id')
2500
        sub_tree.set_root_id('sub-tree-root')
2501
        self.build_tree_contents([('tree/sub/file', 'text1')])
2502
        sub_tree.add('file')
2503
        sub_tree.commit('foo', rev_id='sub-A-id')
2504
        wt.add_reference(sub_tree)
2505
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2506
        # Now create a criss-cross merge in the parent, without modifying the
2507
        # subtree
2508
        wt.commit('B', rev_id='B-id', recursive=None)
2509
        wt.set_last_revision('A-id')
2510
        wt.branch.set_last_revision_info(1, 'A-id')
2511
        wt.commit('C', rev_id='C-id', recursive=None)
2512
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2513
        self.build_tree_contents([('tree/sub/file', 'text2')])
2514
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2515
        wt.commit('E', rev_id='E-id', recursive=None)
2516
        wt.set_parent_ids(['B-id', 'C-id'])
2517
        wt.branch.set_last_revision_info(2, 'B-id')
2518
        wt.commit('D', rev_id='D-id', recursive=None)
2519
2520
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2521
            wt, 'E-id')
2522
        merger.merge_type = _mod_merge.Merge3Merger
2523
        merge_obj = merger.make_merger()
2524
        entries = list(merge_obj._entries_lca())
2525
        # Nothing interesting about this sub-tree, because content changes are
2526
        # computed at a higher level
2527
        self.assertEqual([], entries)
2528
2529
    def test_nested_tree_subtree_renamed(self):
2530
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2531
        # 'tree-reference'
2532
        wt = self.make_branch_and_tree('tree',
2533
            format='dirstate-with-subtree')
2534
        wt.lock_write()
2535
        self.addCleanup(wt.unlock)
2536
        sub_tree = self.make_branch_and_tree('tree/sub',
2537
            format='dirstate-with-subtree')
2538
        wt.set_root_id('a-root-id')
2539
        sub_tree.set_root_id('sub-tree-root')
2540
        self.build_tree_contents([('tree/sub/file', 'text1')])
2541
        sub_tree.add('file')
2542
        sub_tree.commit('foo', rev_id='sub-A-id')
2543
        wt.add_reference(sub_tree)
2544
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2545
        # Now create a criss-cross merge in the parent, without modifying the
2546
        # subtree
2547
        wt.commit('B', rev_id='B-id', recursive=None)
2548
        wt.set_last_revision('A-id')
2549
        wt.branch.set_last_revision_info(1, 'A-id')
2550
        wt.commit('C', rev_id='C-id', recursive=None)
2551
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2552
        wt.rename_one('sub', 'alt_sub')
2553
        wt.commit('E', rev_id='E-id', recursive=None)
2554
        wt.set_last_revision('B-id')
2555
        wt.revert()
2556
        wt.set_parent_ids(['B-id', 'C-id'])
2557
        wt.branch.set_last_revision_info(2, 'B-id')
2558
        wt.commit('D', rev_id='D-id', recursive=None)
2559
2560
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2561
            wt, 'E-id')
2562
        merger.merge_type = _mod_merge.Merge3Merger
2563
        merge_obj = merger.make_merger()
2564
        entries = list(merge_obj._entries_lca())
2565
        root_id = 'a-root-id'
2566
        self.assertEqual([('sub-tree-root', False,
2567
                           ((root_id, [root_id, root_id]), root_id, root_id),
2568
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2569
                           ((False, [False, False]), False, False)),
2570
                         ], entries)
2571
2572
    def test_nested_tree_subtree_renamed_and_modified(self):
2573
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2574
        # 'tree-reference'
2575
        wt = self.make_branch_and_tree('tree',
2576
            format='dirstate-with-subtree')
2577
        wt.lock_write()
2578
        self.addCleanup(wt.unlock)
2579
        sub_tree = self.make_branch_and_tree('tree/sub',
2580
            format='dirstate-with-subtree')
2581
        wt.set_root_id('a-root-id')
2582
        sub_tree.set_root_id('sub-tree-root')
2583
        self.build_tree_contents([('tree/sub/file', 'text1')])
2584
        sub_tree.add('file')
2585
        sub_tree.commit('foo', rev_id='sub-A-id')
2586
        wt.add_reference(sub_tree)
2587
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2588
        # Now create a criss-cross merge in the parent, without modifying the
2589
        # subtree
2590
        wt.commit('B', rev_id='B-id', recursive=None)
2591
        wt.set_last_revision('A-id')
2592
        wt.branch.set_last_revision_info(1, 'A-id')
2593
        wt.commit('C', rev_id='C-id', recursive=None)
2594
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2595
        self.build_tree_contents([('tree/sub/file', 'text2')])
2596
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2597
        wt.rename_one('sub', 'alt_sub')
2598
        wt.commit('E', rev_id='E-id', recursive=None)
2599
        wt.set_last_revision('B-id')
2600
        wt.revert()
2601
        wt.set_parent_ids(['B-id', 'C-id'])
2602
        wt.branch.set_last_revision_info(2, 'B-id')
2603
        wt.commit('D', rev_id='D-id', recursive=None)
2604
2605
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2606
            wt, 'E-id')
2607
        merger.merge_type = _mod_merge.Merge3Merger
2608
        merge_obj = merger.make_merger()
2609
        entries = list(merge_obj._entries_lca())
2610
        root_id = 'a-root-id'
2611
        self.assertEqual([('sub-tree-root', False,
2612
                           ((root_id, [root_id, root_id]), root_id, root_id),
2613
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2614
                           ((False, [False, False]), False, False)),
2615
                         ], entries)
2616
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2617
2618
class TestLCAMultiWay(tests.TestCase):
2619
3514.4.30 by John Arbash Meinel
Several updates.
2620
    def assertLCAMultiWay(self, expected, base, lcas, other, this,
2621
                          allow_overriding_lca=True):
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2622
        self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
3514.4.30 by John Arbash Meinel
Several updates.
2623
                                (base, lcas), other, this,
2624
                                allow_overriding_lca=allow_overriding_lca))
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2625
2626
    def test_other_equal_equal_lcas(self):
2627
        """Test when OTHER=LCA and all LCAs are identical."""
2628
        self.assertLCAMultiWay('this',
2629
            'bval', ['bval', 'bval'], 'bval', 'bval')
2630
        self.assertLCAMultiWay('this',
2631
            'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2632
        self.assertLCAMultiWay('this',
2633
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2634
        self.assertLCAMultiWay('this',
2635
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2636
        self.assertLCAMultiWay('this',
2637
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2638
2639
    def test_other_equal_this(self):
2640
        """Test when other and this are identical."""
2641
        self.assertLCAMultiWay('this',
2642
            'bval', ['bval', 'bval'], 'oval', 'oval')
2643
        self.assertLCAMultiWay('this',
2644
            'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2645
        self.assertLCAMultiWay('this',
2646
            'bval', ['cval', 'dval'], 'oval', 'oval')
2647
        self.assertLCAMultiWay('this',
2648
            'bval', [None, 'lcaval'], 'oval', 'oval')
2649
        self.assertLCAMultiWay('this',
2650
            None, [None, 'lcaval'], 'oval', 'oval')
2651
        self.assertLCAMultiWay('this',
2652
            None, ['lcaval', 'lcaval'], 'oval', 'oval')
2653
        self.assertLCAMultiWay('this',
2654
            None, ['cval', 'dval'], 'oval', 'oval')
2655
        self.assertLCAMultiWay('this',
2656
            None, ['cval', 'dval'], None, None)
2657
        self.assertLCAMultiWay('this',
2658
            None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2659
2660
    def test_no_lcas(self):
2661
        self.assertLCAMultiWay('this',
2662
            'bval', [], 'bval', 'tval')
2663
        self.assertLCAMultiWay('other',
2664
            'bval', [], 'oval', 'bval')
2665
        self.assertLCAMultiWay('conflict',
2666
            'bval', [], 'oval', 'tval')
2667
        self.assertLCAMultiWay('this',
2668
            'bval', [], 'oval', 'oval')
2669
2670
    def test_lca_supersedes_other_lca(self):
2671
        """If one lca == base, the other lca takes precedence"""
2672
        self.assertLCAMultiWay('this',
2673
            'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2674
        self.assertLCAMultiWay('this',
2675
            'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2676
        # This is actually considered a 'revert' because the 'lcaval' in LCAS
2677
        # supersedes the BASE val (in the other LCA) but then OTHER reverts it
2678
        # back to bval.
2679
        self.assertLCAMultiWay('other',
2680
            'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2681
        self.assertLCAMultiWay('conflict',
2682
            'bval', ['bval', 'lcaval'], 'bval', 'tval')
2683
2684
    def test_other_and_this_pick_different_lca(self):
2685
        # OTHER and THIS resolve the lca conflict in different ways
2686
        self.assertLCAMultiWay('conflict',
2687
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2688
        self.assertLCAMultiWay('conflict',
2689
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2690
        self.assertLCAMultiWay('conflict',
2691
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2692
2693
    def test_other_in_lca(self):
2694
        # OTHER takes a value of one of the LCAs, THIS takes a new value, which
2695
        # theoretically supersedes both LCA values and 'wins'
2696
        self.assertLCAMultiWay('this',
2697
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2698
        self.assertLCAMultiWay('this',
2699
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
3514.4.30 by John Arbash Meinel
Several updates.
2700
        self.assertLCAMultiWay('conflict',
2701
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2702
            allow_overriding_lca=False)
2703
        self.assertLCAMultiWay('conflict',
2704
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2705
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2706
        # THIS reverted back to BASE, but that is an explicit supersede of all
2707
        # LCAs
2708
        self.assertLCAMultiWay('this',
2709
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2710
        self.assertLCAMultiWay('this',
2711
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3514.4.30 by John Arbash Meinel
Several updates.
2712
        self.assertLCAMultiWay('conflict',
2713
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2714
            allow_overriding_lca=False)
2715
        self.assertLCAMultiWay('conflict',
2716
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2717
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2718
2719
    def test_this_in_lca(self):
2720
        # THIS takes a value of one of the LCAs, OTHER takes a new value, which
2721
        # theoretically supersedes both LCA values and 'wins'
2722
        self.assertLCAMultiWay('other',
2723
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2724
        self.assertLCAMultiWay('other',
2725
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3514.4.30 by John Arbash Meinel
Several updates.
2726
        self.assertLCAMultiWay('conflict',
2727
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2728
            allow_overriding_lca=False)
2729
        self.assertLCAMultiWay('conflict',
2730
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2731
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2732
        # OTHER reverted back to BASE, but that is an explicit supersede of all
2733
        # LCAs
2734
        self.assertLCAMultiWay('other',
2735
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
3514.4.30 by John Arbash Meinel
Several updates.
2736
        self.assertLCAMultiWay('conflict',
2737
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2738
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2739
2740
    def test_all_differ(self):
2741
        self.assertLCAMultiWay('conflict',
2742
            'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2743
        self.assertLCAMultiWay('conflict',
2744
            'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2745
        self.assertLCAMultiWay('conflict',
2746
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')