~bzr-pqm/bzr/bzr.dev

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