/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2007-10-03 08:06:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2901.
  • Revision ID: mbp@sourcefrog.net-20071003080644-oivy0gkg98sex0ed
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).

Add new LockFailed, which doesn't imply that we failed to get it because of
contention.  Raise this if we fail to create the pending or lock directories
because of Transport errors.

UnlockableTransport is not an internal error.

ReadOnlyLockError has a message which didn't match its name or usage; it's now
deprecated and callers are updated to use LockFailed which is more appropriate.

Add zero_ninetytwo deprecation symbol.

Unify assertMatchesRe with TestCase.assertContainsRe.

When the constructor is deprecated, just say that the class is deprecated, not
the __init__ method - this works better with applyDeprecated in tests.

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 import merge_directive
 
25
from bzrlib.branch import Branch
 
26
from bzrlib.bzrdir import BzrDir
 
27
from bzrlib.conflicts import ConflictList, ContentsConflict
 
28
from bzrlib.osutils import abspath, file_kind, pathjoin
 
29
from bzrlib.tests.blackbox import ExternalBase
 
30
import bzrlib.urlutils as urlutils
 
31
from bzrlib.workingtree import WorkingTree
 
32
 
 
33
 
 
34
class TestMerge(ExternalBase):
 
35
 
 
36
    def example_branch(self, path='.'):
 
37
        tree = self.make_branch_and_tree(path)
 
38
        self.build_tree_contents([
 
39
            (pathjoin(path, 'hello'), 'foo'),
 
40
            (pathjoin(path, 'goodbye'), 'baz')])
 
41
        tree.add('hello')
 
42
        tree.commit(message='setup')
 
43
        tree.add('goodbye')
 
44
        tree.commit(message='setup')
 
45
        return tree
 
46
 
 
47
    def test_merge_reprocess(self):
 
48
        d = BzrDir.create_standalone_workingtree('.')
 
49
        d.commit('h')
 
50
        self.run_bzr('merge . --reprocess --merge-type weave')
 
51
 
 
52
    def test_merge(self):
 
53
        from bzrlib.branch import Branch
 
54
 
 
55
        a_tree = self.example_branch('a')
 
56
        ancestor = a_tree.branch.revno()
 
57
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
 
58
        self.build_tree_contents([('b/goodbye', 'quux')])
 
59
        b_tree.commit(message="more u's are always good")
 
60
 
 
61
        self.build_tree_contents([('a/hello', 'quuux')])
 
62
        # We can't merge when there are in-tree changes
 
63
        os.chdir('a')
 
64
        self.run_bzr('merge ../b', retcode=3)
 
65
        a = WorkingTree.open('.')
 
66
        a_tip = a.commit("Like an epidemic of u's")
 
67
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
 
68
                    retcode=3)
 
69
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
 
70
        a_tree.revert(backups=False)
 
71
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
 
72
        a_tree.revert(backups=False)
 
73
        self.run_bzr_error(['Show-base is not supported for this merge type'],
 
74
                           'merge ../b -r last:1..last:1 --merge-type weave'
 
75
                           ' --show-base')
 
76
        a_tree.revert(backups=False)
 
77
        self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
 
78
        a_tree.revert(backups=False)
 
79
        self.run_bzr('merge ../b -r last:1')
 
80
        self.check_file_contents('goodbye', 'quux')
 
81
        # Merging a branch pulls its revision into the tree
 
82
        b = Branch.open('../b')
 
83
        b_tip = b.last_revision()
 
84
        self.failUnless(a.branch.repository.has_revision(b_tip))
 
85
        self.assertEqual([a_tip, b_tip], a.get_parent_ids())
 
86
        a_tree.revert(backups=False)
 
87
        out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
 
88
        self.assertTrue("Not a branch" in err)
 
89
        self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
 
90
                    %(ancestor,b.revno()))
 
91
        self.assertEquals(a.get_parent_ids(), 
 
92
                          [a.branch.last_revision(), b.last_revision()])
 
93
        self.check_file_contents('goodbye', 'quux')
 
94
        a_tree.revert(backups=False)
 
95
        self.run_bzr('merge -r revno:%d:../b'%b.revno())
 
96
        self.assertEquals(a.get_parent_ids(),
 
97
                          [a.branch.last_revision(), b.last_revision()])
 
98
        a_tip = a.commit('merged')
 
99
        self.run_bzr('merge ../b -r last:1')
 
100
        self.assertEqual([a_tip], a.get_parent_ids())
 
101
 
 
102
    def test_merge_with_missing_file(self):
 
103
        """Merge handles missing file conflicts"""
 
