/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: John Arbash Meinel
  • Date: 2009-12-22 16:28:47 UTC
  • mto: This revision was merged to the branch mainline in revision 4922.
  • Revision ID: john@arbash-meinel.com-20091222162847-tvnsc69to4l4uf5r
Implement a permute_for_extension helper.

Use it for all of the 'simple' extension permutations.
It basically permutes all tests in the current module, by setting TestCase.module.
Which works well for most of our extension tests. Some had more advanced
handling of permutations (extra permutations, custom vars, etc.)

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