~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2006-05-27 01:54:40 UTC
  • mto: (1711.2.26 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1734.
  • Revision ID: john@arbash-meinel.com-20060527015440-1a10495d8e56ed5f
deprecating appendpath, it does exactly what pathjoin does

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.osutils import abspath
29
27
from bzrlib.tests.blackbox import ExternalBase
30
 
import bzrlib.urlutils as urlutils
31
28
from bzrlib.workingtree import WorkingTree
32
29
 
33
30
 
34
31
class TestMerge(ExternalBase):
35
32
 
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 create_conflicting_branches(self):
48
 
        """Create two branches which have overlapping modifications.
49
 
 
50
 
        :return: (tree, other_branch) Where merging other_branch causes a file
51
 
            conflict.
52
 
        """
53
 
        builder = self.make_branch_builder('branch')
54
 
        builder.build_snapshot('rev1', None,
55
 
            [('add', ('', 'root-id', 'directory', None)),
56
 
             ('add', ('fname', 'f-id', 'file', 'a\nb\nc\n'))])
57
 
        builder.build_snapshot('rev2other', ['rev1'],
58
 
            [('modify', ('f-id', 'a\nB\nD\n'))])
59
 
        other = builder.get_branch().bzrdir.sprout('other').open_branch()
60
 
        builder.build_snapshot('rev2this', ['rev1'],
61
 
            [('modify', ('f-id', 'a\nB\nC\n'))])
62
 
        tree = builder.get_branch().create_checkout('tree', lightweight=True)
63
 
        return tree, other
 
33
    def example_branch(test):
 
34
        test.runbzr('init')
 
35
        file('hello', 'wt').write('foo')
 
36
        test.runbzr('add hello')
 
37
        test.runbzr('commit -m setup hello')
 
38
        file('goodbye', 'wt').write('baz')
 
39
        test.runbzr('add goodbye')
 
40
        test.runbzr('commit -m setup goodbye')
64
41
 
65
42
    def test_merge_reprocess(self):
66
43
        d = BzrDir.create_standalone_workingtree('.')
67
44
        d.commit('h')
68
 
        self.run_bzr('merge . --reprocess --merge-type weave')
 
45
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
69
46
 
70
47
    def test_merge(self):
71
48
        from bzrlib.branch import Branch
72
 
 
73
 
        a_tree = self.example_branch('a')
74
 
        ancestor = a_tree.branch.revno()
75
 
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
76
 
        self.build_tree_contents([('b/goodbye', 'quux')])
77
 
        b_tree.commit(message="more u's are always good")
78
 
 
79
 
        self.build_tree_contents([('a/hello', 'quuux')])
 
49
        
 
50
        os.mkdir('a')
 
51
        os.chdir('a')
 
52
        self.example_branch()
 
53
        os.chdir('..')
 
54
        self.runbzr('branch a b')
 
55
        os.chdir('b')
 
56
        file('goodbye', 'wt').write('quux')
 
57
        self.runbzr(['commit',  '-m',  "more u's are always good"])
 
58
 
 
59
        os.chdir('../a')
 
60
        file('hello', 'wt').write('quuux')
80
61
        # We can't merge when there are in-tree changes
81
 
        os.chdir('a')
82
 
        self.run_bzr('merge ../b', retcode=3)
83
 
        a = WorkingTree.open('.')
84
 
        a_tip = a.commit("Like an epidemic of u's")
85
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
 
62
        self.runbzr('merge ../b', retcode=3)
 
63
        self.runbzr(['commit', '-m', "Like an epidemic of u's"])
 
64
        self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
86
65
                    retcode=3)
87
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
88
 
        a_tree.revert(backups=False)
89
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
90
 
        a_tree.revert(backups=False)
91
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type lca')
92
 
        a_tree.revert(backups=False)
93
 
        self.run_bzr_error(['Show-base is not supported for this merge type'],
94
 
                           'merge ../b -r last:1..last:1 --merge-type weave'
95
 
                           ' --show-base')
96
 
        a_tree.revert(backups=False)
97
 
        self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
98
 
        a_tree.revert(backups=False)
99
 
        self.run_bzr('merge ../b -r last:1')
 
66
        self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
 
67
        self.runbzr('revert --no-backup')
 
68
        self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
 
69
        self.runbzr('revert --no-backup')
 
