/brz/remove-bazaar

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