/brz/remove-bazaar

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