70
        self.runbzr('merge ../b -r last:1..last:1 --reprocess')
 
71
        self.runbzr('revert --no-backup')
 
72
        self.runbzr('merge ../b -r last:1')
100
73
        self.check_file_contents('goodbye', 'quux')
101
74
        # Merging a branch pulls its revision into the tree
 
75
        a = WorkingTree.open('.')
102
76
        b = Branch.open('../b')
103
 
        b_tip = b.last_revision()
104
 
        self.failUnless(a.branch.repository.has_revision(b_tip))
105
 
        self.assertEqual([a_tip, b_tip], a.get_parent_ids())
106
 
        a_tree.revert(backups=False)
107
 
        out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
108
 
        self.assertTrue("Not a branch" in err)
109
 
        self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
110
 
                    %(ancestor,b.revno()))
111
 
        self.assertEquals(a.get_parent_ids(), 
112
 
                          [a.branch.last_revision(), b.last_revision()])
113
 
        self.check_file_contents('goodbye', 'quux')
114
 
        a_tree.revert(backups=False)
115
 
        self.run_bzr('merge -r revno:%d:../b'%b.revno())
116
 
        self.assertEquals(a.get_parent_ids(),
117
 
                          [a.branch.last_revision(), b.last_revision()])
118
 
        a_tip = a.commit('merged')
119
 
        self.run_bzr('merge ../b -r last:1')
120
 
        self.assertEqual([a_tip], a.get_parent_ids())
121
 
 
122
 
    def test_merge_defaults_to_reprocess(self):
123
 
        tree, other = self.create_conflicting_branches()
124
 
        # The default merge algorithm should enable 'reprocess' because
125
 
        # 'show-base' is not set
126
 
        self.run_bzr('merge ../other', working_dir='tree',
127
 
                     retcode=1)
128
 
        self.assertEqualDiff('a\n'
129
 
                             'B\n'
130
 
                             '<<<<<<< TREE\n'
131
 
                             'C\n'
132
 
                             '=======\n'
133
 
                             'D\n'
134
 
                             '>>>>>>> MERGE-SOURCE\n',
135
 
                             tree.get_file_text('f-id'))
136
 
 
137
 
    def test_merge_explicit_reprocess_show_base(self):
138
 
        tree, other = self.create_conflicting_branches()
139
 
        # Explicitly setting --reprocess, and --show-base is an error
140
 
        self.run_bzr_error(['Cannot do conflict reduction and show base'],
141
 
                           'merge ../other --reprocess --show-base',
142
 
                           working_dir='tree')
143
 
 
144
 
    def test_merge_override_reprocess(self):
145
 
        tree, other = self.create_conflicting_branches()
146
 
        # Explicitly disable reprocess
147
 
        self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
148
 
                     retcode=1)
149
 
        self.assertEqualDiff('a\n'
150
 
                             '<<<<<<< TREE\n'
151
 
                             'B\n'
152
 
                             'C\n'
153
 
                             '=======\n'
154
 
                             'B\n'
155
 
                             'D\n'
156
 
                             '>>>>>>> MERGE-SOURCE\n',
157
 
                             tree.get_file_text('f-id'))
158
 
 
159
 
    def test_merge_override_show_base(self):
160
 
        tree, other = self.create_conflicting_branches()
161
 
        # Setting '--show-base' will auto-disable '--reprocess'
162
 
        self.run_bzr('merge ../other --show-base', working_dir='tree',
163
 
                     retcode=1)
164
 
        self.assertEqualDiff('a\n'
165
 
                             '<<<<<<< TREE\n'
166
 
                             'B\n'
167
 
                             'C\n'
168
 
                             '||||||| BASE-REVISION\n'
169
 
                             'b\n'
170
 
                             'c\n'
171
 
                             '=======\n'
172
 
                             'B\n'
173
 
                             'D\n'
174
 
                             '>>>>>>> MERGE-SOURCE\n',
175
 
                             tree.get_file_text('f-id'))
 
77
        a.branch.repository.get_revision_xml(b.last_revision())
 
78
        self.log('pending merges: %s', a.pending_merges())
 
79
        self.assertEquals(a.pending_merges(),
 
80
                          [b.last_revision()])
 
81
        self.runbzr('commit -m merged')
 
82
        self.runbzr('merge ../b -r last:1')
 