104
        self.build_tree_contents([
 
105
            ('a/',),
 
106
            ('a/sub/',),
 
107
            ('a/sub/a.txt', 'hello\n'),
 
108
            ('a/b.txt', 'hello\n'),
 
109
            ('a/sub/c.txt', 'hello\n')])
 
110
        a_tree = self.make_branch_and_tree('a')
 
111
        a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
 
112
        a_tree.commit(message='added a')
 
113
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
 
114
        self.build_tree_contents([
 
115
            ('a/sub/a.txt', 'hello\nthere\n'),
 
116
            ('a/b.txt', 'hello\nthere\n'),
 
117
            ('a/sub/c.txt', 'hello\nthere\n')])
 
118
        a_tree.commit(message='Added there')
 
119
        os.remove('a/sub/a.txt')
 
120
        os.remove('a/sub/c.txt')
 
121
        os.rmdir('a/sub')
 
122
        os.remove('a/b.txt')
 
123
        a_tree.commit(message='Removed a.txt')
 
124
        self.build_tree_contents([
 
125
            ('b/sub/a.txt', 'hello\nsomething\n'),
 
126
            ('b/b.txt', 'hello\nsomething\n'),
 
127
            ('b/sub/c.txt', 'hello\nsomething\n')])
 
128
        b_tree.commit(message='Modified a.txt')
 
129
        os.chdir('b')
 
130
        self.run_bzr('merge ../a/', retcode=1)
 
131
        self.failUnlessExists('sub/a.txt.THIS')
 
132
        self.failUnlessExists('sub/a.txt.BASE')
 
133
        os.chdir('../a')
 
134
        self.run_bzr('merge ../b/', retcode=1)
 
135
        self.failUnlessExists('sub/a.txt.OTHER')
 
136
        self.failUnlessExists('sub/a.txt.BASE')
 
137
 
 
138
    def test_merge_remember(self):
 
139
        """Merge changes from one branch to another and test parent location."""
 
140
        tree_a = self.make_branch_and_tree('branch_a')
 
141
        branch_a = tree_a.branch
 
142
        self.build_tree(['branch_a/a'])
 
143
        tree_a.add('a')
 
144
        tree_a.commit('commit a')
 
145
        branch_b = branch_a.bzrdir.sprout('branch_b').open_branch()
 
146
        tree_b = branch_b.bzrdir.open_workingtree()
 
147
        branch_c = branch_a.bzrdir.sprout('branch_c').open_branch()
 
148
        tree_c = branch_c.bzrdir.open_workingtree()
 
149
        self.build_tree(['branch_a/b'])
 
150
        tree_a.add('b')
 
151
        tree_a.commit('commit b')
 
152
        self.build_tree(['branch_c/c'])
 
153
        tree_c.add('c')
 
154
        tree_c.commit('commit c')
 
155
        # reset parent
 
156
        parent = branch_b.get_parent()
 
157
        branch_b.set_parent(None)
 
158
        self.assertEqual(None, branch_b.get_parent())
 
159
        # test merge for failure without parent set
 
160
        os.chdir('branch_b')
 
161
        out = self.run_bzr('merge', retcode=3)
 
162
        self.assertEquals(out,
 
163
                ('','bzr: ERROR: No location specified or remembered\n'))
 
164
        # test implicit --remember when no parent set, this merge conflicts
 
165
        self.build_tree(['d'])
 
166
        tree_b.add('d')
 
167
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
 
168
                           'merge ../branch_a')
 
169
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
 
170
        # test implicit --remember after resolving conflict
 
171
        tree_b.commit('commit d')
 
172
        out, err = self.run_bzr('merge')
 
173
        
 
174
        base = urlutils.local_path_from_url(branch_a.base)
 
175
        self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
 
176
        self.assertEquals(err, '+N  b\nAll changes applied successfully.\n')
 
177
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
 
178
        # re-open tree as external run_bzr modified it
 
179
        tree_b = branch_b.bzrdir.open_workingtree()
 
180
        tree_b.commit('merge branch_a')
 
181
        # test explicit --remember
 
182
        out, err = self.run_bzr('merge ../branch_c --remember')
 
183
        self.assertEquals(out, '')
 
184
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
 
185
        self.assertEquals(abspath(branch_b.get_parent()),
 
186
                          abspath(branch_c.bzrdir.root_transport.base))
 
187
        # re-open tree as external run_bzr modified it
 
188
        tree_b = branch_b.bzrdir.open_workingtree()
 
189
        tree_b.commit('merge branch_c')
 
190
 
 
191
    def test_merge_bundle(self):
 
192
        from bzrlib.testament import Testament
 
193
        tree_a = self.make_branch_and_tree('branch_a')
 
