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
56
self.runbzr('branch a b')
58
file('goodbye', 'wt').write('quux')
59
self.runbzr(['commit', '-m', "more u's are always good"])
62
file('hello', 'wt').write('quuux')
63
# We can't merge when there are in-tree changes
64
self.runbzr('merge ../b', retcode=3)
65
self.runbzr(['commit', '-m', "Like an epidemic of u's"])
66
self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
68
self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
69
self.runbzr('revert --no-backup')
70
self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
71
self.runbzr('revert --no-backup')
72
self.runbzr('merge ../b -r last:1..last:1 --reprocess')
73
self.runbzr('revert --no-backup')
74
self.runbzr('merge ../b -r last:1')
75
self.check_file_contents('goodbye', 'quux')
76
# Merging a branch pulls its revision into the tree
77
a = WorkingTree.open('.')
78
b = Branch.open('../b')
79
a.branch.repository.get_revision_xml(b.last_revision())
80
self.log('pending merges: %s', a.pending_merges())
81
self.assertEquals(a.pending_merges(),
83
self.runbzr('commit -m merged')
84
self.runbzr('merge ../b -r last:1')
85
self.assertEqual(a.pending_merges(), [])
87
def test_merge_with_missing_file(self):
88
"""Merge handles missing file conflicts"""
92
print >> file('sub/a.txt', 'wb'), "hello"
93
print >> file('b.txt', 'wb'), "hello"
94
print >> file('sub/c.txt', 'wb'), "hello"
97
self.runbzr(('commit', '-m', 'added a'))
98
self.runbzr('branch . ../b')
99
print >> file('sub/a.txt', 'ab'), "there"
100
print >> file('b.txt', 'ab'), "there"
101
print >> file('sub/c.txt', 'ab'), "there"
102
self.runbzr(('commit', '-m', 'Added there'))
103
os.unlink('sub/a.txt')
104
os.unlink('sub/c.txt')
107
self.runbzr(('commit', '-m', 'Removed a.txt'))
109
print >> file('sub/a.txt', 'ab'), "something"
110
print >> file('b.txt', 'ab'), "something"
111
print >> file('sub/c.txt', 'ab'), "something"
112
self.runbzr(('commit', '-m', 'Modified a.txt'))
113
self.runbzr('merge ../a/', retcode=1)
114
self.assert_(os.path.exists('sub/a.txt.THIS'))
115
self.assert_(os.path.exists('sub/a.txt.BASE'))
117
self.runbzr('merge ../b/', retcode=1)
118
self.assert_(os.path.exists('sub/a.txt.OTHER'))
119
self.assert_(os.path.exists('sub/a.txt.BASE'))
121
def test_merge_remember(self):
122
"""Merge changes from one branch to another and test parent location."""
123
tree_a = self.make_branch_and_tree('branch_a')
124
branch_a = tree_a.branch
125
self.build_tree(['branch_a/a'])
127
tree_a.commit('commit a')
128
branch_b = branch_a.bzrdir.sprout('branch_b').open_branch()
129
tree_b = branch_b.bzrdir.open_workingtree()
130
branch_c = branch_a.bzrdir.sprout('branch_c').open_branch()
131
tree_c = branch_c.bzrdir.open_workingtree()
132
self.build_tree(['branch_a/b'])
134
tree_a.commit('commit b')
135
self.build_tree(['branch_c/c'])
137
tree_c.commit('commit c')
139
parent = branch_b.get_parent()
140
branch_b.set_parent(None)
141
self.assertEqual(None, branch_b.get_parent())
142
# test merge for failure without parent set
144
out = self.runbzr('merge', retcode=3)
145
self.assertEquals(out,
146
('','bzr: ERROR: No location specified or remembered\n'))
147
# test implicit --remember when no parent set, this merge conflicts
148
self.build_tree(['d'])
150
out = self.runbzr('merge ../branch_a', retcode=3)
151
self.assertEquals(out,
152
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
153
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
154
# test implicit --remember after resolving conflict
155
tree_b.commit('commit d')
156
out, err = self.runbzr('merge')
158
base = urlutils.local_path_from_url(branch_a.base)
159
self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
160
self.assertEquals(err, 'All changes applied successfully.\n')
161
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
162
# re-open tree as external runbzr modified it
163
tree_b = branch_b.bzrdir.open_workingtree()
164
tree_b.commit('merge branch_a')
165
# test explicit --remember
166
out, err = self.runbzr('merge ../branch_c --remember')
167
self.assertEquals(out, '')
168
self.assertEquals(err, 'All changes applied successfully.\n')
169
self.assertEquals(abspath(branch_b.get_parent()),
170
abspath(branch_c.bzrdir.root_transport.base))
171
# re-open tree as external runbzr modified it
172
tree_b = branch_b.bzrdir.open_workingtree()
173
tree_b.commit('merge branch_c')
175
def test_merge_bundle(self):
176
from bzrlib.testament import Testament
177
tree_a = self.make_branch_and_tree('branch_a')
178
f = file('branch_a/a', 'wb')
182
tree_a.commit('message')
184
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
185
f = file('branch_a/a', 'wb')
188
tree_a.commit('message')
190
f = file('branch_b/a', 'wb')
193
tree_b.commit('message')
195
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
196
os.chdir('../branch_a')
197
self.runbzr('merge ../bundle', retcode=1)
198
testament_a = Testament.from_revision(tree_a.branch.repository,
199
tree_b.last_revision())
200
testament_b = Testament.from_revision(tree_b.branch.repository,
201
tree_b.last_revision())
202
self.assertEqualDiff(testament_a.as_text(),
203
testament_b.as_text())
204
tree_a.set_conflicts(ConflictList())
205
tree_a.commit('message')
206
# it is legal to attempt to merge an already-merged bundle
207
output = self.runbzr('merge ../bundle')[1]
208
# but it does nothing
209
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
210
self.assertEqual('Nothing to do.\n', output)
212
def test_merge_uncommitted(self):
213
"""Check that merge --uncommitted behaves properly"""
214
tree_a = self.make_branch_and_tree('a')
215
self.build_tree(['a/file_1', 'a/file_2'])
216
tree_a.add(['file_1', 'file_2'])
217
tree_a.commit('commit 1')
218
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
219
self.failUnlessExists('b/file_1')
220
tree_a.rename_one('file_1', 'file_i')
221
tree_a.commit('commit 2')
222
tree_a.rename_one('file_2', 'file_ii')
224
self.run_bzr('merge', '../a', '--uncommitted')
225
self.failUnlessExists('file_1')
226
self.failUnlessExists('file_ii')
228
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
229
'merge', '../a', '--uncommitted', '-r1')