83
        self.assertEqual(a.pending_merges(), [])
176
84
 
177
85
    def test_merge_with_missing_file(self):
178
86
        """Merge handles missing file conflicts"""
179
 
        self.build_tree_contents([
180
 
            ('a/',),
181
 
            ('a/sub/',),
182
 
            ('a/sub/a.txt', 'hello\n'),
183
 
            ('a/b.txt', 'hello\n'),
184
 
            ('a/sub/c.txt', 'hello\n')])
185
 
        a_tree = self.make_branch_and_tree('a')
186
 
        a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
187
 
        a_tree.commit(message='added a')
188
 
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
189
 
        self.build_tree_contents([
190
 
            ('a/sub/a.txt', 'hello\nthere\n'),
191
 
            ('a/b.txt', 'hello\nthere\n'),
192
 
            ('a/sub/c.txt', 'hello\nthere\n')])
193
 
        a_tree.commit(message='Added there')
194
 
        os.remove('a/sub/a.txt')
195
 
        os.remove('a/sub/c.txt')
196
 
        os.rmdir('a/sub')
197
 
        os.remove('a/b.txt')
198
 
        a_tree.commit(message='Removed a.txt')
199
 
        self.build_tree_contents([
200
 
            ('b/sub/a.txt', 'hello\nsomething\n'),
201
 
            ('b/b.txt', 'hello\nsomething\n'),
202
 
            ('b/sub/c.txt', 'hello\nsomething\n')])
203
 
        b_tree.commit(message='Modified a.txt')
204
 
        os.chdir('b')
205
 
        self.run_bzr('merge ../a/', retcode=1)
206
 
        self.failUnlessExists('sub/a.txt.THIS')
207
 
        self.failUnlessExists('sub/a.txt.BASE')
 
87
        os.mkdir('a')
 
88
        os.chdir('a')
 
89
        os.mkdir('sub')
 
90
        print >> file('sub/a.txt', 'wb'), "hello"
 
91
        print >> file('b.txt', 'wb'), "hello"
 
92
        print >> file('sub/c.txt', 'wb'), "hello"
 
93
        self.runbzr('init')
 
94
        self.runbzr('add')
 
95
        self.runbzr(('commit', '-m', 'added a'))
 
96
        self.runbzr('branch . ../b')
 
97
        print >> file('sub/a.txt', 'ab'), "there"
 
98
        print >> file('b.txt', 'ab'), "there"
 
99
        print >> file('sub/c.txt', 'ab'), "there"
 
100
        self.runbzr(('commit', '-m', 'Added there'))
 
101
        os.unlink('sub/a.txt')
 
102
        os.unlink('sub/c.txt')
 
103
        os.rmdir('sub')
 
104
        os.unlink('b.txt')
 
105
        self.runbzr(('commit', '-m', 'Removed a.txt'))
 
106
        os.chdir('../b')
 
107
        print >> file('sub/a.txt', 'ab'), "something"
 
108
        print >> file('b.txt', 'ab'), "something"
 
109
        print >> file('sub/c.txt', 'ab'), "something"
 
110
        self.runbzr(('commit', '-m', 'Modified a.txt'))
 
111
        self.runbzr('merge ../a/', retcode=1)
 
112
        self.assert_(os.path.exists('sub/a.txt.THIS'))
 
113
        self.assert_(os.path.exists('sub/a.txt.BASE'))
208
114
        os.chdir('../a')
209
 
        self.run_bzr('merge ../b/', retcode=1)
210
 
        self.failUnlessExists('sub/a.txt.OTHER')
211
 
        self.failUnlessExists('sub/a.txt.BASE')
 
115
        self.runbzr('merge ../b/', retcode=1)
 
116
        self.assert_(os.path.exists('sub/a.txt.OTHER'))
 
117
        self.assert_(os.path.exists('sub/a.txt.BASE'))
212
118
 
213
119
    def test_merge_remember(self):
214
 
        """Merge changes from one branch to another, test submit location."""
 
120
        """Merge changes from one branch to another and test parent location."""
215
121
        tree_a = self.make_branch_and_tree('branch_a')
216
122
        branch_a = tree_a.branch
217
123
        self.build_tree(['branch_a/a'])
233
139
        self.assertEqual(None, branch_b.get_parent())
234
140
        # test merge for failure without parent set
235
141
        os.chdir('branch_b')
