~bzr-pqm/bzr/bzr.dev

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