/brz/remove-bazaar

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