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