194
        self.build_tree_contents([('branch_a/a', 'hello')])
 
195
        tree_a.add('a')
 
196
        tree_a.commit('message')
 
197
 
 
198
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
 
199
        self.build_tree_contents([('branch_a/a', 'hey there')])
 
200
        tree_a.commit('message')
 
201
 
 
202
        self.build_tree_contents([('branch_b/a', 'goodbye')])
 
203
        tree_b.commit('message')
 
204
        os.chdir('branch_b')
 
205
        self.run_bzr('bundle ../branch_a -o ../bundle')
 
206
        os.chdir('../branch_a')
 
207
        self.run_bzr('merge ../bundle', retcode=1)
 
208
        testament_a = Testament.from_revision(tree_a.branch.repository,
 
209
                                              tree_b.get_parent_ids()[0])
 
210
        testament_b = Testament.from_revision(tree_b.branch.repository,
 
211
                                              tree_b.get_parent_ids()[0])
 
212
        self.assertEqualDiff(testament_a.as_text(),
 
213
                         testament_b.as_text())
 
214
        tree_a.set_conflicts(ConflictList())
 
215
        tree_a.commit('message')
 
216
        # it is legal to attempt to merge an already-merged bundle
 
217
        output = self.run_bzr('merge ../bundle')[1]
 
218
        # but it does nothing
 
219
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
 
220
        self.assertEqual('Nothing to do.\n', output)
 
221
 
 
222
    def test_merge_uncommitted(self):
 
223
        """Check that merge --uncommitted behaves properly"""
 
224
        tree_a = self.make_branch_and_tree('a')
 
225
        self.build_tree(['a/file_1', 'a/file_2'])
 
226
        tree_a.add(['file_1', 'file_2'])
 
227
        tree_a.commit('commit 1')
 
228
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
229
        self.failUnlessExists('b/file_1')
 
230
        tree_a.rename_one('file_1', 'file_i')
 
231
        tree_a.commit('commit 2')
 
232
        tree_a.rename_one('file_2', 'file_ii')
 
233
        ## os.chdir('b')
 
234
        self.run_bzr('merge a --uncommitted -d b')
 
235
        self.failUnlessExists('b/file_1')
 
236
        self.failUnlessExists('b/file_ii')
 
237
        tree_b.revert()
 
238
        self.run_bzr_error(('Cannot use --uncommitted and --revision',),
 
239
                           'merge /a --uncommitted -r1 -d b')
 
240
 
 
241
    def pullable_branch(self):
 
242
        tree_a = self.make_branch_and_tree('a')
 
243
        self.build_tree(['a/file'])
 
244
        tree_a.add(['file'])
 
245
        self.id1 = tree_a.commit('commit 1')
 
246
 
 
247
        tree_b = self.make_branch_and_tree('b')
 
248
        tree_b.pull(tree_a.branch)
 
249
        file('b/file', 'wb').write('foo')
 
250
        self.id2 = tree_b.commit('commit 2')
 
251
 
 
252
    def test_merge_pull(self):
 
253
        self.pullable_branch()
 
254
        os.chdir('a')
 
255
        (out, err) = self.run_bzr('merge --pull ../b')
 
256
        self.assertContainsRe(out, 'Now on revision 2\\.')
 
257
        tree_a = WorkingTree.open('.')
 
258
        self.assertEqual([self.id2], tree_a.get_parent_ids())
 
259
 
 
260
    def test_merge_kind_change(self):
 
261
        tree_a = self.make_branch_and_tree('tree_a')
 
262
        self.build_tree_contents([('tree_a/file', 'content_1')])
 
263
        tree_a.add('file', 'file-id')
 
264
        tree_a.commit('added file')
 
265
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
 
266
        os.unlink('tree_a/file')
 
267
        self.build_tree(['tree_a/file/'])
 
268
        tree_a.commit('changed file to directory')
 
269
        os.chdir('tree_b')
 
270
        self.run_bzr('merge ../tree_a')
 
271
        self.assertEqual('directory', file_kind('file'))
 
272
        tree_b.revert()
 
273
        self.assertEqual('file', file_kind('file'))
 
274
        self.build_tree_contents([('file', 'content_2')])
 
275
        tree_b.commit('content change')
 
276
        self.run_bzr('merge ../tree_a', retcode=1)
 
277
        self.assertEqual(tree_b.conflicts(),
 
278
                         [ContentsConflict('file', file_id='file-id')])
 
279
 
 
280
    def test_directive_cherrypick(self):
 
281
        source = self.make_branch_and_tree('source')
 
282
        self.build_tree(['source/a'])
 
283
        source.add('a')
 
