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