/brz/remove-bazaar

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