~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-08-17 07:52:09 UTC
  • mfrom: (1910.3.4 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20060817075209-e85a1f9e05ff8b87
(andrew) Trivial fixes to NotImplemented errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
21
21
 
22
22
import os
23
23
 
24
 
from bzrlib import merge_directive
25
24
from bzrlib.branch import Branch
26
25
from bzrlib.bzrdir import BzrDir
27
 
from bzrlib.conflicts import ConflictList, ContentsConflict
28
 
from bzrlib.osutils import abspath, file_kind, pathjoin
 
26
from bzrlib.conflicts import ConflictList
 
27
from bzrlib.osutils import abspath
29
28
from bzrlib.tests.blackbox import ExternalBase
30
29
import bzrlib.urlutils as urlutils
31
30
from bzrlib.workingtree import WorkingTree
33
32
 
34
33
class TestMerge(ExternalBase):
35
34
 
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
 
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')
46
43
 
47
44
    def test_merge_reprocess(self):
48
45
        d = BzrDir.create_standalone_workingtree('.')
49
46
        d.commit('h')
50
 
        self.run_bzr('merge . --reprocess --merge-type weave')
 
47
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
51
48
 
52
49
    def test_merge(self):
53
50
        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')])
 
51
        
 
52
        os.mkdir('a')
 
53
        os.chdir('a')
 
54
        self.example_branch()
 
55
        os.chdir('..')
 
56
        self.runbzr('branch a b')
 
57
        os.chdir('b')
 
58
        file('goodbye', 'wt').write('quux')
 
59
        self.runbzr(['commit',  '-m',  "more u's are always good"])
 
60
 
 
61
        os.chdir('../a')
 
62
        file('hello', 'wt').write('quuux')
62
63
        # 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',
 
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
67
                    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')
 
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')
80
75
        self.check_file_contents('goodbye', 'quux')
81
76
        # Merging a branch pulls its revision into the tree
 
77
        a = WorkingTree.open('.')
82
78
        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())
 
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(),
 
82
                          [b.last_revision()])
 
83
        self.runbzr('commit -m merged')
 
84
        self.runbzr('merge ../b -r last:1')
 
85
        self.assertEqual(a.pending_merges(), [])
101
86
 
102
87
    def test_merge_with_missing_file(self):
