4634.149.1
by Vincent Ladeuil
Fix imports. |
1 |
# Copyright (C) 2006-2010 Canonical Ltd
|
1979.2.1
by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree. |
2 |
# Authors: Robert Collins <robert.collins@canonical.com>
|
3 |
#
|
|
4 |
# This program is free software; you can redistribute it and/or modify
|
|
5 |
# it under the terms of the GNU General Public License as published by
|
|
6 |
# the Free Software Foundation; either version 2 of the License, or
|
|
7 |
# (at your option) any later version.
|
|
8 |
#
|
|
9 |
# This program is distributed in the hope that it will be useful,
|
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
# GNU General Public License for more details.
|
|
13 |
#
|
|
14 |
# You should have received a copy of the GNU General Public License
|
|
15 |
# along with this program; if not, write to the Free Software
|
|
4183.7.1
by Sabin Iacob
update FSF mailing address |
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
1979.2.1
by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree. |
17 |
|
18 |
"""Tests for the WorkingTree.merge_from_branch api."""
|
|
19 |
||
1551.10.31
by Aaron Bentley
Fix WorkingTree4._iter_changes with pending merges and deleted files |
20 |
import os |
21 |
||
1551.15.69
by Aaron Bentley
Add merge_type to merge_from_branch |
22 |
from bzrlib import ( |
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
23 |
conflicts, |
1551.15.69
by Aaron Bentley
Add merge_type to merge_from_branch |
24 |
errors, |
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
25 |
merge, |
1551.15.69
by Aaron Bentley
Add merge_type to merge_from_branch |
26 |
)
|
4634.149.1
by Vincent Ladeuil
Fix imports. |
27 |
from bzrlib.tests import per_workingtree |
28 |
||
29 |
||
30 |
class TestMergeFromBranch(per_workingtree.TestCaseWithWorkingTree): |
|
1979.2.1
by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree. |
31 |
|
32 |
def create_two_trees_for_merging(self): |
|
33 |
"""Create two trees that can be merged from.
|
|
34 |
||
35 |
This sets self.tree_from, self.first_rev, self.tree_to, self.second_rev
|
|
36 |
and self.to_second_rev.
|
|
37 |
"""
|
|
38 |
self.tree_from = self.make_branch_and_tree('from') |
|
39 |
self.first_rev = self.tree_from.commit('first post') |
|
40 |
self.tree_to = self.tree_from.bzrdir.sprout('to').open_workingtree() |
|
41 |
self.second_rev = self.tree_from.commit('second rev', allow_pointless=True) |
|
42 |
self.to_second_rev = self.tree_to.commit('second rev', allow_pointless=True) |
|
43 |
||
44 |
def test_smoking_merge(self): |
|
45 |
"""Smoke test of merge_from_branch."""
|
|
46 |
self.create_two_trees_for_merging() |
|
47 |
self.tree_to.merge_from_branch(self.tree_from.branch) |
|
48 |
self.assertEqual([self.to_second_rev, self.second_rev], |
|
49 |
self.tree_to.get_parent_ids()) |
|
50 |
||
51 |
def test_merge_to_revision(self): |
|
52 |
"""Merge from a branch to a revision that is not the tip."""
|
|
53 |
self.create_two_trees_for_merging() |
|
54 |
self.third_rev = self.tree_from.commit('real_tip') |
|
55 |
self.tree_to.merge_from_branch(self.tree_from.branch, |
|
56 |
to_revision=self.second_rev) |
|
57 |
self.assertEqual([self.to_second_rev, self.second_rev], |
|
58 |
self.tree_to.get_parent_ids()) |
|
1551.10.31
by Aaron Bentley
Fix WorkingTree4._iter_changes with pending merges and deleted files |
59 |
|
60 |
def test_compare_after_merge(self): |
|
61 |
tree_a = self.make_branch_and_tree('tree_a') |
|
62 |
self.build_tree_contents([('tree_a/file', 'text-a')]) |
|
63 |
tree_a.add('file') |
|
64 |
tree_a.commit('added file') |
|
65 |
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree() |
|
66 |
os.unlink('tree_a/file') |
|
67 |
tree_a.commit('deleted file') |
|
68 |
self.build_tree_contents([('tree_b/file', 'text-b')]) |
|
69 |
tree_b.commit('changed file') |
|
70 |
tree_a.merge_from_branch(tree_b.branch) |
|
71 |
tree_a.lock_read() |
|
72 |
self.addCleanup(tree_a.unlock) |
|
3254.1.1
by Aaron Bentley
Make Tree.iter_changes a public method |
73 |
list(tree_a.iter_changes(tree_a.basis_tree())) |
2490.2.28
by Aaron Bentley
Fix handling of null revision |
74 |
|
75 |
def test_merge_empty(self): |
|
76 |
tree_a = self.make_branch_and_tree('tree_a') |
|
77 |
self.build_tree_contents([('tree_a/file', 'text-a')]) |
|
78 |
tree_a.add('file') |
|
79 |
tree_a.commit('added file') |
|
80 |
tree_b = self.make_branch_and_tree('treeb') |
|
81 |
self.assertRaises(errors.NoCommits, tree_a.merge_from_branch, |
|
82 |
tree_b.branch) |
|
83 |
tree_b.merge_from_branch(tree_a.branch) |
|
1551.15.68
by Aaron Bentley
Add support for base to merge_from_branch |
84 |
|
85 |
def test_merge_base(self): |
|
86 |
tree_a = self.make_branch_and_tree('tree_a') |
|
87 |
self.build_tree_contents([('tree_a/file', 'text-a')]) |
|
88 |
tree_a.add('file') |
|
89 |
tree_a.commit('added file', rev_id='rev_1') |
|
90 |
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree() |
|
91 |
os.unlink('tree_a/file') |
|
92 |
tree_a.commit('deleted file') |
|
93 |
self.build_tree_contents([('tree_b/file', 'text-b')]) |
|
94 |
tree_b.commit('changed file') |
|
95 |
self.assertRaises(errors.PointlessMerge, tree_a.merge_from_branch, |
|
96 |
tree_b.branch, from_revision=tree_b.branch.last_revision()) |
|
97 |
tree_a.merge_from_branch(tree_b.branch, from_revision='rev_1') |
|
98 |
tree_a.lock_read() |
|
99 |
self.addCleanup(tree_a.unlock) |
|
3254.1.1
by Aaron Bentley
Make Tree.iter_changes a public method |
100 |
changes = list(tree_a.iter_changes(tree_a.basis_tree())) |
1551.15.68
by Aaron Bentley
Add support for base to merge_from_branch |
101 |
self.assertEqual(1, len(changes)) |
1551.15.69
by Aaron Bentley
Add merge_type to merge_from_branch |
102 |
|
103 |
def test_merge_type(self): |
|
104 |
this = self.make_branch_and_tree('this') |
|
105 |
self.build_tree_contents([('this/foo', 'foo')]) |
|
106 |
this.add('foo', 'foo-id') |
|
107 |
this.commit('added foo') |
|
108 |
other = this.bzrdir.sprout('other').open_workingtree() |
|
109 |
self.build_tree_contents([('other/foo', 'bar')]) |
|
110 |
other.commit('content -> bar') |
|
111 |
self.build_tree_contents([('this/foo', 'baz')]) |
|
112 |
this.commit('content -> baz') |
|
113 |
class QuxMerge(merge.Merge3Merger): |
|
114 |
def text_merge(self, file_id, trans_id): |
|
115 |
self.tt.create_file('qux', trans_id) |
|
116 |
this.merge_from_branch(other.branch, merge_type=QuxMerge) |
|
117 |
self.assertEqual('qux', this.get_file_text('foo-id')) |
|
4634.149.2
by Vincent Ladeuil
Reproduce bug #373898. |
118 |
|
119 |
||
120 |
class TestMergedBranch(per_workingtree.TestCaseWithWorkingTree): |
|
121 |
||
122 |
def make_inner_branch(self): |
|
123 |
bld_inner = self.make_branch_builder('inner') |
|
124 |
bld_inner.start_series() |
|
125 |
bld_inner.build_snapshot( |
|
126 |
'1', None, |
|
127 |
[('add', ('', 'inner-root-id', 'directory', '')), |
|
128 |
('add', ('dir', 'dir-id', 'directory', '')), |
|
129 |
('add', ('dir/file1', 'file1-id', 'file', 'file1 content\n')), |
|
130 |
('add', ('file3', 'file3-id', 'file', 'file3 content\n')), |
|
131 |
])
|
|
132 |
bld_inner.build_snapshot( |
|
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
133 |
'4', ['1'], |
134 |
[('add', ('file4', 'file4-id', 'file', 'file4 content\n')) |
|
135 |
])
|
|
136 |
bld_inner.build_snapshot( |
|
137 |
'5', ['4'], [('rename', ('file4', 'dir/file4'))]) |
|
138 |
bld_inner.build_snapshot( |
|
4634.149.2
by Vincent Ladeuil
Reproduce bug #373898. |
139 |
'3', ['1'], [('modify', ('file3-id', 'new file3 contents\n')),]) |
140 |
bld_inner.build_snapshot( |
|
141 |
'2', ['1'], |
|
142 |
[('add', ('dir/file2', 'file2-id', 'file', 'file2 content\n')), |
|
143 |
])
|
|
144 |
bld_inner.finish_series() |
|
145 |
br = bld_inner.get_branch() |
|
146 |
return br |
|
147 |
||
148 |
def assertTreeLayout(self, expected, tree): |
|
6072.2.5
by Jelmer Vernooij
Don't use for test_merge_from_branch. |
149 |
tree.lock_read() |
150 |
try: |
|
151 |
actual = [e[0] for e in tree.list_files()] |
|
152 |
# list_files doesn't guarantee order
|
|
153 |
actual = sorted(actual) |
|
154 |
self.assertEqual(expected, actual) |
|
155 |
finally: |
|
156 |
tree.unlock() |
|
4634.149.2
by Vincent Ladeuil
Reproduce bug #373898. |
157 |
|
158 |
def make_outer_tree(self): |
|
159 |
outer = self.make_branch_and_tree('outer') |
|
160 |
self.build_tree_contents([('outer/foo', 'foo')]) |
|
161 |
outer.add('foo', 'foo-id') |
|
162 |
outer.commit('added foo') |
|
163 |
inner = self.make_inner_branch() |
|
164 |
outer.merge_from_branch(inner, to_revision='1', from_revision='null:') |
|
5954.4.9
by Aaron Bentley
Fix tests that assumed root was retained in a new-root conflict. |
165 |
#retain original root id.
|
166 |
outer.set_root_id(outer.basis_tree().get_root_id()) |
|
4634.149.2
by Vincent Ladeuil
Reproduce bug #373898. |
167 |
outer.commit('merge inner branch') |
168 |
outer.mkdir('dir-outer', 'dir-outer-id') |
|
169 |
outer.move(['dir', 'file3'], to_dir='dir-outer') |
|
170 |
outer.commit('rename imported dir and file3 to dir-outer') |
|
171 |
return outer, inner |
|
172 |
||
173 |
def test_file1_deleted_in_dir(self): |
|
174 |
outer, inner = self.make_outer_tree() |
|
175 |
outer.remove(['dir-outer/dir/file1'], keep_files=False) |
|
176 |
outer.commit('delete file1') |
|
177 |
outer.merge_from_branch(inner) |
|
178 |
outer.commit('merge the rest') |
|
179 |
self.assertTreeLayout(['dir-outer', |
|
180 |
'dir-outer/dir', |
|
181 |
'dir-outer/dir/file2', |
|
182 |
'dir-outer/file3', |
|
183 |
'foo'], |
|
184 |
outer) |
|
185 |
||
186 |
def test_file3_deleted_in_root(self): |
|
187 |
# Reproduce bug #375898
|
|
188 |
outer, inner = self.make_outer_tree() |
|
189 |
outer.remove(['dir-outer/file3'], keep_files=False) |
|
190 |
outer.commit('delete file3') |
|
191 |
outer.merge_from_branch(inner) |
|
192 |
outer.commit('merge the rest') |
|
193 |
self.assertTreeLayout(['dir-outer', |
|
194 |
'dir-outer/dir', |
|
195 |
'dir-outer/dir/file1', |
|
196 |
'dir-outer/dir/file2', |
|
197 |
'foo'], |
|
198 |
outer) |
|
199 |
||
200 |
||
201 |
def test_file3_in_root_conflicted(self): |
|
202 |
outer, inner = self.make_outer_tree() |
|
203 |
outer.remove(['dir-outer/file3'], keep_files=False) |
|
204 |
outer.commit('delete file3') |
|
205 |
nb_conflicts = outer.merge_from_branch(inner, to_revision='3') |
|
5954.4.9
by Aaron Bentley
Fix tests that assumed root was retained in a new-root conflict. |
206 |
self.assertEqual(4, nb_conflicts) |
4634.149.2
by Vincent Ladeuil
Reproduce bug #373898. |
207 |
self.assertTreeLayout(['dir-outer', |
208 |
'dir-outer/dir', |
|
209 |
'dir-outer/dir/file1', |
|
210 |
# Ideally th conflict helpers should be in
|
|
211 |
# dir-outer/dir but since we can't easily find
|
|
212 |
# back the file3 -> outer-dir/dir rename, root
|
|
213 |
# is good enough -- vila 20100401
|
|
214 |
'file3.BASE', |
|
215 |
'file3.OTHER', |
|
216 |
'foo'], |
|
217 |
outer) |
|
218 |
||
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
219 |
def test_file4_added_in_root(self): |
220 |
outer, inner = self.make_outer_tree() |
|
221 |
nb_conflicts = outer.merge_from_branch(inner, to_revision='4') |
|
5954.4.9
by Aaron Bentley
Fix tests that assumed root was retained in a new-root conflict. |
222 |
# file4 could not be added to its original root, so it gets added to
|
223 |
# the new root with a conflict.
|
|
224 |
self.assertEqual(1, nb_conflicts) |
|
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
225 |
self.assertTreeLayout(['dir-outer', |
226 |
'dir-outer/dir', |
|
227 |
'dir-outer/dir/file1', |
|
228 |
'dir-outer/file3', |
|
229 |
'file4', |
|
230 |
'foo'], |
|
231 |
outer) |
|
232 |
||
233 |
def test_file4_added_then_renamed(self): |
|
234 |
outer, inner = self.make_outer_tree() |
|
5954.4.9
by Aaron Bentley
Fix tests that assumed root was retained in a new-root conflict. |
235 |
# 1 conflict, because file4 can't be put into the old root
|
236 |
self.assertEqual(1, outer.merge_from_branch(inner, to_revision='4')) |
|
237 |
try: |
|
238 |
outer.set_conflicts(conflicts.ConflictList()) |
|
239 |
except errors.UnsupportedOperation: |
|
240 |
# WT2 doesn't have a separate list of conflicts to clear. It
|
|
241 |
# actually says there is a conflict, but happily forgets all about
|
|
242 |
# it.
|
|
243 |
pass
|
|
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
244 |
outer.commit('added file4') |
245 |
# And now file4 gets renamed into an existing dir
|
|
246 |
nb_conflicts = outer.merge_from_branch(inner, to_revision='5') |
|
5954.4.9
by Aaron Bentley
Fix tests that assumed root was retained in a new-root conflict. |
247 |
self.assertEqual(1, nb_conflicts) |
5783.2.1
by John Arbash Meinel
Remove the 'include_unchanged=True' from iter_changes. |
248 |
self.assertTreeLayout(['dir-outer', |
249 |
'dir-outer/dir', |
|
250 |
'dir-outer/dir/file1', |
|
251 |
'dir-outer/dir/file4', |
|
252 |
'dir-outer/file3', |
|
253 |
'foo'], |
|
254 |
outer) |