/brz/remove-bazaar

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