236
 
        out = self.run_bzr('merge', retcode=3)
 
142
        out = self.runbzr('merge', retcode=3)
237
143
        self.assertEquals(out,
238
 
                ('','bzr: ERROR: No location specified or remembered\n'))
 
144
                ('','bzr: ERROR: No merge branch known or specified.\n'))
239
145
        # test implicit --remember when no parent set, this merge conflicts
240
146
        self.build_tree(['d'])
241
147
        tree_b.add('d')
242
 
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
243
 
                           'merge ../branch_a')
244
 
        self.assertEquals(abspath(branch_b.get_submit_branch()),
245
 
                          abspath(parent))
 
148
        out = self.runbzr('merge ../branch_a', retcode=3)
 
149
        self.assertEquals(out,
 
150
                ('','bzr: ERROR: Working tree has uncommitted changes.\n'))
 
151
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
246
152
        # test implicit --remember after resolving conflict
247
153
        tree_b.commit('commit d')
248
 
        out, err = self.run_bzr('merge')
249
 
        
250
 
        base = urlutils.local_path_from_url(branch_a.base)
251
 
        self.assertStartsWith(err,
252
 
                          'Merging from remembered submit location %s\n' % (base,))
253
 
        self.assertEndsWith(err, '+N  b\nAll changes applied successfully.\n')
254
 
        self.assertEquals(abspath(branch_b.get_submit_branch()),
255
 
                          abspath(parent))
256
 
        # re-open tree as external run_bzr modified it
 
154
        out, err = self.runbzr('merge')
 
155
        self.assertEquals(out, 'Using saved branch: ../branch_a\n')
 
156
        self.assertEquals(err, 'All changes applied successfully.\n')
 
157
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
 
158
        # re-open tree as external runbzr modified it
257
159
        tree_b = branch_b.bzrdir.open_workingtree()
258
160
        tree_b.commit('merge branch_a')
259
161
        # test explicit --remember
260
 
        out, err = self.run_bzr('merge ../branch_c --remember')
 
162
        out, err = self.runbzr('merge ../branch_c --remember')
261
163
        self.assertEquals(out, '')
262
 
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
263
 
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
164
        self.assertEquals(err, 'All changes applied successfully.\n')
 
165
        self.assertEquals(abspath(branch_b.get_parent()),
264
166
                          abspath(branch_c.bzrdir.root_transport.base))
265
 
        # re-open tree as external run_bzr modified it
 
167
        # re-open tree as external runbzr modified it
266
168
        tree_b = branch_b.bzrdir.open_workingtree()
267
169
        tree_b.commit('merge branch_c')
268
 
 
269
 
    def test_merge_bundle(self):
270
 
        from bzrlib.testament import Testament
271
 
        tree_a = self.make_branch_and_tree('branch_a')
272
 
        self.build_tree_contents([('branch_a/a', 'hello')])
273
 
        tree_a.add('a')
274
 
        tree_a.commit('message')
275
 
 
276
 
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
277
 
        self.build_tree_contents([('branch_a/a', 'hey there')])
278
 
        tree_a.commit('message')
279
 
 
280
 
        self.build_tree_contents([('branch_b/a', 'goodbye')])
281
 
        tree_b.commit('message')
282
 
        os.chdir('branch_b')
283
 
        self.run_bzr('bundle ../branch_a -o ../bundle')
284
 
        os.chdir('../branch_a')
285
 
        self.run_bzr('merge ../bundle', retcode=1)
286
 
        testament_a = Testament.from_revision(tree_a.branch.repository,
287
 
                                              tree_b.get_parent_ids()[0])
288
 
        testament_b = Testament.from_revision(tree_b.branch.repository,
289
 
                                              tree_b.get_parent_ids()[0])
290
 
        self.assertEqualDiff(testament_a.as_text(),
291
 
                         testament_b.as_text())
292
 
        tree_a.set_conflicts(ConflictList())
293
 
        tree_a.commit('message')
294
 
        # it is legal to attempt to merge an already-merged bundle
295
 
        output = self.run_bzr('merge ../bundle')[1]
296
 
        # but it does nothing
297
 
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
298
 
        self.assertEqual('Nothing to do.\n', output)
299
 
 
300
 
    def test_merge_uncommitted(self):
301
 
        """Check that merge --uncommitted behaves properly"""
