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