103
88
        """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')
 
89
        os.mkdir('a')
 
90
        os.chdir('a')
 
91
        os.mkdir('sub')
 
92
        print >> file('sub/a.txt', 'wb'), "hello"
 
93
        print >> file('b.txt', 'wb'), "hello"
 
94
        print >> file('sub/c.txt', 'wb'), "hello"
 
95
        self.runbzr('init')
 
96
        self.runbzr('add')
 
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')
 
105
        os.rmdir('sub')
 
106
        os.unlink('b.txt')
 
107
        self.runbzr(('commit', '-m', 'Removed a.txt'))
 
108
        os.chdir('../b')
 
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'))
133
116
        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')
 
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'))
137
120
 
138
121
    def test_merge_remember(self):
139
122
        """Merge changes from one branch to another and test parent location."""
158
141
        self.assertEqual(None, branch_b.get_parent())
159
142
        # test merge for failure without parent set
160
143
        os.chdir('branch_b')
161
 
        out = self.run_bzr('merge', retcode=3)
 
144
        out = self.runbzr('merge', retcode=3)
162
145
        self.assertEquals(out,
163
146
                ('','bzr: ERROR: No location specified or remembered\n'))
164
147
        # test implicit --remember when no parent set, this merge conflicts
165
148
        self.build_tree(['d'])
166
149
        tree_b.add('d')
167
 
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
168
 
                           'merge ../branch_a')
 
150
        out = self.runbzr('merge ../branch_a', retcode=3)
 
151
        self.assertEquals(out,
 
152
                ('','bzr: ERROR: Working tree has uncommitted changes.\n'))
169
153
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
170
154
        # test implicit --remember after resolving conflict
171
155
        tree_b.commit('commit d')
172
 
        out, err = self.run_bzr('merge')
 
156
        out, err = self.runbzr('merge')
173
157
        
174
158
        base = urlutils.local_path_from_url(branch_a.base)
175
159
        self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
176
 
        self.assertEquals(err, '+N  b\nAll changes applied successfully.\n')
 
160
        self.assertEquals(err, 'All changes applied successfully.\n')
177
161
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
178
 
        # re-open tree as external run_bzr modified it
 
162
        # re-open tree as external runbzr modified it
179
163
        tree_b = branch_b.bzrdir.open_workingtree()
180
164
        tree_b.commit('merge branch_a')
181
165
        # test explicit --remember
182
 
        out, err = self.run_bzr('merge ../branch_c --remember')
 
166
        out, err = self.runbzr('merge ../branch_c --remember')
183
167
        self.assertEquals(out, '')
184
 
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
 
168
        self.assertEquals(err, 'All changes applied successfully.\n')
185
169
        self.assertEquals(abspath(branch_b.get_parent()),
186
170
                          abspath(branch_c.bzrdir.root_transport.base))
187
 
        # re-open tree as external run_bzr modified it
 
171
        # re-open tree as external runbzr modified it
188
172
        tree_b = branch_b.bzrdir.open_workingtree()
189
173
        tree_b.commit('merge branch_c')
190
174
 
191
175
    def test_merge_bundle(self):
192
176
        from bzrlib.testament import Testament
193
177
        tree_a = self.make_branch_and_tree('branch_a')
194
 
        self.build_tree_contents([('branch_a/a', 'hello')])
 
178
        f = file('branch_a/a', 'wb')
 
179
        f.write('hello')
 
180
        f.close()
195
181
        tree_a.add('a')
196
182
        tree_a.commit('message')
197
183
 
198
184
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
199
 
        self.build_tree_contents([('branch_a/a', 'hey there')])
 
185
        f = file('branch_a/a', 'wb')
 
186
        f.write('hey there')
 
187
        f.close()
200
188
        tree_a.commit('message')
201
189
 
202
 
        self.build_tree_contents([('branch_b/a', 'goodbye')])
 
190
        f = file('branch_b/a', 'wb')
 
191
        f.write('goodbye')
 
192
        f.close()
203
193
        tree_b.commit('message')
204
194
        os.chdir('branch_b')
205
 
        self.run_bzr('bundle ../branch_a -o ../bundle')
 
195
        file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
206
196
        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])
 
197
        self.runbzr('merge ../bundle', retcode=1)
 
198
        testament_a = Testament.from_revision(tree_a.branch.repository, 
 
199
                                              tree_b.last_revision())
210
200
        testament_b = Testament.from_revision(tree_b.branch.repository,
211
 
                                              tree_b.get_parent_ids()[0])
 
201
                                              tree_b.last_revision())
212
202
        self.assertEqualDiff(testament_a.as_text(),
213
203
                         testament_b.as_text())
214
204
        tree_a.set_conflicts(ConflictList())
215
205
        tree_a.commit('message')
216
206
        # it is legal to attempt to merge an already-merged bundle
217
 
        output = self.run_bzr('merge ../bundle')[1]
 
207
        output = self.runbzr('merge ../bundle')[1]
218
208
        # but it does nothing
219
209
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
220
210
        self.assertEqual('Nothing to do.\n', output)
230
220
        tree_a.rename_one('file_1', 'file_i')
231
221
        tree_a.commit('commit 2')
232
222
        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'])
 
223
        os.chdir('b')
 
224
        self.run_bzr('merge', '../a', '--uncommitted')
 
225
        self.failUnlessExists('file_1')
 
226
        self.failUnlessExists('file_ii')
 
227
        tree_b.revert([])
 
228
        self.run_bzr_error(('Cannot use --uncommitted and --revision',), 
 
229
                           'merge', '../a', '--uncommitted', '-r1')