302
 
        tree_a = self.make_branch_and_tree('a')
303
 
        self.build_tree(['a/file_1', 'a/file_2'])
304
 
        tree_a.add(['file_1', 'file_2'])
305
 
        tree_a.commit('commit 1')
306
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
307
 
        self.failUnlessExists('b/file_1')
308
 
        tree_a.rename_one('file_1', 'file_i')
309
 
        tree_a.commit('commit 2')
310
 
        tree_a.rename_one('file_2', 'file_ii')
311
 
        ## os.chdir('b')
312
 
        self.run_bzr('merge a --uncommitted -d b')
313
 
        self.failUnlessExists('b/file_1')
314
 
        self.failUnlessExists('b/file_ii')
315
 
        tree_b.revert()
316
 
        self.run_bzr_error(('Cannot use --uncommitted and --revision',),
317
 
                           'merge /a --uncommitted -r1 -d b')
318
 
 
319
 
    def test_merge_uncommitted_file(self):
320
 
        """It should be possible to merge changes from a single file."""
321
 
        tree_a = self.make_branch_and_tree('tree_a')
322
 
        tree_a.commit('initial commit')
323
 
        tree_a.bzrdir.sprout('tree_b')
324
 
        self.build_tree(['tree_a/file1', 'tree_a/file2'])
325
 
        tree_a.add(['file1', 'file2'])
326
 
        os.chdir('tree_b')
327
 
        self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'])
328
 
        self.failUnlessExists('file1')
329
 
        self.failIfExists('file2')
330
 
 
331
 
    def pullable_branch(self):
332
 
        tree_a = self.make_branch_and_tree('a')
333
 
        self.build_tree(['a/file'])
334
 
        tree_a.add(['file'])
335
 
        self.id1 = tree_a.commit('commit 1')
336
 
 
337
 
        tree_b = self.make_branch_and_tree('b')
338
 
        tree_b.pull(tree_a.branch)
339
 
        file('b/file', 'wb').write('foo')
340
 
        self.id2 = tree_b.commit('commit 2')
341
 
 
342
 
    def test_merge_pull(self):
343
 
        self.pullable_branch()
344
 
        os.chdir('a')
345
 
        (out, err) = self.run_bzr('merge --pull ../b')
346
 
        self.assertContainsRe(out, 'Now on revision 2\\.')
347
 
        tree_a = WorkingTree.open('.')
348
 
        self.assertEqual([self.id2], tree_a.get_parent_ids())
349
 
 
350
 
    def test_merge_kind_change(self):
351
 
        tree_a = self.make_branch_and_tree('tree_a')
352
 
        self.build_tree_contents([('tree_a/file', 'content_1')])
353
 
        tree_a.add('file', 'file-id')
354
 
        tree_a.commit('added file')
355
 
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
356
 
        os.unlink('tree_a/file')
357
 
        self.build_tree(['tree_a/file/'])
358
 
        tree_a.commit('changed file to directory')
359
 
        os.chdir('tree_b')
360
 
        self.run_bzr('merge ../tree_a')
361
 
        self.assertEqual('directory', file_kind('file'))
362
 
        tree_b.revert()
363
 
        self.assertEqual('file', file_kind('file'))
364
 
        self.build_tree_contents([('file', 'content_2')])
365
 
        tree_b.commit('content change')
366
 
        self.run_bzr('merge ../tree_a', retcode=1)
367
 
        self.assertEqual(tree_b.conflicts(),
368
 
                         [ContentsConflict('file', file_id='file-id')])
369
 
 
370
 
    def test_directive_cherrypick(self):
371
 
        source = self.make_branch_and_tree('source')
372
 
        self.build_tree(['source/a'])
373
 
        source.add('a')
374
 
        source.commit('Added a', rev_id='rev1')
375
 
        self.build_tree(['source/b'])
376
 
        source.add('b')
377
 
        source.commit('Added b', rev_id='rev2')
378
 
        target = self.make_branch_and_tree('target')
379
 
        target.commit('empty commit')
380
 
        self.write_directive('directive', source.branch, 'target', 'rev2',
381
 
                             'rev1')
382
 
        out, err = self.run_bzr('merge -d target directive')
383
 
        self.failIfExists('target/a')
384
 
        self.failUnlessExists('target/b')
385
 
        self.assertContainsRe(err, 'Performing cherrypick')
