/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: mernst at mit
  • Date: 2008-10-16 10:57:16 UTC
  • mto: This revision was merged to the branch mainline in revision 3799.
  • Revision ID: mernst@csail.mit.edu-20081016105716-v8x8n5t2pf7f6uds
Improved documentation of stacked and lightweight branches

These patches improve the User Guide's documentation of stacked and
lightweight branches.

Section "1.2.6 Putting the concepts together" should mention stacked
branches and the difference between them and lightweight branches.  It
should also contain links to further details of the common scenarios.

Section "5.3.4 Getting a lightweight checkout" should mention stacked
branches as an option, and should link to all the options, not just some of
them.  It should also clarify that lightweight only applies to checkouts,
not to arbitrary branches.

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 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
 
64
 
 
65
    def test_merge_reprocess(self):
 
66
        d = BzrDir.create_standalone_workingtree('.')
 
67
        d.commit('h')
 
68
        self.run_bzr('merge . --reprocess --merge-type weave')
 
69
 
 
70
    def test_merge(self):
 
71
        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')])
 
80
        # 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',
 
86
                    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')
 
100
        self.check_file_contents('goodbye', 'quux')
 
101
        # Merging a branch pulls its revision into the tree
 
102
        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'))
 
176
 
 
177
    def test_merge_with_missing_file(self):
 
178
        """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')
 
208
        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')
 
212
 
 
213
    def test_merge_remember(self):
 
214
        """Merge changes from one branch to another, test submit location."""
 
215
        tree_a = self.make_branch_and_tree('branch_a')
 
216
        branch_a = tree_a.branch
 
217
        self.build_tree(['branch_a/a'])
 
218
        tree_a.add('a')
 
219
        tree_a.commit('commit a')
 
220
        branch_b = branch_a.bzrdir.sprout('branch_b').open_branch()
 
221
        tree_b = branch_b.bzrdir.open_workingtree()
 
222
        branch_c = branch_a.bzrdir.sprout('branch_c').open_branch()
 
223
        tree_c = branch_c.bzrdir.open_workingtree()
 
224
        self.build_tree(['branch_a/b'])
 
225
        tree_a.add('b')
 
226
        tree_a.commit('commit b')
 
227
        self.build_tree(['branch_c/c'])
 
228
        tree_c.add('c')
 
229
        tree_c.commit('commit c')
 
230
        # reset parent
 
231
        parent = branch_b.get_parent()
 
232
        branch_b.set_parent(None)
 
233
        self.assertEqual(None, branch_b.get_parent())
 
234
        # test merge for failure without parent set
 
235
        os.chdir('branch_b')
 
236
        out = self.run_bzr('merge', retcode=3)
 
237
        self.assertEquals(out,
 
238
                ('','bzr: ERROR: No location specified or remembered\n'))
 
239
        # test implicit --remember when no parent set, this merge conflicts
 
240
        self.build_tree(['d'])
 
241
        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))
 
246
        # test implicit --remember after resolving conflict
 
247
        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
 
257
        tree_b = branch_b.bzrdir.open_workingtree()
 
258
        tree_b.commit('merge branch_a')
 
259
        # test explicit --remember
 
260
        out, err = self.run_bzr('merge ../branch_c --remember')
 
261
        self.assertEquals(out, '')
 
262
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
 
263
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
264
                          abspath(branch_c.bzrdir.root_transport.base))
 
265
        # re-open tree as external run_bzr modified it
 
266
        tree_b = branch_b.bzrdir.open_workingtree()
 
267
        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())))