284
        source.commit('Added a', rev_id='rev1')
 
285
        self.build_tree(['source/b'])
 
286
        source.add('b')
 
287
        source.commit('Added b', rev_id='rev2')
 
288
        target = self.make_branch_and_tree('target')
 
289
        target.commit('empty commit')
 
290
        self.write_directive('directive', source.branch, 'target', 'rev2',
 
291
                             'rev1')
 
292
        self.run_bzr('merge -d target directive')
 
293
        self.failIfExists('target/a')
 
294
        self.failUnlessExists('target/b')
 
295
 
 
296
    def write_directive(self, filename, source, target, revision_id,
 
297
                        base_revision_id=None, mangle_patch=False):
 
298
        md = merge_directive.MergeDirective2.from_objects(
 
299
            source.repository, revision_id, 0, 0, target,
 
300
            base_revision_id=base_revision_id)
 
301
        if mangle_patch:
 
302
            md.patch = 'asdf\n'
 
303
        self.build_tree_contents([(filename, ''.join(md.to_lines()))])
 
304
 
 
305
    def test_directive_verify_warning(self):
 
306
        source = self.make_branch_and_tree('source')
 
307
        self.build_tree(['source/a'])
 
308
        source.add('a')
 
309
        source.commit('Added a', rev_id='rev1')
 
310
        target = self.make_branch_and_tree('target')
 
311
        target.commit('empty commit')
 
312
        self.write_directive('directive', source.branch, 'target', 'rev1')
 
313
        err = self.run_bzr('merge -d target directive')[1]
 
314
        self.assertNotContainsRe(err, 'Preview patch does not match changes')
 
315
        target.revert()
 
316
        self.write_directive('directive', source.branch, 'target', 'rev1',
 
317
                             mangle_patch=True)
 
318
        err = self.run_bzr('merge -d target directive')[1]
 
319
        self.assertContainsRe(err, 'Preview patch does not match changes')
 
320
 
 
321
    def test_merge_arbitrary(self):
 
322
        target = self.make_branch_and_tree('target')
 
323
        target.commit('empty')
 
324
        # We need a revision that has no integer revno
 
325
        branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
 
326
        self.build_tree(['branch_a/file1'])
 
327
        branch_a.add('file1')
 
328
        branch_a.commit('added file1', rev_id='rev2a')
 
329
        branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
 
330
        self.build_tree(['branch_b/file2'])
 
331
        branch_b.add('file2')
 
332
        branch_b.commit('added file2', rev_id='rev2b')
 
333
        branch_b.merge_from_branch(branch_a.branch)
 
334
        self.failUnlessExists('branch_b/file1')
 
335
        branch_b.commit('merged branch_a', rev_id='rev3b')
 
336
 
 
337
        # It works if the revid has an interger revno
 
338
        self.run_bzr('merge -d target -r revid:rev2a branch_a')
 
339
        self.failUnlessExists('target/file1')
 
340
        self.failIfExists('target/file2')
 
341
        target.revert()
 
342
 
 
343
        # It should work if the revid has no integer revno
 
344
        self.run_bzr('merge -d target -r revid:rev2a branch_b')
 
345
        self.failUnlessExists('target/file1')
 
346
        self.failIfExists('target/file2')
 
347
 
 
348
    def assertDirectoryContent(self, directory, entries, message=''):
 
349
        """Assert whether entries (file or directories) exist in a directory.
 
350
        
 
351
        It also checks that there are no extra entries.
 
352
        """
 
353
        ondisk = os.listdir(directory)
 
354
        if set(ondisk) == set(entries):
 
355
            return
 
356
        if message:
 
357
            message += '\n'
 
358
        raise AssertionError(
 
359
            '%s"%s" directory content is different:\na = %s\nb = %s\n'
 
360
            % (message, directory, sorted(entries), sorted(ondisk)))
 
361
 
 
362
    def test_cherrypicking_merge(self):
 
363
        # make source branch
 
364
        source = self.make_branch_and_tree('source')
 
365
        for f in ('a', 'b', 'c', 'd'):
 
366
            self.build_tree(['source/'+f])
 
367
            source.add(f)
 
368
            source.commit('added '+f, rev_id='rev_'+f)
 
369
        # target branch
 
370
        target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
 
371
        self.assertDirectoryContent('target', ['.bzr', 'a'])
 
372
        # pick 1 revision
 
373
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
 
374
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
 
375
        target.revert()
 
376
        # pick 2 revisions
 
377
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
 
378
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
 
379
        target.revert()
 
380
        # pick 1 revision with option --changes
 
381
        self.run_bzr('merge -d target -c revid:rev_d source')
 
382
        self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])