~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
4312.1.1 by John Arbash Meinel
Add a per-implementation test that deleting lines conflicts with modifying lines.
1098
    def test_modify_conflicts_with_delete(self):
1099
        # If one side deletes a line, and the other modifies that line, then
1100
        # the modification should be considered a conflict
1101
        builder = self.make_branch_builder('test')
1102
        builder.start_series()
1103
        builder.build_snapshot('BASE-id', None,
1104
            [('add', ('', None, 'directory', None)),
1105
             ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
1106
            ])
1107
        # Delete 'b\n'
1108
        builder.build_snapshot('OTHER-id', ['BASE-id'],
1109
            [('modify', ('foo-id', 'a\nc\nd\ne\n'))])
1110
        # Modify 'b\n', add 'X\n'
1111
        builder.build_snapshot('THIS-id', ['BASE-id'],
1112
            [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))])
1113
        builder.finish_series()
1114
        branch = builder.get_branch()
1115
        this_tree = branch.bzrdir.create_workingtree()
1116
        this_tree.lock_write()
1117
        self.addCleanup(this_tree.unlock)
1118
        other_tree = this_tree.bzrdir.sprout('other', 'OTHER-id').open_workingtree()
1119
        self.do_merge(this_tree, other_tree)
1120
        if self.merge_type is _mod_merge.LCAMerger:
1121
            self.expectFailure("lca merge doesn't track deleted lines",
1122
                self.assertFileEqual,
1123
                    'a\n'
1124
                    '<<<<<<< TREE\n'
1125
                    'b2\n'
1126
                    '=======\n'
1127
                    '>>>>>>> MERGE-SOURCE\n'
1128
                    'c\n'
1129
                    'd\n'
1130
                    'X\n'
1131
                    'e\n', 'test/foo')
1132
        else:
1133
            self.assertFileEqual(
1134
                'a\n'
1135
                '<<<<<<< TREE\n'
1136
                'b2\n'
1137
                '=======\n'
1138
                '>>>>>>> MERGE-SOURCE\n'
1139
                'c\n'
1140
                'd\n'
1141
                'X\n'
1142
                'e\n', 'test/foo')
1143
3144.5.3 by Aaron Bentley
Test interesting_files for LCA merge
1144
1145
class TestMerge3Merge(TestCaseWithTransport, TestMergeImplementation):
1146
1147
    merge_type = _mod_merge.Merge3Merger
1148
1149
1150
class TestWeaveMerge(TestCaseWithTransport, TestMergeImplementation):
1151
1152
    merge_type = _mod_merge.WeaveMerger
1153
1154
1155
class TestLCAMerge(TestCaseWithTransport, TestMergeImplementation):
1156
1157
    merge_type = _mod_merge.LCAMerger
3514.2.2 by John Arbash Meinel
Restore a real weave merge to 'bzr merge --weave'.
1158
1159
    def test_merge_move_and_change(self):
