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