/brz/remove-bazaar

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