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.pending_merges(), [b.last_revision()])
89
self.check_file_contents('goodbye', 'quux')
90
self.runbzr('revert --no-backup')
91
self.runbzr('merge -r revno:%d:../b'%b.revno())
92
self.assertEquals(a.pending_merges(),
94
a_tip = a.commit('merged')
95
self.runbzr('merge ../b -r last:1')
96
self.assertEqual([a_tip], a.get_parent_ids())
98
def test_merge_with_missing_file(self):
99
"""Merge handles missing file conflicts"""
103
print >> file('sub/a.txt', 'wb'), "hello"
104
print >> file('b.txt', 'wb'), "hello"
105
print >> file('sub/c.txt', 'wb'), "hello"
108
self.runbzr(('commit', '-m', 'added a'))
109
self.runbzr('branch . ../b')
110
print >> file('sub/a.txt', 'ab'), "there"
111
print >> file('b.txt', 'ab'), "there"
112
print >> file('sub/c.txt', 'ab'), "there"
113
self.runbzr(('commit', '-m', 'Added there'))
114
os.unlink('sub/a.txt')
115
os.unlink('sub/c.txt')
118
self.runbzr(('commit', '-m', 'Removed a.txt'))
120
print >> file('sub/a.txt', 'ab'), "something"
121
print >> file('b.txt', 'ab'), "something"
122
print >> file('sub/c.txt', 'ab'), "something"
123
self.runbzr(('commit', '-m', 'Modified a.txt'))
124
self.runbzr('merge ../a/', retcode=1)
125
self.assert_(os.path.exists('sub/a.txt.THIS'))
126
self.assert_(os.path.exists('sub/a.txt.BASE'))
128
self.runbzr('merge ../b/', retcode=1)
129
self.assert_(os.path.exists('sub/a.txt.OTHER'))
130
self.assert_(os.path.exists('sub/a.txt.BASE'))
132
def test_merge_remember(self):
133
"""Merge changes from one branch to another and test parent location."""
134
tree_a = self.make_branch_and_tree('branch_a')
135
branch_a = tree_a.branch
136
self.build_tree(['branch_a/a'])
138
tree_a.commit('commit a')
139
branch_b = branch_a.bzrdir.sprout('branch_b').open_branch()
140
tree_b = branch_b.bzrdir.open_workingtree()
141
branch_c = branch_a.bzrdir.sprout('branch_c').open_branch()
142
tree_c = branch_c.bzrdir.open_workingtree()
143
self.build_tree(['branch_a/b'])
145
tree_a.commit('commit b')
146
self.build_tree(['branch_c/c'])
148
tree_c.commit('commit c')
150
parent = branch_b.get_parent()
151
branch_b.set_parent(None)
152
self.assertEqual(None, branch_b.get_parent())
153
# test merge for failure without parent set
155
out = self.runbzr('merge', retcode=3)
156
self.assertEquals(out,
157
('','bzr: ERROR: No location specified or remembered\n'))
158
# test implicit --remember when no parent set, this merge conflicts
159
self.build_tree(['d'])
161
out = self.runbzr('merge ../branch_a', retcode=3)
162
self.assertEquals(out,
163
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
164
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
165
# test implicit --remember after resolving conflict
166
tree_b.commit('commit d')
167
out, err = self.runbzr('merge')
169
base = urlutils.local_path_from_url(branch_a.base)
170
self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
171
self.assertEquals(err, 'All changes applied successfully.\n')
172
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
173
# re-open tree as external runbzr modified it
174
tree_b = branch_b.bzrdir.open_workingtree()
175
tree_b.commit('merge branch_a')
176
# test explicit --remember
177
out, err = self.runbzr('merge ../branch_c --remember')
178
self.assertEquals(out, '')
179
self.assertEquals(err, 'All changes applied successfully.\n')
180
self.assertEquals(abspath(branch_b.get_parent()),
181
abspath(branch_c.bzrdir.root_transport.base))
182
# re-open tree as external runbzr modified it
183
tree_b = branch_b.bzrdir.open_workingtree()
184
tree_b.commit('merge branch_c')
186
def test_merge_bundle(self):
187
from bzrlib.testament import Testament
188
tree_a = self.make_branch_and_tree('branch_a')
189
f = file('branch_a/a', 'wb')
193
tree_a.commit('message')
195
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
196
f = file('branch_a/a', 'wb')
199
tree_a.commit('message')
201
f = file('branch_b/a', 'wb')
204
tree_b.commit('message')
206
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
207
os.chdir('../branch_a')
208
self.runbzr('merge ../bundle', retcode=1)
209
testament_a = Testament.from_revision(tree_a.branch.repository,
210
tree_b.get_parent_ids()[0])
211
testament_b = Testament.from_revision(tree_b.branch.repository,
212
tree_b.get_parent_ids()[0])
213
self.assertEqualDiff(testament_a.as_text(),
214
testament_b.as_text())
215
tree_a.set_conflicts(ConflictList())
216
tree_a.commit('message')
217
# it is legal to attempt to merge an already-merged bundle
218
output = self.runbzr('merge ../bundle')[1]
219
# but it does nothing
220
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
221
self.assertEqual('Nothing to do.\n', output)
223
def test_merge_uncommitted(self):
224
"""Check that merge --uncommitted behaves properly"""
225
tree_a = self.make_branch_and_tree('a')
226
self.build_tree(['a/file_1', 'a/file_2'])
227
tree_a.add(['file_1', 'file_2'])
228
tree_a.commit('commit 1')
229
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
230
self.failUnlessExists('b/file_1')
231
tree_a.rename_one('file_1', 'file_i')
232
tree_a.commit('commit 2')
233
tree_a.rename_one('file_2', 'file_ii')
235
self.run_bzr('merge', '../a', '--uncommitted')
236
self.failUnlessExists('file_1')
237
self.failUnlessExists('file_ii')
239
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
240
'merge', '../a', '--uncommitted', '-r1')