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