/brz/remove-bazaar

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