1
# Copyright (C) 2006 Canonical Ltd
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.
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.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# Author: Aaron Bentley <aaron.bentley@utoronto.ca>
19
"""Black-box tests for bzr merge.
24
from bzrlib.branch import Branch
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.conflicts import ConflictList
27
from bzrlib.osutils import abspath
28
from bzrlib.tests.blackbox import ExternalBase
29
import bzrlib.urlutils as urlutils
30
from bzrlib.workingtree import WorkingTree
33
class TestMerge(ExternalBase):
35
def example_branch(test):
37
file('hello', 'wt').write('foo')
38
test.runbzr('add hello')
39
test.runbzr('commit -m setup hello')
40
file('goodbye', 'wt').write('baz')
41
test.runbzr('add goodbye')
42
test.runbzr('commit -m setup goodbye')
44
def test_merge_reprocess(self):
45
d = BzrDir.create_standalone_workingtree('.')
47
self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
50
from bzrlib.branch import Branch
55
ancestor = Branch.open('.').revno()
57
self.runbzr('branch a b')
59
file('goodbye', 'wt').write('quux')
60
self.runbzr(['commit', '-m', "more u's are always good"])
63
file('hello', 'wt').write('quuux')
64
# We can't merge when there are in-tree changes
65
self.runbzr('merge ../b', retcode=3)
66
a = WorkingTree.open('.')
67
a_tip = a.commit("Like an epidemic of u's")
68
self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
70
self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
71
self.runbzr('revert --no-backup')
72
self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
73
self.runbzr('revert --no-backup')
74
self.runbzr('merge ../b -r last:1..last:1 --reprocess')
75
self.runbzr('revert --no-backup')
76
self.runbzr('merge ../b -r last:1')
77
self.check_file_contents('goodbye', 'quux')
78
# Merging a branch pulls its revision into the tree
79
b = Branch.open('../b')
80
b_tip = b.last_revision()
81
self.failUnless(a.branch.repository.has_revision(b_tip))
82
self.assertEqual([a_tip, b_tip], a.get_parent_ids())
83
self.runbzr('revert --no-backup')
84
out, err = self.runbzr('merge -r revno:1:./hello', retcode=3)
85
self.assertTrue("Not a branch" in err)
86
self.runbzr('merge -r revno:%d:./..revno:%d:../b'
87
%(ancestor,b.revno()))
88
self.assertEquals(a.get_parent_ids(),
89
[a.branch.last_revision(), b.last_revision()])
90
self.check_file_contents('goodbye', 'quux')
91
self.runbzr('revert --no-backup')
92
self.runbzr('merge -r revno:%d:../b'%b.revno())
93
self.assertEquals(a.get_parent_ids(),
94
[a.branch.last_revision(), b.last_revision()])
95
a_tip = a.commit('merged')
96
self.runbzr('merge ../b -r last:1')
97
self.assertEqual([a_tip], a.get_parent_ids())
99
def test_merge_with_missing_file(self):
100
"""Merge handles missing file conflicts"""
104
print >> file('sub/a.txt', 'wb'), "hello"
105
print >> file('b.txt', 'wb'), "hello"
106
print >> file('sub/c.txt', 'wb'), "hello"
109
self.runbzr(('commit', '-m', 'added a'))
110
self.runbzr('branch . ../b')
111
print >> file('sub/a.txt', 'ab'), "there"
112
print >> file('b.txt', 'ab'), "there"
113
print >> file('sub/c.txt', 'ab'), "there"
114
self.runbzr(('commit', '-m', 'Added there'))
115
os.unlink('sub/a.txt')
116
os.unlink('sub/c.txt')
119
self.runbzr(('commit', '-m', 'Removed a.txt'))
121
print >> file('sub/a.txt', 'ab'), "something"
122
print >> file('b.txt', 'ab'), "something"
123
print >> file('sub/c.txt', 'ab'), "something"
124
self.runbzr(('commit', '-m', 'Modified a.txt'))
125
self.runbzr('merge ../a/', retcode=1)
126
self.assert_(os.path.exists('sub/a.txt.THIS'))
127
self.assert_(os.path.exists('sub/a.txt.BASE'))
129
self.runbzr('merge ../b/', retcode=1)
130
self.assert_(os.path.exists('sub/a.txt.OTHER'))
131
self.assert_(os.path.exists('sub/a.txt.BASE'))
133
def test_merge_remember(self):
134
"""Merge changes from one branch to another and test parent location."""
135
tree_a = self.make_branch_and_tree('branch_a')
136
branch_a = tree_a.branch
137
self.build_tree(['branch_a/a'])
139
tree_a.commit('commit a')
140
branch_b = branch_a.bzrdir.sprout('branch_b').open_branch()
141
tree_b = branch_b.bzrdir.open_workingtree()
142
branch_c = branch_a.bzrdir.sprout('branch_c').open_branch()
143
tree_c = branch_c.bzrdir.open_workingtree()
144
self.build_tree(['branch_a/b'])
146
tree_a.commit('commit b')
147
self.build_tree(['branch_c/c'])
149
tree_c.commit('commit c')
151
parent = branch_b.get_parent()
152
branch_b.set_parent(None)
153
self.assertEqual(None, branch_b.get_parent())
154
# test merge for failure without parent set
156
out = self.runbzr('merge', retcode=3)
157
self.assertEquals(out,
158
('','bzr: ERROR: No location specified or remembered\n'))
159
# test implicit --remember when no parent set, this merge conflicts
160
self.build_tree(['d'])
162
out = self.runbzr('merge ../branch_a', retcode=3)
163
self.assertEquals(out,
164
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
165
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
166
# test implicit --remember after resolving conflict
167
tree_b.commit('commit d')
168
out, err = self.runbzr('merge')
170
base = urlutils.local_path_from_url(branch_a.base)
171
self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
172
self.assertEquals(err, 'All changes applied successfully.\n')
173
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
174
# re-open tree as external runbzr modified it
175
tree_b = branch_b.bzrdir.open_workingtree()
176
tree_b.commit('merge branch_a')
177
# test explicit --remember
178
out, err = self.runbzr('merge ../branch_c --remember')
179
self.assertEquals(out, '')
180
self.assertEquals(err, 'All changes applied successfully.\n')
181
self.assertEquals(abspath(branch_b.get_parent()),
182
abspath(branch_c.bzrdir.root_transport.base))
183
# re-open tree as external runbzr modified it
184
tree_b = branch_b.bzrdir.open_workingtree()
185
tree_b.commit('merge branch_c')
187
def test_merge_bundle(self):
188
from bzrlib.testament import Testament
189
tree_a = self.make_branch_and_tree('branch_a')
190
f = file('branch_a/a', 'wb')
194
tree_a.commit('message')
196
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
197
f = file('branch_a/a', 'wb')
200
tree_a.commit('message')
202
f = file('branch_b/a', 'wb')
205
tree_b.commit('message')
207
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
208
os.chdir('../branch_a')
209
self.runbzr('merge ../bundle', retcode=1)
210
testament_a = Testament.from_revision(tree_a.branch.repository,
211
tree_b.get_parent_ids()[0])
212
testament_b = Testament.from_revision(tree_b.branch.repository,
213
tree_b.get_parent_ids()[0])
214
self.assertEqualDiff(testament_a.as_text(),
215
testament_b.as_text())
216
tree_a.set_conflicts(ConflictList())
217
tree_a.commit('message')
218
# it is legal to attempt to merge an already-merged bundle
219
output = self.runbzr('merge ../bundle')[1]
220
# but it does nothing
221
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
222
self.assertEqual('Nothing to do.\n', output)
224
def test_merge_uncommitted(self):
225
"""Check that merge --uncommitted behaves properly"""
226
tree_a = self.make_branch_and_tree('a')
227
self.build_tree(['a/file_1', 'a/file_2'])
228
tree_a.add(['file_1', 'file_2'])
229
tree_a.commit('commit 1')
230
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
231
self.failUnlessExists('b/file_1')
232
tree_a.rename_one('file_1', 'file_i')
233
tree_a.commit('commit 2')
234
tree_a.rename_one('file_2', 'file_ii')
236
self.run_bzr('merge', '../a', '--uncommitted')
237
self.failUnlessExists('file_1')
238
self.failUnlessExists('file_ii')
240
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
241
'merge', '../a', '--uncommitted', '-r1')
243
def pullable_branch(self):
246
self.example_branch()
248
self.runbzr('branch a b')
250
file('goodbye', 'wt').write('quux')
251
self.runbzr(['commit', '-m', "mode u's are always good"])
254
def pullable_branch(self):
255
tree_a = self.make_branch_and_tree('a')
256
self.build_tree(['a/file'])
258
self.id1 = tree_a.commit('commit 1')
260
tree_b = self.make_branch_and_tree('b')
261
tree_b.pull(tree_a.branch)
262
file('b/file', 'wb').write('foo')
263
self.id2 = tree_b.commit('commit 2')
265
def test_merge_pull(self):
266
self.pullable_branch()
268
(out, err) = self.run_bzr('merge', '--pull', '../b')
269
self.assertContainsRe(err, '1 revision\\(s\\) pulled')
270
tree_a = WorkingTree.open('.')
271
self.assertEqual([self.id2], tree_a.get_parent_ids())