1160
        self.expectFailure("lca merge doesn't conflict for move and change",
1161
            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
1162
3514.4.1 by John Arbash Meinel
Update Merger to set a flag when we encounter a criss-cross merge.
1163
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1164
class LoggingMerger(object):
1165
    # These seem to be the required attributes
1166
    requires_base = False
1167
    supports_reprocess = False
1168
    supports_show_base = False
1169
    supports_cherrypick = False
1170
    # We intentionally do not define supports_lca_trees
1171
1172
    def __init__(self, *args, **kwargs):
1173
        self.args = args
1174
        self.kwargs = kwargs
1175
1176
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1177
class TestMergerBase(TestCaseWithMemoryTransport):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1178
    """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.
1179
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.
1180
    def get_builder(self):
1181
        builder = self.make_branch_builder('path')
1182
        builder.start_series()
1183
        self.addCleanup(builder.finish_series)
1184
        return builder
1185
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1186
    def setup_simple_graph(self):
1187
        """Create a simple 3-node graph.
1188
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1189
        :return: A BranchBuilder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1190
        """
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1191
        #
1192
        #  A
1193
        #  |\
1194
        #  B C
1195
        #
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.
1196
        builder = self.get_builder()
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1197
        builder.build_snapshot('A-id', None,
1198
            [('add', ('', None, 'directory', None))])
1199
        builder.build_snapshot('C-id', ['A-id'], [])
1200
        builder.build_snapshot('B-id', ['A-id'], [])
1201
        return builder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1202
1203
    def setup_criss_cross_graph(self):
1204
        """Create a 5-node graph with a criss-cross.
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1205
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1206
        :return: A BranchBuilder
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1207
        """
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1208
        # A
1209
        # |\
1210
        # B C
1211
        # |X|
1212
        # D E
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1213
        builder = self.setup_simple_graph()
1214
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1215
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1216
        return builder
1217
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1218
    def make_Merger(self, builder, other_revision_id,
1219
                    interesting_files=None, interesting_ids=None):
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1220
        """Make a Merger object from a branch builder"""
1221
        mem_tree = memorytree.MemoryTree.create_on_branch(builder.get_branch())
1222
        mem_tree.lock_write()
1223
        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.
1224
        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.
1225
            mem_tree, other_revision_id)
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1226
        merger.set_interesting_files(interesting_files)
1227
        # It seems there is no matching function for set_interesting_ids
1228
        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.
1229
        merger.merge_type = _mod_merge.Merge3Merger
1230
        return merger
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1231
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1232
1233
class TestMergerInMemory(TestMergerBase):
1234
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1235
    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.
1236
        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.
1237
        self.assertEqual('A-id', merger.base_rev_id)
1238
        self.assertFalse(merger._is_criss_cross)
1239
        self.assertIs(None, merger._lca_trees)
1240
1241
    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.
1242
        builder = self.setup_criss_cross_graph()
1243
        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.
1244
        self.assertEqual('A-id', merger.base_rev_id)
1245
        self.assertTrue(merger._is_criss_cross)
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1246
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1247
                                            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.
1248
        # If we swap the order, we should get a different lca order
1249
        builder.build_snapshot('F-id', ['E-id'], [])
1250
        merger = self.make_Merger(builder, 'D-id')
1251
        self.assertEqual(['C-id', 'B-id'], [t.get_revision_id()
1252
                                            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.
1253
3514.4.23 by John Arbash Meinel
Handle when there are more than 2 LCAs while searching for the unique lca.
1254
    def test_find_base_triple_criss_cross(self):
1255
        #       A-.
1256
        #      / \ \
1257
        #     B   C F # F is merged into both branches
1258
        #     |\ /| |
1259
        #     | X | |\
1260
        #     |/ \| | :
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1261
        #   : D   E |
1262
        #    \|   |/
3514.4.23 by John Arbash Meinel
Handle when there are more than 2 LCAs while searching for the unique lca.
1263
        #     G   H
1264
        builder = self.setup_criss_cross_graph()
1265
        builder.build_snapshot('F-id', ['A-id'], [])
1266
        builder.build_snapshot('H-id', ['E-id', 'F-id'], [])
1267
        builder.build_snapshot('G-id', ['D-id', 'F-id'], [])
1268
        merger = self.make_Merger(builder, 'H-id')
1269
        self.assertEqual(['B-id', 'C-id', 'F-id'],
1270
                         [t.get_revision_id() for t in merger._lca_trees])
1271
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1272
    def test_no_criss_cross_passed_to_merge_type(self):
1273
        class LCATreesMerger(LoggingMerger):
1274
            supports_lca_trees = True
1275
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.
1276
        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.
1277
        merger.merge_type = LCATreesMerger
1278
        merge_obj = merger.make_merger()
1279
        self.assertIsInstance(merge_obj, LCATreesMerger)
1280
        self.assertFalse('lca_trees' in merge_obj.kwargs)
1281
1282
    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.
1283
        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.
1284
        merger.merge_type = _mod_merge.Merge3Merger
1285
        merge_obj = merger.make_merger()
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 merger._lca_trees])
3514.4.4 by John Arbash Meinel
Test that the lca_trees are passed down to the Merger object when appropriate.
1288
1289
    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.
1290
        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.
1291
        # We explicitly do not define supports_lca_trees
1292
        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.
1293
        merge_obj = merger.make_merger()
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1294
        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.
1295
        self.assertFalse('lca_trees' in merge_obj.kwargs)
1296
1297
    def test_criss_cross_unsupported_merge_type(self):
1298
        class UnsupportedLCATreesMerger(LoggingMerger):
1299
            supports_lca_trees = False
1300
3514.4.10 by John Arbash Meinel
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
1301
        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.
1302
        merger.merge_type = UnsupportedLCATreesMerger
1303
        merge_obj = merger.make_merger()
1304
        self.assertIsInstance(merge_obj, UnsupportedLCATreesMerger)
1305
        self.assertFalse('lca_trees' in merge_obj.kwargs)
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1306
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1307
1308
class TestMergerEntriesLCA(TestMergerBase):
1309
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1310
    def make_merge_obj(self, builder, other_revision_id,
1311
                       interesting_files=None, interesting_ids=None):
1312
        merger = self.make_Merger(builder, other_revision_id,
1313
            interesting_files=interesting_files,
1314
            interesting_ids=interesting_ids)
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1315
        return merger.make_merger()
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1316
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1317
    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.
1318
        builder = self.get_builder()
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1319
        builder.build_snapshot('A-id', None,
1320
            [('add', (u'', 'a-root-id', 'directory', None)),
1321
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1322
        builder.build_snapshot('C-id', ['A-id'],
1323
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
1324
        builder.build_snapshot('B-id', ['A-id'],
1325
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
1326
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1327
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1328
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1329
            [('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1330
        merge_obj = self.make_merge_obj(builder, 'E-id')
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1331
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1332
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1333
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1334
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1335
        entries = list(merge_obj._entries_lca())
1336
1337
        # (file_id, changed, parents, names, executable)
1338
        # BASE, lca1, lca2, OTHER, THIS
3514.4.7 by John Arbash Meinel
switch over test_merge to using the new BranchBuilder api.
1339
        root_id = 'a-root-id'
3514.4.12 by John Arbash Meinel
add more filtering for when a directory hasn't actually changed.
1340
        self.assertEqual([('a-id', True,
3514.4.5 by John Arbash Meinel
Initial work on _entries_lca.
1341
                           ((root_id, [root_id, root_id]), root_id, root_id),
1342
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1343
                           ((False, [False, False]), False, False)),
1344
                         ], 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.
1345
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1346
    def test_not_in_base(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1347
        # 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.
1348
        # the tips, but the base has something different
1349
        #       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.
1350
        #       |\
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.
1351
        #       B C  B introduces 'foo', C introduces 'bar'
1352
        #       |X|
1353
        #       D E  D and E now both have 'foo' and 'bar'
1354
        #       |X|
1355
        #       F G  the files are now in F, G, D and E, but not in A
1356
        #            G modifies 'bar'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1357
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.
1358
        builder = self.get_builder()
1359
        builder.build_snapshot('A-id', None,
1360
            [('add', (u'', 'a-root-id', 'directory', None))])
1361
        builder.build_snapshot('B-id', ['A-id'],
1362
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1363
        builder.build_snapshot('C-id', ['A-id'],
1364
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1365
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1366
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1367
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1368
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1369
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1370
            [('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1371
        builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1372
        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.
1373
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1374
        self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1375
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1376
        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.
1377
        entries = list(merge_obj._entries_lca())
1378
        root_id = 'a-root-id'
1379
        self.assertEqual([('bar-id', True,
1380
                           ((None, [root_id, root_id]), root_id, root_id),
1381
                           ((None, [u'bar', u'bar']), u'bar', u'bar'),
1382
                           ((None, [False, False]), False, False)),
1383
                         ], entries)
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1384
1385
    def test_not_in_this(self):
1386
        builder = self.get_builder()
1387
        builder.build_snapshot('A-id', None,
1388
            [('add', (u'', 'a-root-id', 'directory', None)),
1389
             ('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'.
1390
        builder.build_snapshot('B-id', ['A-id'],
1391
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1392
        builder.build_snapshot('C-id', ['A-id'],
1393
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
1394
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1395
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1396
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1397
            [('unversion', 'a-id')])
1398
        merge_obj = self.make_merge_obj(builder, 'E-id')
1399
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1400
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1401
                                            for t in merge_obj._lca_trees])
3514.4.11 by John Arbash Meinel
Handle when an entry is missing in THIS
1402
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1403
1404
        entries = list(merge_obj._entries_lca())
1405
        root_id = 'a-root-id'
1406
        self.assertEqual([('a-id', True,
1407
                           ((root_id, [root_id, root_id]), root_id, None),
1408
                           ((u'a', [u'a', u'a']), u'a', None),
1409
                           ((False, [False, False]), False, None)),
1410
                         ], entries)
1411
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.
1412
    def test_file_not_in_one_lca(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1413
        #   A   # just root
1414
        #   |\
1415
        #   B C # B no file, C introduces a file
1416
        #   |X|
1417
        #   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'.
1418
        builder = self.get_builder()
1419
        builder.build_snapshot('A-id', None,
1420
            [('add', (u'', 'a-root-id', 'directory', None))])
1421
        builder.build_snapshot('B-id', ['A-id'], [])
1422
        builder.build_snapshot('C-id', ['A-id'],
1423
            [('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.
1424
        builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1425
        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'.
1426
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1427
        merge_obj = self.make_merge_obj(builder, 'E-id')
1428
1429
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1430
                                            for t in merge_obj._lca_trees])
1431
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1432
1433
        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.
1434
        self.assertEqual([], entries)
3514.4.13 by John Arbash Meinel
Switch the lca_trees to be in 'find_merge_order'.
1435
3514.4.15 by John Arbash Meinel
Handle when OTHER doesn't have the entry.
1436
    def test_not_in_other(self):
1437
        builder = self.get_builder()
1438
        builder.build_snapshot('A-id', None,
1439
            [('add', (u'', 'a-root-id', 'directory', None)),
1440
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1441
        builder.build_snapshot('B-id', ['A-id'], [])
1442
        builder.build_snapshot('C-id', ['A-id'], [])
1443
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1444
            [('unversion', 'a-id')])
1445
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1446
        merge_obj = self.make_merge_obj(builder, 'E-id')
1447
1448
        entries = list(merge_obj._entries_lca())
1449
        root_id = 'a-root-id'
1450
        self.assertEqual([('a-id', True,
1451
                           ((root_id, [root_id, root_id]), None, root_id),
1452
                           ((u'a', [u'a', u'a']), None, u'a'),
1453
                           ((False, [False, False]), None, False)),
1454
                         ], entries)
1455
3514.4.30 by John Arbash Meinel
Several updates.
1456
    def test_not_in_other_or_lca(self):
1457
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1458
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1459
        #       B C  B nothing, C deletes foo
1460
        #       |X|
1461
        #       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.
1462
        # Analysis:
1463
        #   A => B, no changes
1464
        #   A => C, delete foo (C should supersede B)
1465
        #   C => D, restore foo
1466
        #   C => E, no changes
1467
        # D would then win 'cleanly' and no record would be given
3514.4.30 by John Arbash Meinel
Several updates.
1468
        builder = self.get_builder()
1469
        builder.build_snapshot('A-id', None,
1470
            [('add', (u'', 'a-root-id', 'directory', None)),
1471
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1472
        builder.build_snapshot('B-id', ['A-id'], [])
1473
        builder.build_snapshot('C-id', ['A-id'],
1474
            [('unversion', 'foo-id')])
1475
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1476
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1477
        merge_obj = self.make_merge_obj(builder, 'E-id')
1478
1479
        entries = list(merge_obj._entries_lca())
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1480
        self.assertEqual([], entries)
1481
1482
    def test_not_in_other_mod_in_lca1_not_in_lca2(self):
1483
        #       A    base, introduces 'foo'
1484
        #       |\
1485
        #       B C  B changes 'foo', C deletes foo
1486
        #       |X|
1487
        #       D E  D restores foo (same as B), E leaves it deleted (as C)
1488
        # Analysis:
1489
        #   A => B, modified foo
1490
        #   A => C, delete foo, C does not supersede B
1491
        #   B => D, no changes
1492
        #   C => D, resolve in favor of B
1493
        #   B => E, resolve in favor of E
1494
        #   C => E, no changes
1495
        # In this case, we have a conflict of how the changes were resolved. E
1496
        # picked C and D picked B, so we should issue a conflict
1497
        builder = self.get_builder()
1498
        builder.build_snapshot('A-id', None,
1499
            [('add', (u'', 'a-root-id', 'directory', None)),
1500
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1501
        builder.build_snapshot('B-id', ['A-id'], [
1502
            ('modify', ('foo-id', 'new-content\n'))])
1503
        builder.build_snapshot('C-id', ['A-id'],
1504
            [('unversion', 'foo-id')])
1505
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1506
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1507
        merge_obj = self.make_merge_obj(builder, 'E-id')
1508
1509
        entries = list(merge_obj._entries_lca())
3948.1.4 by Vincent Ladeuil
More cleanup and fix overzealous previous one.
1510
        root_id = 'a-root-id'
3514.4.30 by John Arbash Meinel
Several updates.
1511
        self.assertEqual([('foo-id', True,
1512
                           ((root_id, [root_id, None]), None, root_id),
1513
                           ((u'foo', [u'foo', None]), None, 'foo'),
1514
                           ((False, [False, None]), None, False)),
1515
                         ], entries)
1516
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.
1517
    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.
1518
        #   A   add only root
1519
        #   |\
1520
        #   B C B nothing, C add file
1521
        #   |X|
1522
        #   D E D still has nothing, E removes file
1523
        # Analysis:
1524
        #   B => D, no change
1525
        #   C => D, removed the file
1526
        #   B => E, no change
1527
        #   C => E, removed the file
1528
        # Thus D & E have identical changes, and this is a no-op
1529
        # Alternatively:
1530
        #   A => B, no change
1531
        #   A => C, add file, thus C supersedes B
1532
        #   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.
1533
        builder = self.get_builder()
1534
        builder.build_snapshot('A-id', None,
1535
            [('add', (u'', 'a-root-id', 'directory', None))])
1536
        builder.build_snapshot('B-id', ['A-id'], [])
1537
        builder.build_snapshot('C-id', ['A-id'],
1538
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1539
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1540
            [('unversion', 'a-id')])
1541
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1542
        merge_obj = self.make_merge_obj(builder, 'E-id')
1543
1544
        entries = list(merge_obj._entries_lca())
3948.1.1 by John Arbash Meinel
Fix an edge case with deleted files and criss-cross merges.
1545
        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.
1546
3514.4.16 by John Arbash Meinel
another test for only in OTHER
1547
    def test_only_in_other(self):
1548
        builder = self.get_builder()
1549
        builder.build_snapshot('A-id', None,
1550
            [('add', (u'', 'a-root-id', 'directory', None))])
1551
        builder.build_snapshot('B-id', ['A-id'], [])
1552
        builder.build_snapshot('C-id', ['A-id'], [])
1553
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1554
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1555
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1556
        merge_obj = self.make_merge_obj(builder, 'E-id')
1557
1558
        entries = list(merge_obj._entries_lca())
1559
        root_id = 'a-root-id'
1560
        self.assertEqual([('a-id', True,
1561
                           ((None, [None, None]), root_id, None),
1562
                           ((None, [None, None]), u'a', None),
1563
                           ((None, [None, None]), False, None)),
1564
                         ], entries)
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
1565
3514.4.30 by John Arbash Meinel
Several updates.
1566
    def test_one_lca_supersedes(self):
3514.4.41 by John Arbash Meinel
Some grammar and other clarity feedback from Aaron.
1567
        # One LCA supersedes the other LCAs last modified value, but the
3514.4.30 by John Arbash Meinel
Several updates.
1568
        # value is not the same as BASE.
1569
        #       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.
1570
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1571
        #       B C  B modifies 'foo' (mod B), C does nothing (mod A)
1572
        #       |X|
1573
        #       D E  D does nothing (mod B), E updates 'foo' (mod E)
1574
        #       |X|
1575
        #       F G  F updates 'foo' (mod F). G does nothing (mod E)
1576
        #
1577
        #   At this point, G should not be considered to modify 'foo', even
1578
        #   though its LCAs disagree. This is because the modification in E
1579
        #   completely supersedes the value in D.
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
            [('modify', ('foo-id', 'B content\n'))])
1587
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1588
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1589
            [('modify', ('foo-id', 'E content\n'))])
1590
        builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
1591
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1592
            [('modify', ('foo-id', 'F content\n'))])
1593
        merge_obj = self.make_merge_obj(builder, 'G-id')
1594
1595
        self.assertEqual([], list(merge_obj._entries_lca()))
1596
3514.4.31 by John Arbash Meinel
Add expected failures for cases where we should be looking at more than
1597
    def test_one_lca_supersedes_path(self):
1598
        # Double-criss-cross merge, the ultimate base value is different from
1599
        # the intermediate.
1600
        #   A    value 'foo'
1601
        #   |\
1602
        #   B C  B value 'bar', C = 'foo'
1603
        #   |X|
1604
        #   D E  D = 'bar', E supersedes to 'bing'
1605
        #   |X|
1606
        #   F G  F = 'bing', G supersedes to 'barry'
1607
        #
1608
        # In this case, we technically should not care about the value 'bar' for
1609
        # D, because it was clearly superseded by E's 'bing'. The
1610
        # per-file/attribute graph would actually look like:
1611
        #   A
1612
        #   |
1613
        #   B
1614
        #   |
1615
        #   E
1616
        #   |
1617
        #   G
1618
        #
1619
        # Because the other side of the merge never modifies the value, it just
1620
        # takes the value from the merge.
1621
        #
1622
        # ATM this fails because we will prune 'foo' from the LCAs, but we
1623
        # won't prune 'bar'. This is getting far off into edge-case land, so we
1624
        # aren't supporting it yet.
1625
        #
1626
        builder = self.get_builder()
1627
        builder.build_snapshot('A-id', None,
1628
            [('add', (u'', 'a-root-id', 'directory', None)),
1629
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1630
        builder.build_snapshot('C-id', ['A-id'], [])
1631
        builder.build_snapshot('B-id', ['A-id'],
1632
            [('rename', ('foo', 'bar'))])
1633
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1634
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1635
            [('rename', ('foo', 'bing'))]) # override to bing
1636
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1637
            [('rename', ('bing', 'barry'))]) # override to barry
1638
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1639
            [('rename', ('bar', 'bing'))]) # Merge in E's change
1640
        merge_obj = self.make_merge_obj(builder, 'G-id')
1641
1642
        self.expectFailure("We don't do an actual heads() check on lca values,"
1643
            " or use the per-attribute graph",
1644
            self.assertEqual, [], list(merge_obj._entries_lca()))
1645
1646
    def test_one_lca_accidentally_pruned(self):
1647
        # Another incorrect resolution from the same basic flaw:
1648
        #   A    value 'foo'
1649
        #   |\
1650
        #   B C  B value 'bar', C = 'foo'
1651
        #   |X|
1652
        #   D E  D = 'bar', E reverts to 'foo'
1653
        #   |X|
1654
        #   F G  F = 'bing', G switches to 'bar'
1655
        #
1656
        # 'bar' will not be seen as an interesting change, because 'foo' will
1657
        # be pruned from the LCAs, even though it was newly introduced by E
1658
        # (superseding B).
1659
        builder = self.get_builder()
1660
        builder.build_snapshot('A-id', None,
1661
            [('add', (u'', 'a-root-id', 'directory', None)),
1662
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1663
        builder.build_snapshot('C-id', ['A-id'], [])
1664
        builder.build_snapshot('B-id', ['A-id'],
1665
            [('rename', ('foo', 'bar'))])
1666
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1667
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1668
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
1669
            [('rename', ('foo', 'bar'))])
1670
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
1671
            [('rename', ('bar', 'bing'))]) # should end up conflicting
1672
        merge_obj = self.make_merge_obj(builder, 'G-id')
1673
1674
        entries = list(merge_obj._entries_lca())
1675
        root_id = 'a-root-id'
1676
        self.expectFailure("We prune values from BASE even when relevant.",
1677
            self.assertEqual,
1678
                [('foo-id', False,
1679
                  ((root_id, [root_id, root_id]), root_id, root_id),
1680
                  ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1681
                  ((False, [False, False]), False, False)),
1682
                ], entries)
1683
3514.4.30 by John Arbash Meinel
Several updates.
1684
    def test_both_sides_revert(self):
1685
        # Both sides of a criss-cross revert the text to the lca
1686
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1687
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1688
        #       B C  B modifies 'foo', C modifies 'foo'
1689
        #       |X|
1690
        #       D E  D reverts to B, E reverts to C
1691
        # This should conflict
1692
        builder = self.get_builder()
1693
        builder.build_snapshot('A-id', None,
1694
            [('add', (u'', 'a-root-id', 'directory', None)),
1695
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1696
        builder.build_snapshot('B-id', ['A-id'],
1697
            [('modify', ('foo-id', 'B content\n'))])
1698
        builder.build_snapshot('C-id', ['A-id'],
1699
            [('modify', ('foo-id', 'C content\n'))])
1700
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1701
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1702
        merge_obj = self.make_merge_obj(builder, 'E-id')
1703
1704
        entries = list(merge_obj._entries_lca())
1705
        root_id = 'a-root-id'
1706
        self.assertEqual([('foo-id', True,
1707
                           ((root_id, [root_id, root_id]), root_id, root_id),
1708
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1709
                           ((False, [False, False]), False, False)),
1710
                         ], entries)
1711
1712
    def test_different_lca_resolve_one_side_updates_content(self):
1713
        # Both sides converge, but then one side updates the text.
1714
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1715
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1716
        #       B C  B modifies 'foo', C modifies 'foo'
1717
        #       |X|
1718
        #       D E  D reverts to B, E reverts to C
1719
        #       |
1720
        #       F    F updates to a new value
1721
        # We need to emit an entry for 'foo', because D & E differed on the
1722
        # merge resolution
1723
        builder = self.get_builder()
1724
        builder.build_snapshot('A-id', None,
1725
            [('add', (u'', 'a-root-id', 'directory', None)),
1726
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1727
        builder.build_snapshot('B-id', ['A-id'],
1728
            [('modify', ('foo-id', 'B content\n'))])
1729
        builder.build_snapshot('C-id', ['A-id'],
1730
            [('modify', ('foo-id', 'C content\n'))])
1731
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1732
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1733
        builder.build_snapshot('F-id', ['D-id'],
1734
            [('modify', ('foo-id', 'F content\n'))])
1735
        merge_obj = self.make_merge_obj(builder, 'E-id')
1736
1737
        entries = list(merge_obj._entries_lca())
1738
        root_id = 'a-root-id'
1739
        self.assertEqual([('foo-id', True,
1740
                           ((root_id, [root_id, root_id]), root_id, root_id),
1741
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1742
                           ((False, [False, False]), False, False)),
1743
                         ], entries)
1744
1745
    def test_same_lca_resolution_one_side_updates_content(self):
1746
        # Both sides converge, but then one side updates the text.
1747
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
1748
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
1749
        #       B C  B modifies 'foo', C modifies 'foo'
1750
        #       |X|
1751
        #       D E  D and E use C's value
1752
        #       |
1753
        #       F    F updates to a new value
1754
        # I think it is a bug that this conflicts, but we don't have a way to
1755
        # detect otherwise. And because of:
1756
        #   test_different_lca_resolve_one_side_updates_content
1757
        # We need to conflict.
1758
1759
        builder = self.get_builder()
1760
        builder.build_snapshot('A-id', None,
1761
            [('add', (u'', 'a-root-id', 'directory', None)),
1762
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1763
        builder.build_snapshot('B-id', ['A-id'],
1764
            [('modify', ('foo-id', 'B content\n'))])
1765
        builder.build_snapshot('C-id', ['A-id'],
1766
            [('modify', ('foo-id', 'C content\n'))])
1767
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1768
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1769
            [('modify', ('foo-id', 'C content\n'))]) # Same as E
1770
        builder.build_snapshot('F-id', ['D-id'],
1771
            [('modify', ('foo-id', 'F content\n'))])
1772
        merge_obj = self.make_merge_obj(builder, 'E-id')
1773
1774
        entries = list(merge_obj._entries_lca())
1775
        self.expectFailure("We don't detect that LCA resolution was the"
1776
                           " same on both sides",
1777
            self.assertEqual, [], entries)
1778
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.
1779
    def test_only_path_changed(self):
3514.4.19 by John Arbash Meinel
Add the _lca_multi_way function, and explicit tests.
1780
        builder = self.get_builder()
1781
        builder.build_snapshot('A-id', None,
1782
            [('add', (u'', 'a-root-id', 'directory', None)),
1783
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1784
        builder.build_snapshot('B-id', ['A-id'], [])
1785
        builder.build_snapshot('C-id', ['A-id'], [])
1786
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1787
            [('rename', (u'a', u'b'))])
1788
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1789
        merge_obj = self.make_merge_obj(builder, 'E-id')
1790
        entries = list(merge_obj._entries_lca())
1791
        root_id = 'a-root-id'
1792
        # The content was not changed, only the path
1793
        self.assertEqual([('a-id', False,
1794
                           ((root_id, [root_id, root_id]), root_id, root_id),
1795
                           ((u'a', [u'a', u'a']), u'b', u'a'),
1796
                           ((False, [False, False]), False, False)),
1797
                         ], entries)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1798
1799
    def test_kind_changed(self):
1800
        # Identical content, except 'D' changes a-id into a directory
1801
        builder = self.get_builder()
1802
        builder.build_snapshot('A-id', None,
1803
            [('add', (u'', 'a-root-id', 'directory', None)),
1804
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1805
        builder.build_snapshot('B-id', ['A-id'], [])
1806
        builder.build_snapshot('C-id', ['A-id'], [])
1807
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1808
            [('unversion', 'a-id'),
1809
             ('add', (u'a', 'a-id', 'directory', None))])
1810
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1811
        merge_obj = self.make_merge_obj(builder, 'E-id')
1812
        entries = list(merge_obj._entries_lca())
1813
        root_id = 'a-root-id'
1814
        # Only the kind was changed (content)
1815
        self.assertEqual([('a-id', True,
1816
                           ((root_id, [root_id, root_id]), root_id, root_id),
1817
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1818
                           ((False, [False, False]), False, False)),
1819
                         ], entries)
1820
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
1821
    def test_this_changed_kind(self):
1822
        # Identical content, but THIS changes a file to a directory
1823
        builder = self.get_builder()
1824
        builder.build_snapshot('A-id', None,
1825
            [('add', (u'', 'a-root-id', 'directory', None)),
1826
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
1827
        builder.build_snapshot('B-id', ['A-id'], [])
1828
        builder.build_snapshot('C-id', ['A-id'], [])
1829
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1830
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1831
            [('unversion', 'a-id'),
1832
             ('add', (u'a', 'a-id', 'directory', None))])
1833
        merge_obj = self.make_merge_obj(builder, 'E-id')
1834
        entries = list(merge_obj._entries_lca())
1835
        # Only the kind was changed (content)
1836
        self.assertEqual([], entries)
1837
3514.4.24 by John Arbash Meinel
Implement support for 'interesting_files' and 'interesting_ids' for _entries_lca
1838
    def test_interesting_files(self):
1839
        # Two files modified, but we should filter one of them
1840
        builder = self.get_builder()
1841
        builder.build_snapshot('A-id', None,
1842
            [('add', (u'', 'a-root-id', 'directory', None)),
1843
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1844
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1845
        builder.build_snapshot('B-id', ['A-id'], [])
1846
        builder.build_snapshot('C-id', ['A-id'], [])
1847
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1848
            [('modify', ('a-id', 'new-content\n')),
1849
             ('modify', ('b-id', 'new-content\n'))])
1850
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1851
        merge_obj = self.make_merge_obj(builder, 'E-id',
1852
                                        interesting_files=['b'])
1853
        entries = list(merge_obj._entries_lca())
1854
        root_id = 'a-root-id'
1855
        self.assertEqual([('b-id', True,
1856
                           ((root_id, [root_id, root_id]), root_id, root_id),
1857
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1858
                           ((False, [False, False]), False, False)),
1859
                         ], entries)
1860
1861
    def test_interesting_file_in_this(self):
1862
        # This renamed the file, but it should still match the entry in other
1863
        builder = self.get_builder()
1864
        builder.build_snapshot('A-id', None,
1865
            [('add', (u'', 'a-root-id', 'directory', None)),
1866
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1867
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1868
        builder.build_snapshot('B-id', ['A-id'], [])
1869
        builder.build_snapshot('C-id', ['A-id'], [])
1870
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1871
            [('modify', ('a-id', 'new-content\n')),
1872
             ('modify', ('b-id', 'new-content\n'))])
1873
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1874
            [('rename', ('b', 'c'))])
1875
        merge_obj = self.make_merge_obj(builder, 'E-id',
1876
                                        interesting_files=['c'])
1877
        entries = list(merge_obj._entries_lca())
1878
        root_id = 'a-root-id'
1879
        self.assertEqual([('b-id', True,
1880
                           ((root_id, [root_id, root_id]), root_id, root_id),
1881
                           ((u'b', [u'b', u'b']), u'b', u'c'),
1882
                           ((False, [False, False]), False, False)),
1883
                         ], entries)
1884
1885
    def test_interesting_file_in_base(self):
1886
        # This renamed the file, but it should still match the entry in BASE
1887
        builder = self.get_builder()
1888
        builder.build_snapshot('A-id', None,
1889
            [('add', (u'', 'a-root-id', 'directory', None)),
1890
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1891
             ('add', (u'c', 'c-id', 'file', 'content\n'))])
1892
        builder.build_snapshot('B-id', ['A-id'],
1893
            [('rename', ('c', 'b'))])
1894
        builder.build_snapshot('C-id', ['A-id'],
1895
            [('rename', ('c', 'b'))])
1896
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1897
            [('modify', ('a-id', 'new-content\n')),
1898
             ('modify', ('c-id', 'new-content\n'))])
1899
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1900
        merge_obj = self.make_merge_obj(builder, 'E-id',
1901
                                        interesting_files=['c'])
1902
        entries = list(merge_obj._entries_lca())
1903
        root_id = 'a-root-id'
1904
        self.assertEqual([('c-id', True,
1905
                           ((root_id, [root_id, root_id]), root_id, root_id),
1906
                           ((u'c', [u'b', u'b']), u'b', u'b'),
1907
                           ((False, [False, False]), False, False)),
1908
                         ], entries)
1909
1910
    def test_interesting_file_in_lca(self):
1911
        # This renamed the file, but it should still match the entry in LCA
1912
        builder = self.get_builder()
1913
        builder.build_snapshot('A-id', None,
1914
            [('add', (u'', 'a-root-id', 'directory', None)),
1915
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1916
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1917
        builder.build_snapshot('B-id', ['A-id'],
1918
            [('rename', ('b', 'c'))])
1919
        builder.build_snapshot('C-id', ['A-id'], [])
1920
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1921
            [('modify', ('a-id', 'new-content\n')),
1922
             ('modify', ('b-id', 'new-content\n'))])
1923
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1924
            [('rename', ('c', 'b'))])
1925
        merge_obj = self.make_merge_obj(builder, 'E-id',
1926
                                        interesting_files=['c'])
1927
        entries = list(merge_obj._entries_lca())
1928
        root_id = 'a-root-id'
1929
        self.assertEqual([('b-id', True,
1930
                           ((root_id, [root_id, root_id]), root_id, root_id),
1931
                           ((u'b', [u'c', u'b']), u'b', u'b'),
1932
                           ((False, [False, False]), False, False)),
1933
                         ], entries)
1934
1935
    def test_interesting_ids(self):
1936
        # Two files modified, but we should filter one of them
1937
        builder = self.get_builder()
1938
        builder.build_snapshot('A-id', None,
1939
            [('add', (u'', 'a-root-id', 'directory', None)),
1940
             ('add', (u'a', 'a-id', 'file', 'content\n')),
1941
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
1942
        builder.build_snapshot('B-id', ['A-id'], [])
1943
        builder.build_snapshot('C-id', ['A-id'], [])
1944
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
1945
            [('modify', ('a-id', 'new-content\n')),
1946
             ('modify', ('b-id', 'new-content\n'))])
1947
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1948
        merge_obj = self.make_merge_obj(builder, 'E-id',
1949
                                        interesting_ids=['b-id'])
1950
        entries = list(merge_obj._entries_lca())
1951
        root_id = 'a-root-id'
1952
        self.assertEqual([('b-id', True,
1953
                           ((root_id, [root_id, root_id]), root_id, root_id),
1954
                           ((u'b', [u'b', u'b']), u'b', u'b'),
1955
                           ((False, [False, False]), False, False)),
1956
                         ], entries)
1957
1958
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1959
1960
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
1961
1962
    def get_builder(self):
1963
        builder = self.make_branch_builder('path')
1964
        builder.start_series()
1965
        self.addCleanup(builder.finish_series)
1966
        return builder
1967
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1968
    def get_wt_from_builder(self, builder):
1969
        """Get a real WorkingTree from the builder."""
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1970
        the_branch = builder.get_branch()
1971
        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
1972
        # Note: This is a little bit ugly, but we are holding the branch
1973
        #       write-locked as part of the build process, and we would like to
1974
        #       maintain that. So we just force the WT to re-use the same
1975
        #       branch object.
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1976
        wt._branch = the_branch
1977
        wt.lock_write()
1978
        self.addCleanup(wt.unlock)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
1979
        return wt
1980
1981
    def do_merge(self, builder, other_revision_id):
1982
        wt = self.get_wt_from_builder(builder)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
1983
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
1984
            wt, other_revision_id)
1985
        merger.merge_type = _mod_merge.Merge3Merger
1986
        return wt, merger.do_merge()
1987
1988
    def test_simple_lca(self):
1989
        builder = self.get_builder()
1990
        builder.build_snapshot('A-id', None,
1991
            [('add', (u'', 'a-root-id', 'directory', None)),
1992
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1993
        builder.build_snapshot('C-id', ['A-id'], [])
1994
        builder.build_snapshot('B-id', ['A-id'], [])
1995
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1996
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
1997
            [('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
1998
        wt, conflicts = self.do_merge(builder, 'E-id')
1999
        self.assertEqual(0, conflicts)
2000
        # The merge should have simply update the contents of 'a'
2001
        self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
2002
2003
    def test_conflict_without_lca(self):
2004
        # This test would cause a merge conflict, unless we use the lca trees
2005
        # to determine the real ancestry
2006
        #   A       Path at 'foo'
2007
        #  / \
2008
        # B   C     Path renamed to 'bar' in B
2009
        # |\ /|
2010
        # | X |
2011
        # |/ \|
2012
        # D   E     Path at 'bar' in D and E
2013
        #     |
2014
        #     F     Path at 'baz' in F, which supersedes 'bar' and 'foo'
2015
        builder = self.get_builder()
2016
        builder.build_snapshot('A-id', None,
2017
            [('add', (u'', 'a-root-id', 'directory', None)),
2018
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2019
        builder.build_snapshot('C-id', ['A-id'], [])
2020
        builder.build_snapshot('B-id', ['A-id'],
2021
            [('rename', ('foo', 'bar'))])
2022
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2023
            [('rename', ('foo', 'bar'))])
2024
        builder.build_snapshot('F-id', ['E-id'],
2025
            [('rename', ('bar', 'baz'))])
2026
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2027
        wt, conflicts = self.do_merge(builder, 'F-id')
2028
        self.assertEqual(0, conflicts)
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
2029
        # The merge should simply recognize that the final rename takes
2030
        # precedence
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2031
        self.assertEqual('baz', wt.id2path('foo-id'))
2032
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
2033
    def test_other_deletes_lca_renames(self):
2034
        # This test would cause a merge conflict, unless we use the lca trees
2035
        # to determine the real ancestry
2036
        #   A       Path at 'foo'
2037
        #  / \
2038
        # B   C     Path renamed to 'bar' in B
2039
        # |\ /|
2040
        # | X |
2041
        # |/ \|
2042
        # D   E     Path at 'bar' in D and E
2043
        #     |
2044
        #     F     F deletes 'bar'
2045
        builder = self.get_builder()
2046
        builder.build_snapshot('A-id', None,
2047
            [('add', (u'', 'a-root-id', 'directory', None)),
2048
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2049
        builder.build_snapshot('C-id', ['A-id'], [])
2050
        builder.build_snapshot('B-id', ['A-id'],
2051
            [('rename', ('foo', 'bar'))])
2052
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2053
            [('rename', ('foo', 'bar'))])
2054
        builder.build_snapshot('F-id', ['E-id'],
2055
            [('unversion', 'foo-id')])
2056
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2057
        wt, conflicts = self.do_merge(builder, 'F-id')
2058
        self.assertEqual(0, conflicts)
2059
        self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
2060
2061
    def test_executable_changes(self):
2062
        #   A       Path at 'foo'
2063
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2064
        # B   C
3514.4.22 by John Arbash Meinel
Handle executable bit changes as well.
2065
        # |\ /|
2066
        # | X |
2067
        # |/ \|
2068
        # D   E
2069
        #     |
2070
        #     F     Executable bit changed
2071
        builder = self.get_builder()
2072
        builder.build_snapshot('A-id', None,
2073
            [('add', (u'', 'a-root-id', 'directory', None)),
2074
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2075
        builder.build_snapshot('C-id', ['A-id'], [])
2076
        builder.build_snapshot('B-id', ['A-id'], [])
2077
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2078
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2079
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2080
        wt = self.get_wt_from_builder(builder)
2081
        tt = transform.TreeTransform(wt)
2082
        try:
2083
            tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2084
            tt.apply()
2085
        except:
2086
            tt.finalize()
2087
            raise
2088
        self.assertTrue(wt.is_executable('foo-id'))
2089
        wt.commit('F-id', rev_id='F-id')
2090
        # Reset to D, so that we can merge F
2091
        wt.set_parent_ids(['D-id'])
2092
        wt.branch.set_last_revision_info(3, 'D-id')
2093
        wt.revert()
2094
        self.assertFalse(wt.is_executable('foo-id'))
2095
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2096
        self.assertEqual(0, conflicts)
2097
        self.assertTrue(wt.is_executable('foo-id'))
2098
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2099
    def test_create_symlink(self):
2100
        self.requireFeature(tests.SymlinkFeature)
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2101
        #   A
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2102
        #  / \
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2103
        # B   C
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2104
        # |\ /|
2105
        # | X |
2106
        # |/ \|
2107
        # D   E
2108
        #     |
2109
        #     F     Add a symlink 'foo' => 'bar'
2110
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2111
        # have symlink support
2112
        builder = self.get_builder()
2113
        builder.build_snapshot('A-id', None,
2114
            [('add', (u'', 'a-root-id', 'directory', None))])
2115
        builder.build_snapshot('C-id', ['A-id'], [])
2116
        builder.build_snapshot('B-id', ['A-id'], [])
2117
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2118
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2119
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2120
        wt = self.get_wt_from_builder(builder)
2121
        os.symlink('bar', 'path/foo')
2122
        wt.add(['foo'], ['foo-id'])
2123
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2124
        wt.commit('add symlink', rev_id='F-id')
2125
        # Reset to D, so that we can merge F
2126
        wt.set_parent_ids(['D-id'])
2127
        wt.branch.set_last_revision_info(3, 'D-id')
2128
        wt.revert()
2129
        self.assertIs(None, wt.path2id('foo'))
2130
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2131
        self.assertEqual(0, conflicts)
2132
        self.assertEqual('foo-id', wt.path2id('foo'))
2133
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2134
3514.4.30 by John Arbash Meinel
Several updates.
2135
    def test_both_sides_revert(self):
2136
        # Both sides of a criss-cross revert the text to the lca
2137
        #       A    base, introduces 'foo'
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2138
        #       |\
3514.4.30 by John Arbash Meinel
Several updates.
2139
        #       B C  B modifies 'foo', C modifies 'foo'
2140
        #       |X|
2141
        #       D E  D reverts to B, E reverts to C
2142
        # This should conflict
2143
        # This must be done with a real WorkingTree, because normally their
2144
        # inventory contains "None" rather than a real sha1
2145
        builder = self.get_builder()
2146
        builder.build_snapshot('A-id', None,
2147
            [('add', (u'', 'a-root-id', 'directory', None)),
2148
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
2149
        builder.build_snapshot('B-id', ['A-id'],
2150
            [('modify', ('foo-id', 'B content\n'))])
2151
        builder.build_snapshot('C-id', ['A-id'],
2152
            [('modify', ('foo-id', 'C content\n'))])
2153
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2154
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2155
        wt, conflicts = self.do_merge(builder, 'E-id')
2156
        self.assertEqual(1, conflicts)
2157
        self.assertEqualDiff('<<<<<<< TREE\n'
2158
                             'B content\n'
2159
                             '=======\n'
2160
                             'C content\n'
2161
                             '>>>>>>> MERGE-SOURCE\n',
2162
                             wt.get_file_text('foo-id'))
2163
3514.4.28 by John Arbash Meinel
More symlink tests.
2164
    def test_modified_symlink(self):
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2165
        self.requireFeature(tests.SymlinkFeature)
2166
        #   A       Create symlink foo => bar
2167
        #  / \
2168
        # B   C     B relinks foo => baz
2169
        # |\ /|
2170
        # | X |
2171
        # |/ \|
2172
        # D   E     D & E have foo => baz
2173
        #     |
2174
        #     F     F changes it to bing
2175
        #
2176
        # 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
2177
        # value actually comes from B
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2178
2179
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2180
        # have symlink support
2181
        wt = self.make_branch_and_tree('path')
2182
        wt.lock_write()
2183
        self.addCleanup(wt.unlock)
2184
        os.symlink('bar', 'path/foo')
2185
        wt.add(['foo'], ['foo-id'])
2186
        wt.commit('add symlink', rev_id='A-id')
2187
        os.remove('path/foo')
2188
        os.symlink('baz', 'path/foo')
2189
        wt.commit('foo => baz', rev_id='B-id')
2190
        wt.set_last_revision('A-id')
2191
        wt.branch.set_last_revision_info(1, 'A-id')
2192
        wt.revert()
2193
        wt.commit('C', rev_id='C-id')
2194
        wt.merge_from_branch(wt.branch, 'B-id')
2195
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2196
        wt.commit('E merges C & B', rev_id='E-id')
2197
        os.remove('path/foo')
2198
        os.symlink('bing', 'path/foo')
2199
        wt.commit('F foo => bing', rev_id='F-id')
2200
        wt.set_last_revision('B-id')
2201
        wt.branch.set_last_revision_info(2, 'B-id')
2202
        wt.revert()
2203
        wt.merge_from_branch(wt.branch, 'C-id')
2204
        wt.commit('D merges B & C', rev_id='D-id')
2205
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
3948.1.7 by Vincent Ladeuil
Slight refactoring and test fixing.
2206
        self.assertEqual(0, conflicts)
3514.4.27 by John Arbash Meinel
A couple of symlink tests, we need to do more.
2207
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2208
3514.4.28 by John Arbash Meinel
More symlink tests.
2209
    def test_renamed_symlink(self):
2210
        self.requireFeature(tests.SymlinkFeature)
2211
        #   A       Create symlink foo => bar
2212
        #  / \
2213
        # B   C     B renames foo => barry
2214
        # |\ /|
2215
        # | X |
2216
        # |/ \|
2217
        # D   E     D & E have barry
2218
        #     |
2219
        #     F     F renames barry to blah
2220
        #
2221
        # 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
2222
        # value actually comes from B
3514.4.28 by John Arbash Meinel
More symlink tests.
2223
2224
        wt = self.make_branch_and_tree('path')
2225
        wt.lock_write()
2226
        self.addCleanup(wt.unlock)
2227
        os.symlink('bar', 'path/foo')
2228
        wt.add(['foo'], ['foo-id'])
2229
        wt.commit('A add symlink', rev_id='A-id')
2230
        wt.rename_one('foo', 'barry')
2231
        wt.commit('B foo => barry', rev_id='B-id')
2232
        wt.set_last_revision('A-id')
2233
        wt.branch.set_last_revision_info(1, 'A-id')
2234
        wt.revert()
2235
        wt.commit('C', rev_id='C-id')
2236
        wt.merge_from_branch(wt.branch, 'B-id')
2237
        self.assertEqual('barry', wt.id2path('foo-id'))
2238
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2239
        wt.commit('E merges C & B', rev_id='E-id')
2240
        wt.rename_one('barry', 'blah')
2241
        wt.commit('F barry => blah', rev_id='F-id')
2242
        wt.set_last_revision('B-id')
2243
        wt.branch.set_last_revision_info(2, 'B-id')
2244
        wt.revert()
2245
        wt.merge_from_branch(wt.branch, 'C-id')
2246
        wt.commit('D merges B & C', rev_id='D-id')
2247
        self.assertEqual('barry', wt.id2path('foo-id'))
2248
        # Check the output of the Merger object directly
2249
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2250
            wt, 'F-id')
2251
        merger.merge_type = _mod_merge.Merge3Merger
2252
        merge_obj = merger.make_merger()
2253
        root_id = wt.path2id('')
2254
        entries = list(merge_obj._entries_lca())
2255
        # No content change, just a path change
2256
        self.assertEqual([('foo-id', False,
2257
                           ((root_id, [root_id, root_id]), root_id, root_id),
2258
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2259
                           ((False, [False, False]), False, False)),
2260
                         ], entries)
2261
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2262
        self.assertEqual(0, conflicts)
2263
        self.assertEqual('blah', wt.id2path('foo-id'))
2264
2265
    def test_symlink_no_content_change(self):
2266
        self.requireFeature(tests.SymlinkFeature)
2267
        #   A       Create symlink foo => bar
2268
        #  / \
2269
        # B   C     B relinks foo => baz
2270
        # |\ /|
2271
        # | X |
2272
        # |/ \|
2273
        # D   E     D & E have foo => baz
2274
        # |
2275
        # F         F has foo => bing
2276
        #
2277
        # Merging E into F should not cause a conflict, because E doesn't have
2278
        # a content change relative to the LCAs (it does relative to A)
2279
        wt = self.make_branch_and_tree('path')
2280
        wt.lock_write()
2281
        self.addCleanup(wt.unlock)
2282
        os.symlink('bar', 'path/foo')
2283
        wt.add(['foo'], ['foo-id'])
2284
        wt.commit('add symlink', rev_id='A-id')
2285
        os.remove('path/foo')
2286
        os.symlink('baz', 'path/foo')
2287
        wt.commit('foo => baz', rev_id='B-id')
2288
        wt.set_last_revision('A-id')
2289
        wt.branch.set_last_revision_info(1, 'A-id')
2290
        wt.revert()
2291
        wt.commit('C', rev_id='C-id')
2292
        wt.merge_from_branch(wt.branch, 'B-id')
2293
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2294
        wt.commit('E merges C & B', rev_id='E-id')
2295
        wt.set_last_revision('B-id')
2296
        wt.branch.set_last_revision_info(2, 'B-id')
2297
        wt.revert()
2298
        wt.merge_from_branch(wt.branch, 'C-id')
2299
        wt.commit('D merges B & C', rev_id='D-id')
2300
        os.remove('path/foo')
2301
        os.symlink('bing', 'path/foo')
2302
        wt.commit('F foo => bing', rev_id='F-id')
2303
2304
        # Check the output of the Merger object directly
2305
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2306
            wt, 'E-id')
2307
        merger.merge_type = _mod_merge.Merge3Merger
2308
        merge_obj = merger.make_merger()
2309
        # Nothing interesting happened in OTHER relative to BASE
2310
        self.assertEqual([], list(merge_obj._entries_lca()))
2311
        # Now do a real merge, just to test the rest of the stack
2312
        conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2313
        self.assertEqual(0, conflicts)
2314
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2315
3514.4.35 by John Arbash Meinel
Add a test that we only call get_symlink_target if the object should be a symlink.
2316
    def test_symlink_this_changed_kind(self):
2317
        self.requireFeature(tests.SymlinkFeature)
2318
        #   A       Nothing
2319
        #  / \
2320
        # B   C     B creates symlink foo => bar
2321
        # |\ /|
2322
        # | X |
2323
        # |/ \|
2324
        # D   E     D changes foo into a file, E has foo => bing
2325
        #
2326
        # Mostly, this is trying to test that we don't try to os.readlink() on
2327
        # a file, or when there is nothing there
2328
        wt = self.make_branch_and_tree('path')
2329
        wt.lock_write()
2330
        self.addCleanup(wt.unlock)
2331
        wt.commit('base', rev_id='A-id')
2332
        os.symlink('bar', 'path/foo')
2333
        wt.add(['foo'], ['foo-id'])
2334
        wt.commit('add symlink foo => bar', rev_id='B-id')
2335
        wt.set_last_revision('A-id')
2336
        wt.branch.set_last_revision_info(1, 'A-id')
2337
        wt.revert()
2338
        wt.commit('C', rev_id='C-id')
2339
        wt.merge_from_branch(wt.branch, 'B-id')
2340
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2341
        os.remove('path/foo')
2342
        # We have to change the link in E, or it won't try to do a comparison
2343
        os.symlink('bing', 'path/foo')
2344
        wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2345
        wt.set_last_revision('B-id')
2346
        wt.branch.set_last_revision_info(2, 'B-id')
2347
        wt.revert()
2348
        wt.merge_from_branch(wt.branch, 'C-id')
2349
        os.remove('path/foo')
2350
        self.build_tree_contents([('path/foo', 'file content\n')])
2351
        # XXX: workaround, WT doesn't detect kind changes unless you do
2352
        # iter_changes()
2353
        list(wt.iter_changes(wt.basis_tree()))
2354
        wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2355
2356
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2357
            wt, 'E-id')
2358
        merger.merge_type = _mod_merge.Merge3Merger
2359
        merge_obj = merger.make_merger()
2360
        entries = list(merge_obj._entries_lca())
2361
        root_id = wt.path2id('')
2362
        self.assertEqual([('foo-id', True,
2363
                           ((None, [root_id, None]), root_id, root_id),
2364
                           ((None, [u'foo', None]), u'foo', u'foo'),
2365
                           ((None, [False, None]), False, False)),
2366
                         ], entries)
2367
3514.4.34 by John Arbash Meinel
Handle symlinks properly when objects are not RevisionTrees
2368
    def test_symlink_all_wt(self):
2369
        """Check behavior if all trees are Working Trees."""
2370
        self.requireFeature(tests.SymlinkFeature)
2371
        # The big issue is that entry.symlink_target is None for WorkingTrees.
2372
        # So we need to make sure we handle that case correctly.
2373
        #   A   foo => bar
2374
        #   |\
2375
        #   B C B relinks foo => baz
2376
        #   |X|
2377
        #   D E D & E have foo => baz
2378
        #     |
2379
        #     F F changes it to bing
2380
        # Merging D & F should result in F cleanly overriding D, because D's
2381
        # value actually comes from B
2382
2383
        wt = self.make_branch_and_tree('path')
2384
        wt.lock_write()
2385
        self.addCleanup(wt.unlock)
2386
        os.symlink('bar', 'path/foo')
2387
        wt.add(['foo'], ['foo-id'])
2388
        wt.commit('add symlink', rev_id='A-id')
2389
        os.remove('path/foo')
2390
        os.symlink('baz', 'path/foo')
2391
        wt.commit('foo => baz', rev_id='B-id')
2392
        wt.set_last_revision('A-id')
2393
        wt.branch.set_last_revision_info(1, 'A-id')
2394
        wt.revert()
2395
        wt.commit('C', rev_id='C-id')
2396
        wt.merge_from_branch(wt.branch, 'B-id')
2397
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2398
        wt.commit('E merges C & B', rev_id='E-id')
2399
        os.remove('path/foo')
2400
        os.symlink('bing', 'path/foo')
2401
        wt.commit('F foo => bing', rev_id='F-id')
2402
        wt.set_last_revision('B-id')
2403
        wt.branch.set_last_revision_info(2, 'B-id')
2404
        wt.revert()
2405
        wt.merge_from_branch(wt.branch, 'C-id')
2406
        wt.commit('D merges B & C', rev_id='D-id')
2407
        wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2408
        wt_base.lock_read()
2409
        self.addCleanup(wt_base.unlock)
2410
        wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2411
        wt_lca1.lock_read()
2412
        self.addCleanup(wt_lca1.unlock)
2413
        wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2414
        wt_lca2.lock_read()
2415
        self.addCleanup(wt_lca2.unlock)
2416
        wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2417
        wt_other.lock_read()
2418
        self.addCleanup(wt_other.unlock)
2419
        merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2420
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2421
        entries = list(merge_obj._entries_lca())
2422
        root_id = wt.path2id('')
2423
        self.assertEqual([('foo-id', True,
2424
                           ((root_id, [root_id, root_id]), root_id, root_id),
2425
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2426
                           ((False, [False, False]), False, False)),
2427
                         ], entries)
2428
3514.4.25 by John Arbash Meinel
A few more merge-level behavior tests.
2429
    def test_other_reverted_path_to_base(self):
2430
        #   A       Path at 'foo'
2431
        #  / \
2432
        # B   C     Path at 'bar' in B
2433
        # |\ /|
2434
        # | X |
2435
        # |/ \|
2436
        # D   E     Path at 'bar'
2437
        #     |
2438
        #     F     Path at 'foo'
2439
        builder = self.get_builder()
2440
        builder.build_snapshot('A-id', None,
2441
            [('add', (u'', 'a-root-id', 'directory', None)),
2442
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2443
        builder.build_snapshot('C-id', ['A-id'], [])
2444
        builder.build_snapshot('B-id', ['A-id'],
2445
            [('rename', ('foo', 'bar'))])
2446
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2447
            [('rename', ('foo', 'bar'))]) # merge the rename
2448
        builder.build_snapshot('F-id', ['E-id'],
2449
            [('rename', ('bar', 'foo'))]) # Rename back to BASE
2450
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2451
        wt, conflicts = self.do_merge(builder, 'F-id')
2452
        self.assertEqual(0, conflicts)
2453
        self.assertEqual('foo', wt.id2path('foo-id'))
2454
2455
    def test_other_reverted_content_to_base(self):
2456
        builder = self.get_builder()
2457
        builder.build_snapshot('A-id', None,
2458
            [('add', (u'', 'a-root-id', 'directory', None)),
2459
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2460
        builder.build_snapshot('C-id', ['A-id'], [])
2461
        builder.build_snapshot('B-id', ['A-id'],
2462
            [('modify', ('foo-id', 'B content\n'))])
2463
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2464
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2465
        builder.build_snapshot('F-id', ['E-id'],
2466
            [('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2467
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2468
        wt, conflicts = self.do_merge(builder, 'F-id')
2469
        self.assertEqual(0, conflicts)
2470
        # 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.
2471
        #       before this will work. Or at least use the LCA trees to find
2472
        #       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.
2473
        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.
2474
3514.4.28 by John Arbash Meinel
More symlink tests.
2475
    def test_other_modified_content(self):
2476
        builder = self.get_builder()
2477
        builder.build_snapshot('A-id', None,
2478
            [('add', (u'', 'a-root-id', 'directory', None)),
2479
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2480
        builder.build_snapshot('C-id', ['A-id'], [])
2481
        builder.build_snapshot('B-id', ['A-id'],
2482
            [('modify', ('foo-id', 'B content\n'))])
2483
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2484
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
2485
        builder.build_snapshot('F-id', ['E-id'],
2486
            [('modify', ('foo-id', 'F content\n'))]) # Override B content
2487
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2488
        wt, conflicts = self.do_merge(builder, 'F-id')
2489
        self.assertEqual(0, conflicts)
2490
        self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2491
3514.4.32 by John Arbash Meinel
Add a test for proper behavior when *everything* is a WT.
2492
    def test_all_wt(self):
2493
        """Check behavior if all trees are Working Trees."""
2494
        # The big issue is that entry.revision is None for WorkingTrees. (as is
2495
        # entry.text_sha1, etc. So we need to make sure we handle that case
2496
        # correctly.
2497
        #   A   Content of 'foo', path of 'a'
2498
        #   |\
2499
        #   B C B modifies content, C renames 'a' => 'b'
2500
        #   |X|
2501
        #   D E E updates content, renames 'b' => 'c'
2502
        builder = self.get_builder()
2503
        builder.build_snapshot('A-id', None,
2504
            [('add', (u'', 'a-root-id', 'directory', None)),
2505
             ('add', (u'a', 'a-id', 'file', 'base content\n')),
2506
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2507
        builder.build_snapshot('B-id', ['A-id'],
2508
            [('modify', ('foo-id', 'B content\n'))])
2509
        builder.build_snapshot('C-id', ['A-id'],
2510
            [('rename', ('a', 'b'))])
2511
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
2512
            [('rename', ('b', 'c')),
2513
             ('modify', ('foo-id', 'E content\n'))])
2514
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
2515
            [('rename', ('a', 'b'))]) # merged change
2516
        wt_this = self.get_wt_from_builder(builder)
2517
        wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2518
        wt_base.lock_read()
2519
        self.addCleanup(wt_base.unlock)
2520
        wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2521
        wt_lca1.lock_read()
2522
        self.addCleanup(wt_lca1.unlock)
2523
        wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2524
        wt_lca2.lock_read()
2525
        self.addCleanup(wt_lca2.unlock)
2526
        wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2527
        wt_other.lock_read()
2528
        self.addCleanup(wt_other.unlock)
2529
        merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2530
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2531
        entries = list(merge_obj._entries_lca())
2532
        root_id = 'a-root-id'
2533
        self.assertEqual([('a-id', False,
2534
                           ((root_id, [root_id, root_id]), root_id, root_id),
2535
                           ((u'a', [u'a', u'b']), u'c', u'b'),
2536
                           ((False, [False, False]), False, False)),
2537
                          ('foo-id', True,
2538
                           ((root_id, [root_id, root_id]), root_id, root_id),
2539
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2540
                           ((False, [False, False]), False, False)),
2541
                         ], entries)
2542
3514.4.33 by John Arbash Meinel
Implement support for 'tree-reference'.
2543
    def test_nested_tree_unmodified(self):
2544
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2545
        # 'tree-reference'
2546
        wt = self.make_branch_and_tree('tree',
2547
            format='dirstate-with-subtree')
2548
        wt.lock_write()
2549
        self.addCleanup(wt.unlock)
2550
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2551
            format='dirstate-with-subtree')
2552
        wt.set_root_id('a-root-id')
2553
        sub_tree.set_root_id('sub-tree-root')
2554
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2555
        sub_tree.add('file')
2556
        sub_tree.commit('foo', rev_id='sub-A-id')
2557
        wt.add_reference(sub_tree)
2558
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2559
        # Now create a criss-cross merge in the parent, without modifying the
2560
        # subtree
2561
        wt.commit('B', rev_id='B-id', recursive=None)
2562
        wt.set_last_revision('A-id')
2563
        wt.branch.set_last_revision_info(1, 'A-id')
2564
        wt.commit('C', rev_id='C-id', recursive=None)
2565
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2566
        wt.commit('E', rev_id='E-id', recursive=None)
2567
        wt.set_parent_ids(['B-id', 'C-id'])
2568
        wt.branch.set_last_revision_info(2, 'B-id')
2569
        wt.commit('D', rev_id='D-id', recursive=None)
2570
2571
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2572
            wt, 'E-id')
2573
        merger.merge_type = _mod_merge.Merge3Merger
2574
        merge_obj = merger.make_merger()
2575
        entries = list(merge_obj._entries_lca())
2576
        self.assertEqual([], entries)
2577
2578
    def test_nested_tree_subtree_modified(self):
2579
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2580
        # 'tree-reference'
2581
        wt = self.make_branch_and_tree('tree',
2582
            format='dirstate-with-subtree')
2583
        wt.lock_write()
2584
        self.addCleanup(wt.unlock)
2585
        sub_tree = self.make_branch_and_tree('tree/sub',
2586
            format='dirstate-with-subtree')
2587
        wt.set_root_id('a-root-id')
2588
        sub_tree.set_root_id('sub-tree-root')
2589
        self.build_tree_contents([('tree/sub/file', 'text1')])
2590
        sub_tree.add('file')
2591
        sub_tree.commit('foo', rev_id='sub-A-id')
2592
        wt.add_reference(sub_tree)
2593
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2594
        # Now create a criss-cross merge in the parent, without modifying the
2595
        # subtree
2596
        wt.commit('B', rev_id='B-id', recursive=None)
2597
        wt.set_last_revision('A-id')
2598
        wt.branch.set_last_revision_info(1, 'A-id')
2599
        wt.commit('C', rev_id='C-id', recursive=None)
2600
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2601
        self.build_tree_contents([('tree/sub/file', 'text2')])
2602
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2603
        wt.commit('E', rev_id='E-id', recursive=None)
2604
        wt.set_parent_ids(['B-id', 'C-id'])
2605
        wt.branch.set_last_revision_info(2, 'B-id')
2606
        wt.commit('D', rev_id='D-id', recursive=None)
2607
2608
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2609
            wt, 'E-id')
2610
        merger.merge_type = _mod_merge.Merge3Merger
2611
        merge_obj = merger.make_merger()
2612
        entries = list(merge_obj._entries_lca())
2613
        # Nothing interesting about this sub-tree, because content changes are
2614
        # computed at a higher level
2615
        self.assertEqual([], entries)
2616
2617
    def test_nested_tree_subtree_renamed(self):
2618
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2619
        # 'tree-reference'
2620
        wt = self.make_branch_and_tree('tree',
2621
            format='dirstate-with-subtree')
2622
        wt.lock_write()
2623
        self.addCleanup(wt.unlock)
2624
        sub_tree = self.make_branch_and_tree('tree/sub',
2625
            format='dirstate-with-subtree')
2626
        wt.set_root_id('a-root-id')
2627
        sub_tree.set_root_id('sub-tree-root')
2628
        self.build_tree_contents([('tree/sub/file', 'text1')])
2629
        sub_tree.add('file')
2630
        sub_tree.commit('foo', rev_id='sub-A-id')
2631
        wt.add_reference(sub_tree)
2632
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2633
        # Now create a criss-cross merge in the parent, without modifying the
2634
        # subtree
2635
        wt.commit('B', rev_id='B-id', recursive=None)
2636
        wt.set_last_revision('A-id')
2637
        wt.branch.set_last_revision_info(1, 'A-id')
2638
        wt.commit('C', rev_id='C-id', recursive=None)
2639
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2640
        wt.rename_one('sub', 'alt_sub')
2641
        wt.commit('E', rev_id='E-id', recursive=None)
2642
        wt.set_last_revision('B-id')
2643
        wt.revert()
2644
        wt.set_parent_ids(['B-id', 'C-id'])
2645
        wt.branch.set_last_revision_info(2, 'B-id')
2646
        wt.commit('D', rev_id='D-id', recursive=None)
2647
2648
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2649
            wt, 'E-id')
2650
        merger.merge_type = _mod_merge.Merge3Merger
2651
        merge_obj = merger.make_merger()
2652
        entries = list(merge_obj._entries_lca())
2653
        root_id = 'a-root-id'
2654
        self.assertEqual([('sub-tree-root', False,
2655
                           ((root_id, [root_id, root_id]), root_id, root_id),
2656
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2657
                           ((False, [False, False]), False, False)),
2658
                         ], entries)
2659
2660
    def test_nested_tree_subtree_renamed_and_modified(self):
2661
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2662
        # 'tree-reference'
2663
        wt = self.make_branch_and_tree('tree',
2664
            format='dirstate-with-subtree')
2665
        wt.lock_write()
2666
        self.addCleanup(wt.unlock)
2667
        sub_tree = self.make_branch_and_tree('tree/sub',
2668
            format='dirstate-with-subtree')
2669
        wt.set_root_id('a-root-id')
2670
        sub_tree.set_root_id('sub-tree-root')
2671
        self.build_tree_contents([('tree/sub/file', 'text1')])
2672
        sub_tree.add('file')
2673
        sub_tree.commit('foo', rev_id='sub-A-id')
2674
        wt.add_reference(sub_tree)
2675
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2676
        # Now create a criss-cross merge in the parent, without modifying the
2677
        # subtree
2678
        wt.commit('B', rev_id='B-id', recursive=None)
2679
        wt.set_last_revision('A-id')
2680
        wt.branch.set_last_revision_info(1, 'A-id')
2681
        wt.commit('C', rev_id='C-id', recursive=None)
2682
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2683
        self.build_tree_contents([('tree/sub/file', 'text2')])
2684
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2685
        wt.rename_one('sub', 'alt_sub')
2686
        wt.commit('E', rev_id='E-id', recursive=None)
2687
        wt.set_last_revision('B-id')
2688
        wt.revert()
2689
        wt.set_parent_ids(['B-id', 'C-id'])
2690
        wt.branch.set_last_revision_info(2, 'B-id')
2691
        wt.commit('D', rev_id='D-id', recursive=None)
2692
2693
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
2694
            wt, 'E-id')
2695
        merger.merge_type = _mod_merge.Merge3Merger
2696
        merge_obj = merger.make_merger()
2697
        entries = list(merge_obj._entries_lca())
2698
        root_id = 'a-root-id'
2699
        self.assertEqual([('sub-tree-root', False,
2700
                           ((root_id, [root_id, root_id]), root_id, root_id),
2701
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2702
                           ((False, [False, False]), False, False)),
2703
                         ], entries)
2704
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2705
2706
class TestLCAMultiWay(tests.TestCase):
2707
3514.4.30 by John Arbash Meinel
Several updates.
2708
    def assertLCAMultiWay(self, expected, base, lcas, other, this,
2709
                          allow_overriding_lca=True):
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2710
        self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
3514.4.30 by John Arbash Meinel
Several updates.
2711
                                (base, lcas), other, this,
2712
                                allow_overriding_lca=allow_overriding_lca))
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2713
2714
    def test_other_equal_equal_lcas(self):
2715
        """Test when OTHER=LCA and all LCAs are identical."""
2716
        self.assertLCAMultiWay('this',
2717
            'bval', ['bval', 'bval'], 'bval', 'bval')
2718
        self.assertLCAMultiWay('this',
2719
            'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2720
        self.assertLCAMultiWay('this',
2721
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2722
        self.assertLCAMultiWay('this',
2723
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2724
        self.assertLCAMultiWay('this',
2725
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2726
2727
    def test_other_equal_this(self):
2728
        """Test when other and this are identical."""
2729
        self.assertLCAMultiWay('this',
2730
            'bval', ['bval', 'bval'], 'oval', 'oval')
2731
        self.assertLCAMultiWay('this',
2732
            'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2733
        self.assertLCAMultiWay('this',
2734
            'bval', ['cval', 'dval'], 'oval', 'oval')
2735
        self.assertLCAMultiWay('this',
2736
            'bval', [None, 'lcaval'], 'oval', 'oval')
2737
        self.assertLCAMultiWay('this',
2738
            None, [None, 'lcaval'], 'oval', 'oval')
2739
        self.assertLCAMultiWay('this',
2740
            None, ['lcaval', 'lcaval'], 'oval', 'oval')
2741
        self.assertLCAMultiWay('this',
2742
            None, ['cval', 'dval'], 'oval', 'oval')
2743
        self.assertLCAMultiWay('this',
2744
            None, ['cval', 'dval'], None, None)
2745
        self.assertLCAMultiWay('this',
2746
            None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2747
2748
    def test_no_lcas(self):
2749
        self.assertLCAMultiWay('this',
2750
            'bval', [], 'bval', 'tval')
2751
        self.assertLCAMultiWay('other',
2752
            'bval', [], 'oval', 'bval')
2753
        self.assertLCAMultiWay('conflict',
2754
            'bval', [], 'oval', 'tval')
2755
        self.assertLCAMultiWay('this',
2756
            'bval', [], 'oval', 'oval')
2757
2758
    def test_lca_supersedes_other_lca(self):
2759
        """If one lca == base, the other lca takes precedence"""
2760
        self.assertLCAMultiWay('this',
2761
            'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2762
        self.assertLCAMultiWay('this',
2763
            'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2764
        # This is actually considered a 'revert' because the 'lcaval' in LCAS
2765
        # supersedes the BASE val (in the other LCA) but then OTHER reverts it
2766
        # back to bval.
2767
        self.assertLCAMultiWay('other',
2768
            'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2769
        self.assertLCAMultiWay('conflict',
2770
            'bval', ['bval', 'lcaval'], 'bval', 'tval')
2771
2772
    def test_other_and_this_pick_different_lca(self):
2773
        # OTHER and THIS resolve the lca conflict in different ways
2774
        self.assertLCAMultiWay('conflict',
2775
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2776
        self.assertLCAMultiWay('conflict',
2777
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2778
        self.assertLCAMultiWay('conflict',
2779
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2780
2781
    def test_other_in_lca(self):
2782
        # OTHER takes a value of one of the LCAs, THIS takes a new value, which
2783
        # theoretically supersedes both LCA values and 'wins'
2784
        self.assertLCAMultiWay('this',
2785
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2786
        self.assertLCAMultiWay('this',
2787
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
3514.4.30 by John Arbash Meinel
Several updates.
2788
        self.assertLCAMultiWay('conflict',
2789
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2790
            allow_overriding_lca=False)
2791
        self.assertLCAMultiWay('conflict',
2792
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2793
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2794
        # THIS reverted back to BASE, but that is an explicit supersede of all
2795
        # LCAs
2796
        self.assertLCAMultiWay('this',
2797
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2798
        self.assertLCAMultiWay('this',
2799
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3514.4.30 by John Arbash Meinel
Several updates.
2800
        self.assertLCAMultiWay('conflict',
2801
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2802
            allow_overriding_lca=False)
2803
        self.assertLCAMultiWay('conflict',
2804
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2805
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2806
2807
    def test_this_in_lca(self):
2808
        # THIS takes a value of one of the LCAs, OTHER takes a new value, which
2809
        # theoretically supersedes both LCA values and 'wins'
2810
        self.assertLCAMultiWay('other',
2811
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2812
        self.assertLCAMultiWay('other',
2813
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3514.4.30 by John Arbash Meinel
Several updates.
2814
        self.assertLCAMultiWay('conflict',
2815
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2816
            allow_overriding_lca=False)
2817
        self.assertLCAMultiWay('conflict',
2818
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2819
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2820
        # OTHER reverted back to BASE, but that is an explicit supersede of all
2821
        # LCAs
2822
        self.assertLCAMultiWay('other',
2823
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
3514.4.30 by John Arbash Meinel
Several updates.
2824
        self.assertLCAMultiWay('conflict',
2825
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2826
            allow_overriding_lca=False)
3514.4.21 by John Arbash Meinel
Hook up the lca-way logic into the path handlers.
2827
2828
    def test_all_differ(self):
2829
        self.assertLCAMultiWay('conflict',
2830
            'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2831
        self.assertLCAMultiWay('conflict',
2832
            'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2833
        self.assertLCAMultiWay('conflict',
2834
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')