~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_merge.py

Don't encode unicode messages to UTF-8 in mutter() (the stream writer does it).

Use a codec wrapped log file in tests to match the real environment.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
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
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
#
17
 
# Author: Aaron Bentley <aaron.bentley@utoronto.ca>
18
 
 
19
 
"""Black-box tests for bzr merge.
20
 
"""
21
 
 
22
 
import os
23
 
 
24
 
from bzrlib.branch import Branch
25
 
from bzrlib.bzrdir import BzrDir
26
 
from bzrlib.conflicts import ConflictList, ContentsConflict
27
 
from bzrlib.osutils import abspath, file_kind
28
 
from bzrlib.tests.blackbox import ExternalBase
29
 
import bzrlib.urlutils as urlutils
30
 
from bzrlib.workingtree import WorkingTree
31
 
 
32
 
 
33
 
class TestMerge(ExternalBase):
34
 
 
35
 
    def example_branch(test):
36
 
        test.runbzr('init')
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')
43
 
 
44
 
    def test_merge_reprocess(self):
45
 
        d = BzrDir.create_standalone_workingtree('.')
46
 
        d.commit('h')
47
 
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
48
 
 
49
 
    def test_merge(self):
50
 
        from bzrlib.branch import Branch
51
 
        
52
 
        os.mkdir('a')
53
 
        os.chdir('a')
54
 
        self.example_branch()
55
 
        ancestor = Branch.open('.').revno()
56
 
        os.chdir('..')
57
 
        self.runbzr('branch a b')
58
 
        os.chdir('b')
59
 
        file('goodbye', 'wt').write('quux')
60
 
        self.runbzr(['commit',  '-m',  "more u's are always good"])
61
 
 
62
 
        os.chdir('../a')
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',
69
 
                    retcode=3)
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())
98
 
 
99
 
    def test_merge_with_missing_file(self):
100
 
        """Merge handles missing file conflicts"""
101
 
        os.mkdir('a')
102
 
        os.chdir('a')
103
 
        os.mkdir('sub')
104
 
        print >> file('sub/a.txt', 'wb'), "hello"
105
 
        print >> file('b.txt', 'wb'), "hello"
106
 
        print >> file('sub/c.txt', 'wb'), "hello"
107
 
        self.runbzr('init')
108
 
        self.runbzr('add')
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')
117
 
        os.rmdir('sub')
118
 
        os.unlink('b.txt')
119
 
        self.runbzr(('commit', '-m', 'Removed a.txt'))
120
 
        os.chdir('../b')
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'))
128
 
        os.chdir('../a')
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'))
132
 
 
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'])
138
 
        tree_a.add('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'])
145
 
        tree_a.add('b')
146
 
        tree_a.commit('commit b')
147
 
        self.build_tree(['branch_c/c'])
148
 
        tree_c.add('c')
149
 
        tree_c.commit('commit c')
150
 
        # reset parent
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
155
 
        os.chdir('branch_b')
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'])
161
 
        tree_b.add('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')
169
 
        
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, '+N  b\nAll 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, '+N  c\nAll 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')
186
 
 
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')
191
 
        f.write('hello')
192
 
        f.close()
193
 
        tree_a.add('a')
194
 
        tree_a.commit('message')
195
 
 
196
 
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
197
 
        f = file('branch_a/a', 'wb')
198
 
        f.write('hey there')
199
 
        f.close()
200
 
        tree_a.commit('message')
201
 
 
202
 
        f = file('branch_b/a', 'wb')
203
 
        f.write('goodbye')
204
 
        f.close()
205
 
        tree_b.commit('message')
206
 
        os.chdir('branch_b')
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)
223
 
 
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')
235
 
        ## os.chdir('b')
236
 
        self.run_bzr('merge', 'a', '--uncommitted', '-d', 'b')
237
 
        self.failUnlessExists('b/file_1')
238
 
        self.failUnlessExists('b/file_ii')
239
 
        tree_b.revert([])
240
 
        self.run_bzr_error(('Cannot use --uncommitted and --revision',),
241
 
                           'merge', '/a', '--uncommitted', '-r1',
242
 
                           '-d', 'b')
243
 
 
244
 
    def pullable_branch(self):
245
 
        os.mkdir('a')
246
 
        os.chdir('a')
247
 
        self.example_branch()
248
 
        os.chdir('..')
249
 
        self.runbzr('branch a b')
250
 
        os.chdir('b')
251
 
        file('goodbye', 'wt').write('quux')
252
 
        self.runbzr(['commit', '-m', "mode u's are always good"])
253
 
        os.chdir('../a')
254
 
 
255
 
    def pullable_branch(self):
256
 
        tree_a = self.make_branch_and_tree('a')
257
 
        self.build_tree(['a/file'])
258
 
        tree_a.add(['file'])
259
 
        self.id1 = tree_a.commit('commit 1')
260
 
        
261
 
        tree_b = self.make_branch_and_tree('b')
262
 
        tree_b.pull(tree_a.branch)
263
 
        file('b/file', 'wb').write('foo')
264
 
        self.id2 = tree_b.commit('commit 2')
265
 
 
266
 
    def test_merge_pull(self):
267
 
        self.pullable_branch()
268
 
        os.chdir('a')
269
 
        (out, err) = self.run_bzr('merge', '--pull', '../b')
270
 
        self.assertContainsRe(err, 'Now on revision 2\\.')
271
 
        tree_a = WorkingTree.open('.')
272
 
        self.assertEqual([self.id2], tree_a.get_parent_ids())
273
 
 
274
 
    def test_merge_kind_change(self):
275
 
        tree_a = self.make_branch_and_tree('tree_a')
276
 
        self.build_tree_contents([('tree_a/file', 'content_1')])
277
 
        tree_a.add('file', 'file-id')
278
 
        tree_a.commit('added file')
279
 
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
280
 
        os.unlink('tree_a/file')
281
 
        self.build_tree(['tree_a/file/'])
282
 
        tree_a.commit('changed file to directory')
283
 
        os.chdir('tree_b')
284
 
        self.run_bzr('merge', '../tree_a')
285
 
        self.assertEqual('directory', file_kind('file'))
286
 
        tree_b.revert([])
287
 
        self.assertEqual('file', file_kind('file'))
288
 
        self.build_tree_contents([('file', 'content_2')])
289
 
        tree_b.commit('content change')
290
 
        self.run_bzr('merge', '../tree_a', retcode=1)
291
 
        self.assertEqual(tree_b.conflicts(),
292
 
                         [ContentsConflict('file', file_id='file-id')])