~bzr-pqm/bzr/bzr.dev

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