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