386
 
 
387
 
    def write_directive(self, filename, source, target, revision_id,
388
 
                        base_revision_id=None, mangle_patch=False):
389
 
        md = merge_directive.MergeDirective2.from_objects(
390
 
            source.repository, revision_id, 0, 0, target,
391
 
            base_revision_id=base_revision_id)
392
 
        if mangle_patch:
393
 
            md.patch = 'asdf\n'
394
 
        self.build_tree_contents([(filename, ''.join(md.to_lines()))])
395
 
 
396
 
    def test_directive_verify_warning(self):
397
 
        source = self.make_branch_and_tree('source')
398
 
        self.build_tree(['source/a'])
399
 
        source.add('a')
400
 
        source.commit('Added a', rev_id='rev1')
401
 
        target = self.make_branch_and_tree('target')
402
 
        target.commit('empty commit')
403
 
        self.write_directive('directive', source.branch, 'target', 'rev1')
404
 
        err = self.run_bzr('merge -d target directive')[1]
405
 
        self.assertNotContainsRe(err, 'Preview patch does not match changes')
406
 
        target.revert()
407
 
        self.write_directive('directive', source.branch, 'target', 'rev1',
408
 
                             mangle_patch=True)
409
 
        err = self.run_bzr('merge -d target directive')[1]
410
 
        self.assertContainsRe(err, 'Preview patch does not match changes')
411
 
 
412
 
    def test_merge_arbitrary(self):
413
 
        target = self.make_branch_and_tree('target')
414
 
        target.commit('empty')
415
 
        # We need a revision that has no integer revno
416
 
        branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
417
 
        self.build_tree(['branch_a/file1'])
418
 
        branch_a.add('file1')
419
 
        branch_a.commit('added file1', rev_id='rev2a')
420
 
        branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
421
 
        self.build_tree(['branch_b/file2'])
422
 
        branch_b.add('file2')
423
 
        branch_b.commit('added file2', rev_id='rev2b')
424
 
        branch_b.merge_from_branch(branch_a.branch)
425
 
        self.failUnlessExists('branch_b/file1')
426
 
        branch_b.commit('merged branch_a', rev_id='rev3b')
427
 
 
428
 
        # It works if the revid has an interger revno
429
 
        self.run_bzr('merge -d target -r revid:rev2a branch_a')
430
 
        self.failUnlessExists('target/file1')
431
 
        self.failIfExists('target/file2')
432
 
        target.revert()
433
 
 
434
 
        # It should work if the revid has no integer revno
435
 
        self.run_bzr('merge -d target -r revid:rev2a branch_b')
436
 
        self.failUnlessExists('target/file1')
437
 
        self.failIfExists('target/file2')
438
 
 
439
 
    def assertDirectoryContent(self, directory, entries, message=''):
440
 
        """Assert whether entries (file or directories) exist in a directory.
441
 
        
442
 
        It also checks that there are no extra entries.
443
 
        """
444
 
        ondisk = os.listdir(directory)
445
 
        if set(ondisk) == set(entries):
446
 
            return
447
 
        if message:
448
 
            message += '\n'
449
 
        raise AssertionError(
450
 
            '%s"%s" directory content is different:\na = %s\nb = %s\n'
451
 
            % (message, directory, sorted(entries), sorted(ondisk)))
452
 
 
453
 
    def test_cherrypicking_merge(self):
454
 
        # make source branch
455
 
        source = self.make_branch_and_tree('source')
456
 
        for f in ('a', 'b', 'c', 'd'):
457
 
            self.build_tree(['source/'+f])
458
 
            source.add(f)
459
 
            source.commit('added '+f, rev_id='rev_'+f)
460
 
        # target branch
461
 
        target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
462
 
        self.assertDirectoryContent('target', ['.bzr', 'a'])
463
 
        # pick 1 revision
464
 
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
465
 
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
466
 
        target.revert()
467
 
        # pick 2 revisions
468
 
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
469
 
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
470
 
        target.revert()
471
 
        # pick 1 revision with option --changes
472
 
        self.run_bzr('merge -d target -c revid:rev_d source')
473
 
        self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
474
 
 
475
 
    def test_merge_criss_cross(self):
476
 
        tree_a = self.make_branch_and_tree('a')
477
 
        tree_a.commit('', rev_id='rev1')
