~bzr-pqm/bzr/bzr.dev

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