~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())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
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)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
99
        # merge all of branch 2 into branch 1 even though they
1390 by Robert Collins
pair programming worx... merge integration and weave
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()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
153
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
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()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
158
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
1551.2.23 by Aaron Bentley
Got merge_inner's ignore_zero parameter working
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')])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
251
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
252
    def test_merge_type_registry(self):
253
        merge_type_option = option.Option.OPTIONS['merge-type']
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
254
        self.assertFalse('merge4' in [x[0] for x in
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
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')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
259
        self.assertTrue('merge4' in [x[0] for x in
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
260
                        merge_type_option.iter_switches()])
261
        registry.remove('merge4')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
262
        self.assertFalse('merge4' in [x[0] for x in
2221.4.16 by Aaron Bentley
Add tests for get_merge_type_registry
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.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
721
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
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())
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.
1384
        self.assertEqual([], entries)
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1385
3514.4.15 by John Arbash Meinel
Handle when OTHER doesn't have the entry.
1386
    def test_not_in_other(self):
1387
        builder = self.get_builder()
1388
        builder.build_snapshot('A-id', None,
1389
            [('add', (u'', 'a-root-id', 'directory', None)),
1390
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1391
        builder.build_snapshot('B-id', ['A-id'], [])
1392
        builder.build_snapshot('C-id', ['A-id'], [])
1393
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1394
            [('unversion', 'a-id')])
1395
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1396
        merge_obj = self.make_merge_obj(builder, 'E-id')
1397
1398
        entries = list(merge_obj._entries_lca())
1399
        root_id = 'a-root-id'
1400
        self.assertEqual([('a-id', True,
1401
                           ((root_id, [root_id, root_id]), None, root_id),
1402
                           ((u'a', [u'a', u'a']), None, u'a'),
1403
                           ((False, [False, False]), None, False)),
1404
                         ], entries)
1405
3514.4.30 by John Arbash Meinel
Several updates.
1406
    def test_not_in_other_or_lca(self):
1407
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1408
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1409
        #       B C  B nothing, C deletes foo
1410
        #       |X|
1411
        #       D E  D restores foo (same as B), E leaves it deleted
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1412
        # Analysis:
1413
        #   A => B, no changes
1414
        #   A => C, delete foo (C should supersede B)
1415
        #   C => D, restore foo
1416
        #   C => E, no changes
1417
        # D would then win 'cleanly' and no record would be given
3514.4.30 by John Arbash Meinel
Several updates.
1418
        builder = self.get_builder()
1419
        builder.build_snapshot('A-id', None,
1420
            [('add', (u'', 'a-root-id', 'directory', None)),
1421
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1422
        builder.build_snapshot('B-id', ['A-id'], [])
1423
        builder.build_snapshot('C-id', ['A-id'],
1424
            [('unversion', 'foo-id')])
1425
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1426
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1427
        merge_obj = self.make_merge_obj(builder, 'E-id')
1428
1429
        entries = list(merge_obj._entries_lca())
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1430
        self.assertEqual([], entries)
1431
1432
    def test_not_in_other_mod_in_lca1_not_in_lca2(self):
1433
        #       A    base, introduces 'foo'
1434
        #       |\
1435
        #       B C  B changes 'foo', C deletes foo
1436
        #       |X|
1437
        #       D E  D restores foo (same as B), E leaves it deleted (as C)
1438
        # Analysis:
1439
        #   A => B, modified foo
1440
        #   A => C, delete foo, C does not supersede B
1441
        #   B => D, no changes
1442
        #   C => D, resolve in favor of B
1443
        #   B => E, resolve in favor of E
1444
        #   C => E, no changes
1445
        # In this case, we have a conflict of how the changes were resolved. E
1446
        # picked C and D picked B, so we should issue a conflict
1447
        builder = self.get_builder()
1448
        builder.build_snapshot('A-id', None,
1449
            [('add', (u'', 'a-root-id', 'directory', None)),
1450
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1451
        builder.build_snapshot('B-id', ['A-id'], [
1452
            ('modify', ('foo-id', 'new-content\n'))])
1453
        builder.build_snapshot('C-id', ['A-id'],
1454
            [('unversion', 'foo-id')])
1455
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1456
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1457
        merge_obj = self.make_merge_obj(builder, 'E-id')
1458
1459
        entries = list(merge_obj._entries_lca())
3948.1.4 by Vincent Ladeuil
More cleanup and fix overzealous previous one.
1460
        root_id = 'a-root-id'
3514.4.30 by John Arbash Meinel
Several updates.
1461
        self.assertEqual([('foo-id', True,
1462
                           ((root_id, [root_id, None]), None, root_id),
1463
                           ((u'foo', [u'foo', None]), None, 'foo'),
1464
                           ((False, [False, None]), None, False)),
1465
                         ], entries)
1466
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.
1467
    def test_only_in_one_lca(self):
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1468
        #   A   add only root
1469
        #   |\
1470
        #   B C B nothing, C add file
1471
        #   |X|
1472
        #   D E D still has nothing, E removes file
1473
        # Analysis:
1474
        #   B => D, no change
1475
        #   C => D, removed the file
1476
        #   B => E, no change
1477
        #   C => E, removed the file
1478
        # Thus D & E have identical changes, and this is a no-op
1479
        # Alternatively:
1480
        #   A => B, no change
1481
        #   A => C, add file, thus C supersedes B
1482
        #   w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
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.
1483
        builder = self.get_builder()
1484
        builder.build_snapshot('A-id', None,
1485
            [('add', (u'', 'a-root-id', 'directory', None))])
1486
        builder.build_snapshot('B-id', ['A-id'], [])
1487
        builder.build_snapshot('C-id', ['A-id'],
1488
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1489
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1490
            [('unversion', 'a-id')])
1491
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1492
        merge_obj = self.make_merge_obj(builder, 'E-id')
1493
1494
        entries = list(merge_obj._entries_lca())
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1495
        self.assertEqual([], entries)
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.
1496
3514.4.16 by John Arbash Meinel
another test for only in OTHER
1497
    def test_only_in_other(self):
1498
        builder = self.get_builder()
1499
        builder.build_snapshot('A-id', None,
1500
            [('add', (u'', 'a-root-id', 'directory', None))])
1501
        builder.build_snapshot('B-id', ['A-id'], [])
1502
        builder.build_snapshot('C-id', ['A-id'], [])
1503
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1504
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1505
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1506
        merge_obj = self.make_merge_obj(builder, 'E-id')
1507
1508
        entries = list(merge_obj._entries_lca())
1509
        root_id = 'a-root-id'
1510
        self.assertEqual([('a-id', True,
1511
                           ((None, [None, None]), root_id, None),
1512
                           ((None, [None, None]), u'a', None),
1513
                           ((None, [None, None]), False, None)),
1514
                         ], entries)
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
1515
3514.4.30 by John Arbash Meinel
Several updates.
1516
    def test_one_lca_supersedes(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1517
        # One LCA supersedes the other LCAs last modified value, but the
3514.4.30 by John Arbash Meinel
Several updates.
1518
        # value is not the same as BASE.
1519
        #       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.
1520
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1521
        #       B C  B modifies 'foo' (mod B), C does nothing (mod A)
1522
        #       |X|
1523
        #       D E  D does nothing (mod B), E updates 'foo' (mod E)
1524
        #       |X|
1525
        #       F G  F updates 'foo' (mod F). G does nothing (mod E)
1526
        #
1527
        #   At this point, G should not be considered to modify 'foo', even
1528
        #   though its LCAs disagree. This is because the modification in E
1529
        #   completely supersedes the value in D.
1530
        builder = self.get_builder()
1531
        builder.build_snapshot('A-id', None,
1532
            [('add', (u'', 'a-root-id', 'directory', None)),
1533
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1534
        builder.build_snapshot('C-id', ['A-id'], [])
1535
        builder.build_snapshot('B-id', ['A-id'],
1536
            [('modify', ('foo-id', 'B content\n'))])
1537
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1538
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1539
            [('modify', ('foo-id', 'E content\n'))])
1540
        builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
1541
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1542
            [('modify', ('foo-id', 'F content\n'))])
1543
        merge_obj = self.make_merge_obj(builder, 'G-id')
1544
1545
        self.assertEqual([], list(merge_obj._entries_lca()))
1546
3514.4.31 by John Arbash Meinel
Add expected failures for cases where we should be looking at more than
1547
    def test_one_lca_supersedes_path(self):
1548
        # Double-criss-cross merge, the ultimate base value is different from
1549
        # the intermediate.
1550
        #   A    value 'foo'
1551
        #   |\
1552
        #   B C  B value 'bar', C = 'foo'
1553
        #   |X|
1554
        #   D E  D = 'bar', E supersedes to 'bing'
1555
        #   |X|
1556
        #   F G  F = 'bing', G supersedes to 'barry'
1557
        #
1558
        # In this case, we technically should not care about the value 'bar' for
1559
        # D, because it was clearly superseded by E's 'bing'. The
1560
        # per-file/attribute graph would actually look like:
1561
        #   A
1562
        #   |
1563
        #   B
1564
        #   |
1565
        #   E
1566
        #   |
1567
        #   G
1568
        #
1569
        # Because the other side of the merge never modifies the value, it just
1570
        # takes the value from the merge.
1571
        #
1572
        # ATM this fails because we will prune 'foo' from the LCAs, but we
1573
        # won't prune 'bar'. This is getting far off into edge-case land, so we
1574
        # aren't supporting it yet.
1575
        #
1576
        builder = self.get_builder()
1577
        builder.build_snapshot('A-id', None,
1578
            [('add', (u'', 'a-root-id', 'directory', None)),
1579
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1580
        builder.build_snapshot('C-id', ['A-id'], [])
1581
        builder.build_snapshot('B-id', ['A-id'],
1582
            [('rename', ('foo', 'bar'))])
1583
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1584
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1585
            [('rename', ('foo', 'bing'))]) # override to bing
1586
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1587
            [('rename', ('bing', 'barry'))]) # override to barry
1588
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1589
            [('rename', ('bar', 'bing'))]) # Merge in E's change
1590
        merge_obj = self.make_merge_obj(builder, 'G-id')
1591
1592
        self.expectFailure("We don't do an actual heads() check on lca values,"
1593
            " or use the per-attribute graph",
1594
            self.assertEqual, [], list(merge_obj._entries_lca()))
1595
1596
    def test_one_lca_accidentally_pruned(self):
1597
        # Another incorrect resolution from the same basic flaw:
1598
        #   A    value 'foo'
1599
        #   |\
1600
        #   B C  B value 'bar', C = 'foo'
1601
        #   |X|
1602
        #   D E  D = 'bar', E reverts to 'foo'
1603
        #   |X|
1604
        #   F G  F = 'bing', G switches to 'bar'
1605
        #
1606
        # 'bar' will not be seen as an interesting change, because 'foo' will
1607
        # be pruned from the LCAs, even though it was newly introduced by E
1608
        # (superseding B).
1609
        builder = self.get_builder()
1610
        builder.build_snapshot('A-id', None,
1611
            [('add', (u'', 'a-root-id', 'directory', None)),
1612
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1613
        builder.build_snapshot('C-id', ['A-id'], [])
1614
        builder.build_snapshot('B-id', ['A-id'],
1615
            [('rename', ('foo', 'bar'))])
1616
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1617
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1618
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1619
            [('rename', ('foo', 'bar'))])
1620
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1621
            [('rename', ('bar', 'bing'))]) # should end up conflicting
1622
        merge_obj = self.make_merge_obj(builder, 'G-id')
1623
1624
        entries = list(merge_obj._entries_lca())
1625
        root_id = 'a-root-id'
1626
        self.expectFailure("We prune values from BASE even when relevant.",
1627
            self.assertEqual,
1628
                [('foo-id', False,
1629
                  ((root_id, [root_id, root_id]), root_id, root_id),
1630
                  ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1631
                  ((False, [False, False]), False, False)),
1632
                ], entries)
1633
3514.4.30 by John Arbash Meinel
Several updates.
1634
    def test_both_sides_revert(self):
1635
        # Both sides of a criss-cross revert the text to the lca
1636
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1637
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1638
        #       B C  B modifies 'foo', C modifies 'foo'
1639
        #       |X|
1640
        #       D E  D reverts to B, E reverts to C
1641
        # This should conflict
1642
        builder = self.get_builder()
1643
        builder.build_snapshot('A-id', None,
1644
            [('add', (u'', 'a-root-id', 'directory', None)),
1645
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1646
        builder.build_snapshot('B-id', ['A-id'],
1647
            [('modify', ('foo-id', 'B content\n'))])
1648
        builder.build_snapshot('C-id', ['A-id'],
1649
            [('modify', ('foo-id', 'C content\n'))])
1650
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1651
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1652
        merge_obj = self.make_merge_obj(builder, 'E-id')
1653
1654
        entries = list(merge_obj._entries_lca())
1655
        root_id = 'a-root-id'
1656
        self.assertEqual([('foo-id', True,
1657
                           ((root_id, [root_id, root_id]), root_id, root_id),
1658
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1659
                           ((False, [False, False]), False, False)),
1660
                         ], entries)
1661
1662
    def test_different_lca_resolve_one_side_updates_content(self):
1663
        # Both sides converge, but then one side updates the text.
1664
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1665
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1666
        #       B C  B modifies 'foo', C modifies 'foo'
1667
        #       |X|
1668
        #       D E  D reverts to B, E reverts to C
1669
        #       |
1670
        #       F    F updates to a new value
1671
        # We need to emit an entry for 'foo', because D & E differed on the
1672
        # merge resolution
1673
        builder = self.get_builder()
1674
        builder.build_snapshot('A-id', None,
1675
            [('add', (u'', 'a-root-id', 'directory', None)),
1676
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1677
        builder.build_snapshot('B-id', ['A-id'],
1678
            [('modify', ('foo-id', 'B content\n'))])
1679
        builder.build_snapshot('C-id', ['A-id'],
1680
            [('modify', ('foo-id', 'C content\n'))])
1681
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1682
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1683
        builder.build_snapshot('F-id', ['D-id'],
1684
            [('modify', ('foo-id', 'F content\n'))])
1685
        merge_obj = self.make_merge_obj(builder, 'E-id')
1686
1687
        entries = list(merge_obj._entries_lca())
1688
        root_id = 'a-root-id'
1689
        self.assertEqual([('foo-id', True,
1690
                           ((root_id, [root_id, root_id]), root_id, root_id),
1691
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1692
                           ((False, [False, False]), False, False)),
1693
                         ], entries)
1694
1695
    def test_same_lca_resolution_one_side_updates_content(self):
1696
        # Both sides converge, but then one side updates the text.
1697
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1698
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1699
        #       B C  B modifies 'foo', C modifies 'foo'
1700
        #       |X|
1701
        #       D E  D and E use C's value
1702
        #       |
1703
        #       F    F updates to a new value
1704
        # I think it is a bug that this conflicts, but we don't have a way to
1705
        # detect otherwise. And because of:
1706
        #   test_different_lca_resolve_one_side_updates_content
1707
        # We need to conflict.
1708
1709
        builder = self.get_builder()
1710
        builder.build_snapshot('A-id', None,
1711
            [('add', (u'', 'a-root-id', 'directory', None)),
1712
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1713
        builder.build_snapshot('B-id', ['A-id'],
1714
            [('modify', ('foo-id', 'B content\n'))])
1715
        builder.build_snapshot('C-id', ['A-id'],
1716
            [('modify', ('foo-id', 'C content\n'))])
1717
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1718
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1719
            [('modify', ('foo-id', 'C content\n'))]) # Same as E
1720
        builder.build_snapshot('F-id', ['D-id'],
1721
            [('modify', ('foo-id', 'F content\n'))])
1722
        merge_obj = self.make_merge_obj(builder, 'E-id')
1723
1724
        entries = list(merge_obj._entries_lca())
1725
        self.expectFailure("We don't detect that LCA resolution was the"
1726
                           " same on both sides",
1727
            self.assertEqual, [], entries)
1728
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.
1729
    def test_only_path_changed(self):
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
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
            [('rename', (u'a', u'b'))])
1738
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1739
        merge_obj = self.make_merge_obj(builder, 'E-id')
1740
        entries = list(merge_obj._entries_lca())
1741
        root_id = 'a-root-id'
1742
        # The content was not changed, only the path
1743
        self.assertEqual([('a-id', False,
1744
                           ((root_id, [root_id, root_id]), root_id, root_id),
1745
                           ((u'a', [u'a', u'a']), u'b', u'a'),
1746
                           ((False, [False, False]), False, False)),
1747
                         ], entries)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1748
1749
    def test_kind_changed(self):
1750
        # Identical content, except 'D' changes a-id into a directory
1751
        builder = self.get_builder()
1752
        builder.build_snapshot('A-id', None,
1753
            [('add', (u'', 'a-root-id', 'directory', None)),
1754
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1755
        builder.build_snapshot('B-id', ['A-id'], [])
1756
        builder.build_snapshot('C-id', ['A-id'], [])
1757
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1758
            [('unversion', 'a-id'),
1759
             ('add', (u'a', 'a-id', 'directory', None))])
1760
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1761
        merge_obj = self.make_merge_obj(builder, 'E-id')
1762
        entries = list(merge_obj._entries_lca())
1763
        root_id = 'a-root-id'
1764
        # Only the kind was changed (content)
1765
        self.assertEqual([('a-id', True,
1766
                           ((root_id, [root_id, root_id]), root_id, root_id),
1767
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1768
                           ((False, [False, False]), False, False)),
1769
                         ], entries)
1770
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
1771
    def test_this_changed_kind(self):
1772
        # Identical content, but THIS changes a file to a directory
1773
        builder = self.get_builder()
1774
        builder.build_snapshot('A-id', None,
1775
            [('add', (u'', 'a-root-id', 'directory', None)),
1776
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1777
        builder.build_snapshot('B-id', ['A-id'], [])
1778
        builder.build_snapshot('C-id', ['A-id'], [])
1779
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1780
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1781
            [('unversion', 'a-id'),
1782
             ('add', (u'a', 'a-id', 'directory', None))])
1783
        merge_obj = self.make_merge_obj(builder, 'E-id')
1784
        entries = list(merge_obj._entries_lca())
1785
        # Only the kind was changed (content)
1786
        self.assertEqual([], entries)
1787
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1788
    def test_interesting_files(self):
1789
        # Two files modified, but we should filter one of them
1790
        builder = self.get_builder()
1791
        builder.build_snapshot('A-id', None,
1792
            [('add', (u'', 'a-root-id', 'directory', None)),
1793
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1794
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1795
        builder.build_snapshot('B-id', ['A-id'], [])
1796
        builder.build_snapshot('C-id', ['A-id'], [])
1797
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1798
            [('modify', ('a-id', 'new-content\n')),
1799
             ('modify', ('b-id', 'new-content\n'))])
1800
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1801
        merge_obj = self.make_merge_obj(builder, 'E-id',
1802
                                        interesting_files=['b'])
1803
        entries = list(merge_obj._entries_lca())
1804
        root_id = 'a-root-id'
1805
        self.assertEqual([('b-id', True,
1806
                           ((root_id, [root_id, root_id]), root_id, root_id),
1807
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1808
                           ((False, [False, False]), False, False)),
1809
                         ], entries)
1810
1811
    def test_interesting_file_in_this(self):
1812
        # This renamed the file, but it should still match the entry in other
1813
        builder = self.get_builder()
1814
        builder.build_snapshot('A-id', None,
1815
            [('add', (u'', 'a-root-id', 'directory', None)),
1816
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1817
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1818
        builder.build_snapshot('B-id', ['A-id'], [])
1819
        builder.build_snapshot('C-id', ['A-id'], [])
1820
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1821
            [('modify', ('a-id', 'new-content\n')),
1822
             ('modify', ('b-id', 'new-content\n'))])
1823
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1824
            [('rename', ('b', 'c'))])
1825
        merge_obj = self.make_merge_obj(builder, 'E-id',
1826
                                        interesting_files=['c'])
1827
        entries = list(merge_obj._entries_lca())
1828
        root_id = 'a-root-id'
1829
        self.assertEqual([('b-id', True,
1830
                           ((root_id, [root_id, root_id]), root_id, root_id),
1831
                           ((u'b', [u'b', u'b']), u'b', u'c'),
1832
                           ((False, [False, False]), False, False)),
1833
                         ], entries)
1834
1835
    def test_interesting_file_in_base(self):
1836
        # This renamed the file, but it should still match the entry in BASE
1837
        builder = self.get_builder()
1838
        builder.build_snapshot('A-id', None,
1839
            [('add', (u'', 'a-root-id', 'directory', None)),
1840
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1841
             ('add', (u'c', 'c-id', 'file', 'content\n'))])
1842
        builder.build_snapshot('B-id', ['A-id'],
1843
            [('rename', ('c', 'b'))])
1844
        builder.build_snapshot('C-id', ['A-id'],
1845
            [('rename', ('c', 'b'))])
1846
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1847
            [('modify', ('a-id', 'new-content\n')),
1848
             ('modify', ('c-id', 'new-content\n'))])
1849
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1850
        merge_obj = self.make_merge_obj(builder, 'E-id',
1851
                                        interesting_files=['c'])
1852
        entries = list(merge_obj._entries_lca())
1853
        root_id = 'a-root-id'
1854
        self.assertEqual([('c-id', True,
1855
                           ((root_id, [root_id, root_id]), root_id, root_id),
1856
                           ((u'c', [u'b', u'b']), u'b', u'b'),
1857
                           ((False, [False, False]), False, False)),
1858
                         ], entries)
1859
1860
    def test_interesting_file_in_lca(self):
1861
        # This renamed the file, but it should still match the entry in LCA
1862
        builder = self.get_builder()
1863
        builder.build_snapshot('A-id', None,
1864
            [('add', (u'', 'a-root-id', 'directory', None)),
1865
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1866
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1867
        builder.build_snapshot('B-id', ['A-id'],
1868
            [('rename', ('b', 'c'))])
1869
        builder.build_snapshot('C-id', ['A-id'], [])
1870
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1871
            [('modify', ('a-id', 'new-content\n')),
1872
             ('modify', ('b-id', 'new-content\n'))])
1873
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1874
            [('rename', ('c', 'b'))])
1875
        merge_obj = self.make_merge_obj(builder, 'E-id',
1876
                                        interesting_files=['c'])
1877
        entries = list(merge_obj._entries_lca())
1878
        root_id = 'a-root-id'
1879
        self.assertEqual([('b-id', True,
1880
                           ((root_id, [root_id, root_id]), root_id, root_id),
1881
                           ((u'b', [u'c', u'b']), u'b', u'b'),
1882
                           ((False, [False, False]), False, False)),
1883
                         ], entries)
1884
1885
    def test_interesting_ids(self):
1886
        # Two files modified, but we should filter one of them
1887
        builder = self.get_builder()
1888
        builder.build_snapshot('A-id', None,
1889
            [('add', (u'', 'a-root-id', 'directory', None)),
1890
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1891
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1892
        builder.build_snapshot('B-id', ['A-id'], [])
1893
        builder.build_snapshot('C-id', ['A-id'], [])
1894
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1895
            [('modify', ('a-id', 'new-content\n')),
1896
             ('modify', ('b-id', 'new-content\n'))])
1897
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1898
        merge_obj = self.make_merge_obj(builder, 'E-id',
1899
                                        interesting_ids=['b-id'])
1900
        entries = list(merge_obj._entries_lca())
1901
        root_id = 'a-root-id'
1902
        self.assertEqual([('b-id', True,
1903
                           ((root_id, [root_id, root_id]), root_id, root_id),
1904
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1905
                           ((False, [False, False]), False, False)),
1906
                         ], entries)
1907
1908
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1909
1910
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
1911
1912
    def get_builder(self):
1913
        builder = self.make_branch_builder('path')
1914
        builder.start_series()
1915
        self.addCleanup(builder.finish_series)
1916
        return builder
1917
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1918
    def get_wt_from_builder(self, builder):
1919
        """Get a real WorkingTree from the builder."""
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1920
        the_branch = builder.get_branch()
1921
        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
1922
        # Note: This is a little bit ugly, but we are holding the branch
1923
        #       write-locked as part of the build process, and we would like to
1924
        #       maintain that. So we just force the WT to re-use the same
1925
        #       branch object.
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1926
        wt._branch = the_branch
1927
        wt.lock_write()
1928
        self.addCleanup(wt.unlock)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1929
        return wt
1930
1931
    def do_merge(self, builder, other_revision_id):
1932
        wt = self.get_wt_from_builder(builder)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1933
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
1934
            wt, other_revision_id)
1935
        merger.merge_type = _mod_merge.Merge3Merger
1936
        return wt, merger.do_merge()
1937
1938
    def test_simple_lca(self):
1939
        builder = self.get_builder()
1940
        builder.build_snapshot('A-id', None,
1941
            [('add', (u'', 'a-root-id', 'directory', None)),
1942
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1943
        builder.build_snapshot('C-id', ['A-id'], [])
1944
        builder.build_snapshot('B-id', ['A-id'], [])
1945
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1946
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1947
            [('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
1948
        wt, conflicts = self.do_merge(builder, 'E-id')
1949
        self.assertEqual(0, conflicts)
1950
        # The merge should have simply update the contents of 'a'
1951
        self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
1952
1953
    def test_conflict_without_lca(self):
1954
        # This test would cause a merge conflict, unless we use the lca trees
1955
        # to determine the real ancestry
1956
        #   A       Path at 'foo'
1957
        #  / \
1958
        # B   C     Path renamed to 'bar' in B
1959
        # |\ /|
1960
        # | X |
1961
        # |/ \|
1962
        # D   E     Path at 'bar' in D and E
1963
        #     |
1964
        #     F     Path at 'baz' in F, which supersedes 'bar' and 'foo'
1965
        builder = self.get_builder()
1966
        builder.build_snapshot('A-id', None,
1967
            [('add', (u'', 'a-root-id', 'directory', None)),
1968
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1969
        builder.build_snapshot('C-id', ['A-id'], [])
1970
        builder.build_snapshot('B-id', ['A-id'],
1971
            [('rename', ('foo', 'bar'))])
1972
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
1973
            [('rename', ('foo', 'bar'))])
1974
        builder.build_snapshot('F-id', ['E-id'],
1975
            [('rename', ('bar', 'baz'))])
1976
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1977
        wt, conflicts = self.do_merge(builder, 'F-id')
1978
        self.assertEqual(0, conflicts)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1979
        # The merge should simply recognize that the final rename takes
1980
        # precedence
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1981
        self.assertEqual('baz', wt.id2path('foo-id'))
1982
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1983
    def test_other_deletes_lca_renames(self):
1984
        # This test would cause a merge conflict, unless we use the lca trees
1985
        # to determine the real ancestry
1986
        #   A       Path at 'foo'
1987
        #  / \
1988
        # B   C     Path renamed to 'bar' in B
1989
        # |\ /|
1990
        # | X |
1991
        # |/ \|
1992
        # D   E     Path at 'bar' in D and E
1993
        #     |
1994
        #     F     F deletes 'bar'
1995
        builder = self.get_builder()
1996
        builder.build_snapshot('A-id', None,
1997
            [('add', (u'', 'a-root-id', 'directory', None)),
1998
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1999
        builder.build_snapshot('C-id', ['A-id'], [])
2000
        builder.build_snapshot('B-id', ['A-id'],
2001
            [('rename', ('foo', 'bar'))])
2002
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2003
            [('rename', ('foo', 'bar'))])
2004
        builder.build_snapshot('F-id', ['E-id'],
2005
            [('unversion', 'foo-id')])
2006
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2007
        wt, conflicts = self.do_merge(builder, 'F-id')
2008
        self.assertEqual(0, conflicts)
2009
        self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
2010
2011
    def test_executable_changes(self):
2012
        #   A       Path at 'foo'
2013
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2014
        # B   C
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
2015
        # |\ /|
2016
        # | X |
2017
        # |/ \|
2018
        # D   E
2019
        #     |
2020
        #     F     Executable bit changed
2021
        builder = self.get_builder()
2022
        builder.build_snapshot('A-id', None,
2023
            [('add', (u'', 'a-root-id', 'directory', None)),
2024
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2025
        builder.build_snapshot('C-id', ['A-id'], [])
2026
        builder.build_snapshot('B-id', ['A-id'], [])
2027
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2028
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2029
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2030
        wt = self.get_wt_from_builder(builder)
2031
        tt = transform.TreeTransform(wt)
2032
        try:
2033
            tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2034
            tt.apply()
2035
        except:
2036
            tt.finalize()
2037
            raise
2038
        self.assertTrue(wt.is_executable('foo-id'))
2039
        wt.commit('F-id', rev_id='F-id')
2040
        # Reset to D, so that we can merge F
2041
        wt.set_parent_ids(['D-id'])
2042
        wt.branch.set_last_revision_info(3, 'D-id')
2043
        wt.revert()
2044
        self.assertFalse(wt.is_executable('foo-id'))
2045
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2046
        self.assertEqual(0, conflicts)
2047
        self.assertTrue(wt.is_executable('foo-id'))
2048
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2049
    def test_create_symlink(self):
2050
        self.requireFeature(tests.SymlinkFeature)
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2051
        #   A
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2052
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2053
        # B   C
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2054
        # |\ /|
2055
        # | X |
2056
        # |/ \|
2057
        # D   E
2058
        #     |
2059
        #     F     Add a symlink 'foo' => 'bar'
2060
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2061
        # have symlink support
2062
        builder = self.get_builder()
2063
        builder.build_snapshot('A-id', None,
2064
            [('add', (u'', 'a-root-id', 'directory', None))])
2065
        builder.build_snapshot('C-id', ['A-id'], [])
2066
        builder.build_snapshot('B-id', ['A-id'], [])
2067
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2068
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2069
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2070
        wt = self.get_wt_from_builder(builder)
2071
        os.symlink('bar', 'path/foo')
2072
        wt.add(['foo'], ['foo-id'])
2073
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2074
        wt.commit('add symlink', rev_id='F-id')
2075
        # Reset to D, so that we can merge F
2076
        wt.set_parent_ids(['D-id'])
2077
        wt.branch.set_last_revision_info(3, 'D-id')
2078
        wt.revert()
2079
        self.assertIs(None, wt.path2id('foo'))
2080
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2081
        self.assertEqual(0, conflicts)
2082
        self.assertEqual('foo-id', wt.path2id('foo'))
2083
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2084
3514.4.30 by John Arbash Meinel
Several updates.
2085
    def test_both_sides_revert(self):
2086
        # Both sides of a criss-cross revert the text to the lca
2087
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2088
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
2089
        #       B C  B modifies 'foo', C modifies 'foo'
2090
        #       |X|
2091
        #       D E  D reverts to B, E reverts to C
2092
        # This should conflict
2093
        # This must be done with a real WorkingTree, because normally their
2094
        # inventory contains "None" rather than a real sha1
2095
        builder = self.get_builder()
2096
        builder.build_snapshot('A-id', None,
2097
            [('add', (u'', 'a-root-id', 'directory', None)),
2098
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
2099
        builder.build_snapshot('B-id', ['A-id'],
2100
            [('modify', ('foo-id', 'B content\n'))])
2101
        builder.build_snapshot('C-id', ['A-id'],
2102
            [('modify', ('foo-id', 'C content\n'))])
2103
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2104
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2105
        wt, conflicts = self.do_merge(builder, 'E-id')
2106
        self.assertEqual(1, conflicts)
2107
        self.assertEqualDiff('<<<<<<< TREE\n'
2108
                             'B content\n'
2109
                             '=======\n'
2110
                             'C content\n'
2111
                             '>>>>>>> MERGE-SOURCE\n',
2112
                             wt.get_file_text('foo-id'))
2113
3514.4.28 by John Arbash Meinel
More symlink tests.
2114
    def test_modified_symlink(self):
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2115
        self.requireFeature(tests.SymlinkFeature)
2116
        #   A       Create symlink foo => bar
2117
        #  / \
2118
        # B   C     B relinks foo => baz
2119
        # |\ /|
2120
        # | X |
2121
        # |/ \|
2122
        # D   E     D & E have foo => baz
2123
        #     |
2124
        #     F     F changes it to bing
2125
        #
2126
        # 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
2127
        # value actually comes from B
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2128
2129
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2130
        # have symlink support
2131
        wt = self.make_branch_and_tree('path')
2132
        wt.lock_write()
2133
        self.addCleanup(wt.unlock)
2134
        os.symlink('bar', 'path/foo')
2135
        wt.add(['foo'], ['foo-id'])
2136
        wt.commit('add symlink', rev_id='A-id')
2137
        os.remove('path/foo')
2138
        os.symlink('baz', 'path/foo')
2139
        wt.commit('foo => baz', rev_id='B-id')
2140
        wt.set_last_revision('A-id')
2141
        wt.branch.set_last_revision_info(1, 'A-id')
2142
        wt.revert()
2143
        wt.commit('C', rev_id='C-id')
2144
        wt.merge_from_branch(wt.branch, 'B-id')
2145
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2146
        wt.commit('E merges C & B', rev_id='E-id')
2147
        os.remove('path/foo')
2148
        os.symlink('bing', 'path/foo')
2149
        wt.commit('F foo => bing', rev_id='F-id')
2150
        wt.set_last_revision('B-id')
2151
        wt.branch.set_last_revision_info(2, 'B-id')
2152
        wt.revert()
2153
        wt.merge_from_branch(wt.branch, 'C-id')
2154
        wt.commit('D merges B & C', rev_id='D-id')
2155
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
3948.1.7 by Vincent Ladeuil
Slight refactoring and test fixing.
2156
        self.assertEqual(0, conflicts)
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2157
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2158
3514.4.28 by John Arbash Meinel
More symlink tests.
2159
    def test_renamed_symlink(self):
2160
        self.requireFeature(tests.SymlinkFeature)
2161
        #   A       Create symlink foo => bar
2162
        #  / \
2163
        # B   C     B renames foo => barry
2164
        # |\ /|
2165
        # | X |
2166
        # |/ \|
2167
        # D   E     D & E have barry
2168
        #     |
2169
        #     F     F renames barry to blah
2170
        #
2171
        # 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
2172
        # value actually comes from B
3514.4.28 by John Arbash Meinel
More symlink tests.
2173
2174
        wt = self.make_branch_and_tree('path')
2175
        wt.lock_write()
2176
        self.addCleanup(wt.unlock)
2177
        os.symlink('bar', 'path/foo')
2178
        wt.add(['foo'], ['foo-id'])
2179
        wt.commit('A add symlink', rev_id='A-id')
2180
        wt.rename_one('foo', 'barry')
2181
        wt.commit('B foo => barry', rev_id='B-id')
2182
        wt.set_last_revision('A-id')
2183
        wt.branch.set_last_revision_info(1, 'A-id')
2184
        wt.revert()
2185
        wt.commit('C', rev_id='C-id')
2186
        wt.merge_from_branch(wt.branch, 'B-id')
2187
        self.assertEqual('barry', wt.id2path('foo-id'))
2188
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2189
        wt.commit('E merges C & B', rev_id='E-id')
2190
        wt.rename_one('barry', 'blah')
2191
        wt.commit('F barry => blah', rev_id='F-id')
2192
        wt.set_last_revision('B-id')
2193
        wt.branch.set_last_revision_info(2, 'B-id')
2194
        wt.revert()
2195
        wt.merge_from_branch(wt.branch, 'C-id')
2196
        wt.commit('D merges B & C', rev_id='D-id')
2197
        self.assertEqual('barry', wt.id2path('foo-id'))
2198
        # Check the output of the Merger object directly
2199
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2200
            wt, 'F-id')
2201
        merger.merge_type = _mod_merge.Merge3Merger
2202
        merge_obj = merger.make_merger()
2203
        root_id = wt.path2id('')
2204
        entries = list(merge_obj._entries_lca())
2205
        # No content change, just a path change
2206
        self.assertEqual([('foo-id', False,
2207
                           ((root_id, [root_id, root_id]), root_id, root_id),
2208
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2209
                           ((False, [False, False]), False, False)),
2210
                         ], entries)
2211
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2212
        self.assertEqual(0, conflicts)
2213
        self.assertEqual('blah', wt.id2path('foo-id'))
2214
2215
    def test_symlink_no_content_change(self):
2216
        self.requireFeature(tests.SymlinkFeature)
2217
        #   A       Create symlink foo => bar
2218
        #  / \
2219
        # B   C     B relinks foo => baz
2220
        # |\ /|
2221
        # | X |
2222
        # |/ \|
2223
        # D   E     D & E have foo => baz
2224
        # |
2225
        # F         F has foo => bing
2226
        #
2227
        # Merging E into F should not cause a conflict, because E doesn't have
2228
        # a content change relative to the LCAs (it does relative to A)
2229
        wt = self.make_branch_and_tree('path')
2230
        wt.lock_write()
2231
        self.addCleanup(wt.unlock)
2232
        os.symlink('bar', 'path/foo')
2233
        wt.add(['foo'], ['foo-id'])
2234
        wt.commit('add symlink', rev_id='A-id')
2235
        os.remove('path/foo')
2236
        os.symlink('baz', 'path/foo')
2237
        wt.commit('foo => baz', rev_id='B-id')
2238
        wt.set_last_revision('A-id')
2239
        wt.branch.set_last_revision_info(1, 'A-id')
2240
        wt.revert()
2241
        wt.commit('C', rev_id='C-id')
2242
        wt.merge_from_branch(wt.branch, 'B-id')
2243
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2244
        wt.commit('E merges C & B', rev_id='E-id')
2245
        wt.set_last_revision('B-id')
2246
        wt.branch.set_last_revision_info(2, 'B-id')
2247
        wt.revert()
2248
        wt.merge_from_branch(wt.branch, 'C-id')
2249
        wt.commit('D merges B & C', rev_id='D-id')
2250
        os.remove('path/foo')
2251
        os.symlink('bing', 'path/foo')
2252
        wt.commit('F foo => bing', rev_id='F-id')
2253
2254
        # Check the output of the Merger object directly
2255
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2256
            wt, 'E-id')
2257
        merger.merge_type = _mod_merge.Merge3Merger
2258
        merge_obj = merger.make_merger()
2259
        # Nothing interesting happened in OTHER relative to BASE
2260
        self.assertEqual([], list(merge_obj._entries_lca()))
2261
        # Now do a real merge, just to test the rest of the stack
2262
        conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2263
        self.assertEqual(0, conflicts)
2264
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2265
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
2266
    def test_symlink_this_changed_kind(self):
2267
        self.requireFeature(tests.SymlinkFeature)
2268
        #   A       Nothing
2269
        #  / \
2270
        # B   C     B creates symlink foo => bar
2271
        # |\ /|
2272
        # | X |
2273
        # |/ \|
2274
        # D   E     D changes foo into a file, E has foo => bing
2275
        #
2276
        # Mostly, this is trying to test that we don't try to os.readlink() on
2277
        # a file, or when there is nothing there
2278
        wt = self.make_branch_and_tree('path')
2279
        wt.lock_write()
2280
        self.addCleanup(wt.unlock)
2281
        wt.commit('base', rev_id='A-id')
2282
        os.symlink('bar', 'path/foo')
2283
        wt.add(['foo'], ['foo-id'])
2284
        wt.commit('add symlink foo => bar', rev_id='B-id')
2285
        wt.set_last_revision('A-id')
2286
        wt.branch.set_last_revision_info(1, 'A-id')
2287
        wt.revert()
2288
        wt.commit('C', rev_id='C-id')
2289
        wt.merge_from_branch(wt.branch, 'B-id')
2290
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2291
        os.remove('path/foo')
2292
        # We have to change the link in E, or it won't try to do a comparison
2293
        os.symlink('bing', 'path/foo')
2294
        wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2295
        wt.set_last_revision('B-id')
2296
        wt.branch.set_last_revision_info(2, 'B-id')
2297
        wt.revert()
2298
        wt.merge_from_branch(wt.branch, 'C-id')
2299
        os.remove('path/foo')
2300
        self.build_tree_contents([('path/foo', 'file content\n')])
2301
        # XXX: workaround, WT doesn't detect kind changes unless you do
2302
        # iter_changes()
2303
        list(wt.iter_changes(wt.basis_tree()))
2304
        wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2305
2306
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2307
            wt, 'E-id')
2308
        merger.merge_type = _mod_merge.Merge3Merger
2309
        merge_obj = merger.make_merger()
2310
        entries = list(merge_obj._entries_lca())
2311
        root_id = wt.path2id('')
2312
        self.assertEqual([('foo-id', True,
2313
                           ((None, [root_id, None]), root_id, root_id),
2314
                           ((None, [u'foo', None]), u'foo', u'foo'),
2315
                           ((None, [False, None]), False, False)),
2316
                         ], entries)
2317
3514.4.34 by John Arbash Meinel
Handle symlinks properly when objects are not RevisionTrees
2318
    def test_symlink_all_wt(self):
2319
        """Check behavior if all trees are Working Trees."""
2320
        self.requireFeature(tests.SymlinkFeature)
2321
        # The big issue is that entry.symlink_target is None for WorkingTrees.
2322
        # So we need to make sure we handle that case correctly.
2323
        #   A   foo => bar
2324
        #   |\
2325
        #   B C B relinks foo => baz
2326
        #   |X|
2327
        #   D E D & E have foo => baz
2328
        #     |
2329
        #     F F changes it to bing
2330
        # Merging D & F should result in F cleanly overriding D, because D's
2331
        # value actually comes from B
2332
2333
        wt = self.make_branch_and_tree('path')
2334
        wt.lock_write()
2335
        self.addCleanup(wt.unlock)
2336
        os.symlink('bar', 'path/foo')
2337
        wt.add(['foo'], ['foo-id'])
2338
        wt.commit('add symlink', rev_id='A-id')
2339
        os.remove('path/foo')
2340
        os.symlink('baz', 'path/foo')
2341
        wt.commit('foo => baz', rev_id='B-id')
2342
        wt.set_last_revision('A-id')
2343
        wt.branch.set_last_revision_info(1, 'A-id')
2344
        wt.revert()
2345
        wt.commit('C', rev_id='C-id')
2346
        wt.merge_from_branch(wt.branch, 'B-id')
2347
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2348
        wt.commit('E merges C & B', rev_id='E-id')
2349
        os.remove('path/foo')
2350
        os.symlink('bing', 'path/foo')
2351
        wt.commit('F foo => bing', rev_id='F-id')
2352
        wt.set_last_revision('B-id')
2353
        wt.branch.set_last_revision_info(2, 'B-id')
2354
        wt.revert()
2355
        wt.merge_from_branch(wt.branch, 'C-id')
2356
        wt.commit('D merges B & C', rev_id='D-id')
2357
        wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2358
        wt_base.lock_read()
2359
        self.addCleanup(wt_base.unlock)
2360
        wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2361
        wt_lca1.lock_read()
2362
        self.addCleanup(wt_lca1.unlock)
2363
        wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2364
        wt_lca2.lock_read()
2365
        self.addCleanup(wt_lca2.unlock)
2366
        wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2367
        wt_other.lock_read()
2368
        self.addCleanup(wt_other.unlock)
2369
        merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2370
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2371
        entries = list(merge_obj._entries_lca())
2372
        root_id = wt.path2id('')
2373
        self.assertEqual([('foo-id', True,
2374
                           ((root_id, [root_id, root_id]), root_id, root_id),
2375
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2376
                           ((False, [False, False]), False, False)),
2377
                         ], entries)
2378
3514.4.25 by John Arbash Meinel
A few more merge-level behavior tests.
2379
    def test_other_reverted_path_to_base(self):
2380
        #   A       Path at 'foo'
2381
        #  / \
2382
        # B   C     Path at 'bar' in B
2383
        # |\ /|
2384
        # | X |
2385
        # |/ \|
2386
        # D   E     Path at 'bar'
2387
        #     |
2388
        #     F     Path at 'foo'
2389
        builder = self.get_builder()
2390
        builder.build_snapshot('A-id', None,
2391
            [('add', (u'', 'a-root-id', 'directory', None)),
2392
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2393
        builder.build_snapshot('C-id', ['A-id'], [])
2394
        builder.build_snapshot('B-id', ['A-id'],
2395
            [('rename', ('foo', 'bar'))])
2396
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2397
            [('rename', ('foo', 'bar'))]) # merge the rename
2398
        builder.build_snapshot('F-id', ['E-id'],
2399
            [('rename', ('bar', 'foo'))]) # Rename back to BASE
2400
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2401
        wt, conflicts = self.do_merge(builder, 'F-id')
2402
        self.assertEqual(0, conflicts)
2403
        self.assertEqual('foo', wt.id2path('foo-id'))
2404
2405
    def test_other_reverted_content_to_base(self):
2406
        builder = self.get_builder()
2407
        builder.build_snapshot('A-id', None,
2408
            [('add', (u'', 'a-root-id', 'directory', None)),
2409
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2410
        builder.build_snapshot('C-id', ['A-id'], [])
2411
        builder.build_snapshot('B-id', ['A-id'],
2412
            [('modify', ('foo-id', 'B content\n'))])
2413
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2414
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2415
        builder.build_snapshot('F-id', ['E-id'],
2416
            [('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2417
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2418
        wt, conflicts = self.do_merge(builder, 'F-id')
2419
        self.assertEqual(0, conflicts)
2420
        # 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.
2421
        #       before this will work. Or at least use the LCA trees to find
2422
        #       the appropriate content base. (which is B, not A).
3955.2.1 by John Arbash Meinel
Change the workings of merge_content to be lca aware.
2423
        self.assertEqual('base content\n', wt.get_file_text('foo-id'))
3514.4.25 by John Arbash Meinel
A few more merge-level behavior tests.
2424
3514.4.28 by John Arbash Meinel
More symlink tests.
2425
    def test_other_modified_content(self):
2426
        builder = self.get_builder()
2427
        builder.build_snapshot('A-id', None,
2428
            [('add', (u'', 'a-root-id', 'directory', None)),
2429
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2430
        builder.build_snapshot('C-id', ['A-id'], [])
2431
        builder.build_snapshot('B-id', ['A-id'],
2432
            [('modify', ('foo-id', 'B content\n'))])
2433
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2434
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2435
        builder.build_snapshot('F-id', ['E-id'],
2436
            [('modify', ('foo-id', 'F content\n'))]) # Override B content
2437
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2438
        wt, conflicts = self.do_merge(builder, 'F-id')
2439
        self.assertEqual(0, conflicts)
2440
        self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2441
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2442
    def test_all_wt(self):
2443
        """Check behavior if all trees are Working Trees."""
2444
        # The big issue is that entry.revision is None for WorkingTrees. (as is
2445
        # entry.text_sha1, etc. So we need to make sure we handle that case
2446
        # correctly.
2447
        #   A   Content of 'foo', path of 'a'
2448
        #   |\
2449
        #   B C B modifies content, C renames 'a' => 'b'
2450
        #   |X|
2451
        #   D E E updates content, renames 'b' => 'c'
2452
        builder = self.get_builder()
2453
        builder.build_snapshot('A-id', None,
2454
            [('add', (u'', 'a-root-id', 'directory', None)),
2455
             ('add', (u'a', 'a-id', 'file', 'base content\n')),
2456
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2457
        builder.build_snapshot('B-id', ['A-id'],
2458
            [('modify', ('foo-id', 'B content\n'))])
2459
        builder.build_snapshot('C-id', ['A-id'],
2460
            [('rename', ('a', 'b'))])
2461
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2462
            [('rename', ('b', 'c')),
2463
             ('modify', ('foo-id', 'E content\n'))])
2464
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
2465
            [('rename', ('a', 'b'))]) # merged change
2466
        wt_this = self.get_wt_from_builder(builder)
2467
        wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2468
        wt_base.lock_read()
2469
        self.addCleanup(wt_base.unlock)
2470
        wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2471
        wt_lca1.lock_read()
2472
        self.addCleanup(wt_lca1.unlock)
2473
        wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2474
        wt_lca2.lock_read()
2475
        self.addCleanup(wt_lca2.unlock)
2476
        wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2477
        wt_other.lock_read()
2478
        self.addCleanup(wt_other.unlock)
2479
        merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2480
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2481
        entries = list(merge_obj._entries_lca())
2482
        root_id = 'a-root-id'
2483
        self.assertEqual([('a-id', False,
2484
                           ((root_id, [root_id, root_id]), root_id, root_id),
2485
                           ((u'a', [u'a', u'b']), u'c', u'b'),
2486
                           ((False, [False, False]), False, False)),
2487
                          ('foo-id', True,
2488
                           ((root_id, [root_id, root_id]), root_id, root_id),
2489
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2490
                           ((False, [False, False]), False, False)),
2491
                         ], entries)
2492
3514.4.33 by John Arbash Meinel
Implement support for 'tree-reference'.
2493
    def test_nested_tree_unmodified(self):
2494
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2495
        # 'tree-reference'
2496
        wt = self.make_branch_and_tree('tree',
2497
            format='dirstate-with-subtree')
2498
        wt.lock_write()
2499
        self.addCleanup(wt.unlock)
2500
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2501
            format='dirstate-with-subtree')
2502
        wt.set_root_id('a-root-id')
2503
        sub_tree.set_root_id('sub-tree-root')
2504
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2505
        sub_tree.add('file')
2506
        sub_tree.commit('foo', rev_id='sub-A-id')
2507
        wt.add_reference(sub_tree)
2508
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2509
        # Now create a criss-cross merge in the parent, without modifying the
2510
        # subtree
2511
        wt.commit('B', rev_id='B-id', recursive=None)
2512
        wt.set_last_revision('A-id')
2513
        wt.branch.set_last_revision_info(1, 'A-id')
2514
        wt.commit('C', rev_id='C-id', recursive=None)
2515
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2516
        wt.commit('E', rev_id='E-id', recursive=None)
2517
        wt.set_parent_ids(['B-id', 'C-id'])
2518
        wt.branch.set_last_revision_info(2, 'B-id')
2519
        wt.commit('D', rev_id='D-id', recursive=None)
2520
2521
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2522
            wt, 'E-id')
2523
        merger.merge_type = _mod_merge.Merge3Merger
2524
        merge_obj = merger.make_merger()
2525
        entries = list(merge_obj._entries_lca())
2526
        self.assertEqual([], entries)
2527
2528
    def test_nested_tree_subtree_modified(self):
2529
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2530
        # 'tree-reference'
2531
        wt = self.make_branch_and_tree('tree',
2532
            format='dirstate-with-subtree')
2533
        wt.lock_write()
2534
        self.addCleanup(wt.unlock)
2535
        sub_tree = self.make_branch_and_tree('tree/sub',
2536
            format='dirstate-with-subtree')
2537
        wt.set_root_id('a-root-id')
2538
        sub_tree.set_root_id('sub-tree-root')
2539
        self.build_tree_contents([('tree/sub/file', 'text1')])
2540
        sub_tree.add('file')
2541
        sub_tree.commit('foo', rev_id='sub-A-id')
2542
        wt.add_reference(sub_tree)
2543
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2544
        # Now create a criss-cross merge in the parent, without modifying the
2545
        # subtree
2546
        wt.commit('B', rev_id='B-id', recursive=None)
2547
        wt.set_last_revision('A-id')
2548
        wt.branch.set_last_revision_info(1, 'A-id')
2549
        wt.commit('C', rev_id='C-id', recursive=None)
2550
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2551
        self.build_tree_contents([('tree/sub/file', 'text2')])
2552
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2553
        wt.commit('E', rev_id='E-id', recursive=None)
2554
        wt.set_parent_ids(['B-id', 'C-id'])
2555
        wt.branch.set_last_revision_info(2, 'B-id')
2556
        wt.commit('D', rev_id='D-id', recursive=None)
2557
2558
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2559
            wt, 'E-id')
2560
        merger.merge_type = _mod_merge.Merge3Merger
2561
        merge_obj = merger.make_merger()
2562
        entries = list(merge_obj._entries_lca())
2563
        # Nothing interesting about this sub-tree, because content changes are
2564
        # computed at a higher level
2565
        self.assertEqual([], entries)
2566
2567
    def test_nested_tree_subtree_renamed(self):
2568
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2569
        # 'tree-reference'
2570
        wt = self.make_branch_and_tree('tree',
2571
            format='dirstate-with-subtree')
2572
        wt.lock_write()
2573
        self.addCleanup(wt.unlock)
2574
        sub_tree = self.make_branch_and_tree('tree/sub',
2575
            format='dirstate-with-subtree')
2576
        wt.set_root_id('a-root-id')
2577
        sub_tree.set_root_id('sub-tree-root')
2578
        self.build_tree_contents([('tree/sub/file', 'text1')])
2579
        sub_tree.add('file')
2580
        sub_tree.commit('foo', rev_id='sub-A-id')
2581
        wt.add_reference(sub_tree)
2582
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2583
        # Now create a criss-cross merge in the parent, without modifying the
2584
        # subtree
2585
        wt.commit('B', rev_id='B-id', recursive=None)
2586
        wt.set_last_revision('A-id')
2587
        wt.branch.set_last_revision_info(1, 'A-id')
2588
        wt.commit('C', rev_id='C-id', recursive=None)
2589
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2590
        wt.rename_one('sub', 'alt_sub')
2591
        wt.commit('E', rev_id='E-id', recursive=None)
2592
        wt.set_last_revision('B-id')
2593
        wt.revert()
2594
        wt.set_parent_ids(['B-id', 'C-id'])
2595
        wt.branch.set_last_revision_info(2, 'B-id')
2596
        wt.commit('D', rev_id='D-id', recursive=None)
2597
2598
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2599
            wt, 'E-id')
2600
        merger.merge_type = _mod_merge.Merge3Merger
2601
        merge_obj = merger.make_merger()
2602
        entries = list(merge_obj._entries_lca())
2603
        root_id = 'a-root-id'
2604
        self.assertEqual([('sub-tree-root', False,
2605
                           ((root_id, [root_id, root_id]), root_id, root_id),
2606
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2607
                           ((False, [False, False]), False, False)),
2608
                         ], entries)
2609
2610
    def test_nested_tree_subtree_renamed_and_modified(self):
2611
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2612
        # 'tree-reference'
2613
        wt = self.make_branch_and_tree('tree',
2614
            format='dirstate-with-subtree')
2615
        wt.lock_write()
2616
        self.addCleanup(wt.unlock)
2617
        sub_tree = self.make_branch_and_tree('tree/sub',
2618
            format='dirstate-with-subtree')
2619
        wt.set_root_id('a-root-id')
2620
        sub_tree.set_root_id('sub-tree-root')
2621
        self.build_tree_contents([('tree/sub/file', 'text1')])
2622
        sub_tree.add('file')
2623
        sub_tree.commit('foo', rev_id='sub-A-id')
2624
        wt.add_reference(sub_tree)
2625
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2626
        # Now create a criss-cross merge in the parent, without modifying the
2627
        # subtree
2628
        wt.commit('B', rev_id='B-id', recursive=None)
2629
        wt.set_last_revision('A-id')
2630
        wt.branch.set_last_revision_info(1, 'A-id')
2631
        wt.commit('C', rev_id='C-id', recursive=None)
2632
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2633
        self.build_tree_contents([('tree/sub/file', 'text2')])
2634
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2635
        wt.rename_one('sub', 'alt_sub')
2636
        wt.commit('E', rev_id='E-id', recursive=None)
2637
        wt.set_last_revision('B-id')
2638
        wt.revert()
2639
        wt.set_parent_ids(['B-id', 'C-id'])
2640
        wt.branch.set_last_revision_info(2, 'B-id')
2641
        wt.commit('D', rev_id='D-id', recursive=None)
2642
2643
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2644
            wt, 'E-id')
2645
        merger.merge_type = _mod_merge.Merge3Merger
2646
        merge_obj = merger.make_merger()
2647
        entries = list(merge_obj._entries_lca())
2648
        root_id = 'a-root-id'
2649
        self.assertEqual([('sub-tree-root', False,
2650
                           ((root_id, [root_id, root_id]), root_id, root_id),
2651
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2652
                           ((False, [False, False]), False, False)),
2653
                         ], entries)
2654
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2655
2656
class TestLCAMultiWay(tests.TestCase):
2657
3514.4.30 by John Arbash Meinel
Several updates.
2658
    def assertLCAMultiWay(self, expected, base, lcas, other, this,
2659
                          allow_overriding_lca=True):
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2660
        self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
3514.4.30 by John Arbash Meinel
Several updates.
2661
                                (base, lcas), other, this,
2662
                                allow_overriding_lca=allow_overriding_lca))
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2663
2664
    def test_other_equal_equal_lcas(self):
2665
        """Test when OTHER=LCA and all LCAs are identical."""
2666
        self.assertLCAMultiWay('this',
2667
            'bval', ['bval', 'bval'], 'bval', 'bval')
2668
        self.assertLCAMultiWay('this',
2669
            'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2670
        self.assertLCAMultiWay('this',
2671
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2672
        self.assertLCAMultiWay('this',
2673
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2674
        self.assertLCAMultiWay('this',
2675
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2676
2677
    def test_other_equal_this(self):
2678
        """Test when other and this are identical."""
2679
        self.assertLCAMultiWay('this',
2680
            'bval', ['bval', 'bval'], 'oval', 'oval')
2681
        self.assertLCAMultiWay('this',
2682
            'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2683
        self.assertLCAMultiWay('this',
2684
            'bval', ['cval', 'dval'], 'oval', 'oval')
2685
        self.assertLCAMultiWay('this',
2686
            'bval', [None, 'lcaval'], 'oval', 'oval')
2687
        self.assertLCAMultiWay('this',
2688
            None, [None, 'lcaval'], 'oval', 'oval')
2689
        self.assertLCAMultiWay('this',
2690
            None, ['lcaval', 'lcaval'], 'oval', 'oval')
2691
        self.assertLCAMultiWay('this',
2692
            None, ['cval', 'dval'], 'oval', 'oval')
2693
        self.assertLCAMultiWay('this',
2694
            None, ['cval', 'dval'], None, None)
2695
        self.assertLCAMultiWay('this',
2696
            None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2697
2698
    def test_no_lcas(self):
2699
        self.assertLCAMultiWay('this',
2700
            'bval', [], 'bval', 'tval')
2701
        self.assertLCAMultiWay('other',
2702
            'bval', [], 'oval', 'bval')
2703
        self.assertLCAMultiWay('conflict',
2704
            'bval', [], 'oval', 'tval')
2705
        self.assertLCAMultiWay('this',
2706
            'bval', [], 'oval', 'oval')
2707
2708
    def test_lca_supersedes_other_lca(self):
2709
        """If one lca == base, the other lca takes precedence"""
2710
        self.assertLCAMultiWay('this',
2711
            'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2712
        self.assertLCAMultiWay('this',
2713
            'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2714
        # This is actually considered a 'revert' because the 'lcaval' in LCAS
2715
        # supersedes the BASE val (in the other LCA) but then OTHER reverts it
2716
        # back to bval.
2717
        self.assertLCAMultiWay('other',
2718
            'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2719
        self.assertLCAMultiWay('conflict',
2720
            'bval', ['bval', 'lcaval'], 'bval', 'tval')
2721
2722
    def test_other_and_this_pick_different_lca(self):
2723
        # OTHER and THIS resolve the lca conflict in different ways
2724
        self.assertLCAMultiWay('conflict',
2725
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2726
        self.assertLCAMultiWay('conflict',
2727
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2728
        self.assertLCAMultiWay('conflict',
2729
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2730
2731
    def test_other_in_lca(self):
2732
        # OTHER takes a value of one of the LCAs, THIS takes a new value, which
2733
        # theoretically supersedes both LCA values and 'wins'
2734
        self.assertLCAMultiWay('this',
2735
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2736
        self.assertLCAMultiWay('this',
2737
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
3514.4.30 by John Arbash Meinel
Several updates.
2738
        self.assertLCAMultiWay('conflict',
2739
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2740
            allow_overriding_lca=False)
2741
        self.assertLCAMultiWay('conflict',
2742
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2743
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2744
        # THIS reverted back to BASE, but that is an explicit supersede of all
2745
        # LCAs
2746
        self.assertLCAMultiWay('this',
2747
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2748
        self.assertLCAMultiWay('this',
2749
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3514.4.30 by John Arbash Meinel
Several updates.
2750
        self.assertLCAMultiWay('conflict',
2751
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2752
            allow_overriding_lca=False)
2753
        self.assertLCAMultiWay('conflict',
2754
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2755
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2756
2757
    def test_this_in_lca(self):
2758
        # THIS takes a value of one of the LCAs, OTHER takes a new value, which
2759
        # theoretically supersedes both LCA values and 'wins'
2760
        self.assertLCAMultiWay('other',
2761
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2762
        self.assertLCAMultiWay('other',
2763
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3514.4.30 by John Arbash Meinel
Several updates.
2764
        self.assertLCAMultiWay('conflict',
2765
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2766
            allow_overriding_lca=False)
2767
        self.assertLCAMultiWay('conflict',
2768
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2769
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2770
        # OTHER reverted back to BASE, but that is an explicit supersede of all
2771
        # LCAs
2772
        self.assertLCAMultiWay('other',
2773
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
3514.4.30 by John Arbash Meinel
Several updates.
2774
        self.assertLCAMultiWay('conflict',
2775
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2776
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2777
2778
    def test_all_differ(self):
2779
        self.assertLCAMultiWay('conflict',
2780
            'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2781
        self.assertLCAMultiWay('conflict',
2782
            'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2783
        self.assertLCAMultiWay('conflict',
2784
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')