478
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
479
 
        tree_a.commit('', rev_id='rev2a')
480
 
        tree_b.commit('', rev_id='rev2b')
481
 
        tree_a.merge_from_branch(tree_b.branch)
482
 
        tree_b.merge_from_branch(tree_a.branch)
483
 
        tree_a.commit('', rev_id='rev3a')
484
 
        tree_b.commit('', rev_id='rev3b')
485
 
        graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
486
 
        out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
487
 
        self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
488
 
 
489
 
    def test_merge_from_submit(self):
490
 
        tree_a = self.make_branch_and_tree('a')
491
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
492
 
        tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
493
 
        out, err = self.run_bzr(['merge', '-d', 'c'])
494
 
        self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
495
 
        tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
496
 
        out, err = self.run_bzr(['merge', '-d', 'c'])
497
 
        self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
498
 
 
499
 
    def test_remember_sets_submit(self):
500
 
        tree_a = self.make_branch_and_tree('a')
501
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
502
 
        self.assertIs(tree_b.branch.get_submit_branch(), None)
503
 
 
504
 
        # Remember should not happen if using default from parent
505
 
        out, err = self.run_bzr(['merge', '-d', 'b'])
506
 
        self.assertIs(tree_b.branch.get_submit_branch(), None)
507
 
 
508
 
        # Remember should happen if user supplies location
509
 
        out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
510
 
        self.assertEqual(tree_b.branch.get_submit_branch(),
511
 
                         tree_a.bzrdir.root_transport.base)
512
 
 
513
 
    def test_weave_cherrypick(self):
514
 
        this_tree = self.make_branch_and_tree('this')
515
 
        self.build_tree_contents([('this/file', "a\n")])
516
 
        this_tree.add('file')
517
 
        this_tree.commit('rev1')
518
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
519
 
        self.build_tree_contents([('other/file', "a\nb\n")])
520
 
        other_tree.commit('rev2b')
521
 
        self.build_tree_contents([('other/file', "c\na\nb\n")])
522
 
        other_tree.commit('rev3b')
523
 
        self.run_bzr('merge --weave -d this other -r -2..-1')
524
 
        self.assertFileEqual('c\na\n', 'this/file')
525
 
 
526
 
    def test_lca_merge_criss_cross(self):
527
 
        tree_a = self.make_branch_and_tree('a')
528
 
        self.build_tree_contents([('a/file', 'base-contents\n')])
529
 
        tree_a.add('file')
530
 
        tree_a.commit('', rev_id='rev1')
531
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
532
 
        self.build_tree_contents([('a/file',
533
 
                                   'base-contents\nthis-contents\n')])
534
 
        tree_a.commit('', rev_id='rev2a')
535
 
        self.build_tree_contents([('b/file',
536
 
                                   'base-contents\nother-contents\n')])
537
 
        tree_b.commit('', rev_id='rev2b')
538
 
        tree_a.merge_from_branch(tree_b.branch)
539
 
        self.build_tree_contents([('a/file',
540
 
                                   'base-contents\nthis-contents\n')])
541
 
        tree_a.set_conflicts(ConflictList())
542
 
        tree_b.merge_from_branch(tree_a.branch)
543
 
        self.build_tree_contents([('b/file',
544
 
                                   'base-contents\nother-contents\n')])
545
 
        tree_b.set_conflicts(ConflictList())
546
 
        tree_a.commit('', rev_id='rev3a')
547
 
        tree_b.commit('', rev_id='rev3b')
548
 
        out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
549
 
        self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
550
 
                             '=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
551
 
                             'a/file')
552
 
 
553
 
    def test_merge_preview(self):
554
 
        this_tree = self.make_branch_and_tree('this')
555
 
        this_tree.commit('rev1')
556
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
557
 
        self.build_tree_contents([('other/file', 'new line')])
558
 
        other_tree.add('file')
559
 
        other_tree.commit('rev2a')
560
 
        this_tree.commit('rev2b')
561
 
        out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
562
 
        self.assertContainsRe(out, '\+new line')
563
 
        self.assertNotContainsRe(err, '\+N  file\n')
564
 
        this_tree.lock_read()
565
 
        self.addCleanup(this_tree.unlock)
566
 
        self.assertEqual([],
567
 
                         list(this_tree.iter_changes(this_tree.basis_tree())))