/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-06-18 18:18:36 UTC
  • mto: This revision was merged to the branch mainline in revision 4461.
  • Revision ID: john@arbash-meinel.com-20090618181836-biodfkat9a8eyzjz
The new add_inventory_by_delta is returning a CHKInventory when mapping from NULL
Which is completely valid, but 'broke' one of the tests.
So to fix it, changed the test to use CHKInventories on both sides, and add an __eq__
member. The nice thing is that CHKInventory.__eq__ is fairly cheap, since it only
has to check the root keys.

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 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
 
 
240
        # test uncommitted changes
 
241
        self.build_tree(['d'])
 
242
        tree_b.add('d')
 
243
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
 
244
                           'merge')
 
245
 
 
246
        # merge should now pass and implicitly remember merge location
 
247
        tree_b.commit('commit d')
 
248
        out, err = self.run_bzr('merge ../branch_a')
 
249
 
 
250
        base = urlutils.local_path_from_url(branch_a.base)
 
251
        self.assertEndsWith(err, '+N  b\nAll changes applied successfully.\n')
 
252
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
253
                          abspath(parent))
 
254
        # test implicit --remember when committing new file
 
255
        self.build_tree(['e'])
 
256
        tree_b.add('e')
 
257
        tree_b.commit('commit e')
 
258
        out, err = self.run_bzr('merge')
 
259
        self.assertStartsWith(err,
 
260
                          'Merging from remembered submit location %s\n' % (base,))
 
261
        # re-open tree as external run_bzr modified it
 
262
        tree_b = branch_b.bzrdir.open_workingtree()
 
263
        tree_b.commit('merge branch_a')
 
264
        # test explicit --remember
 
265
        out, err = self.run_bzr('merge ../branch_c --remember')
 
266
        self.assertEquals(out, '')
 
267
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
 
268
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
269
                          abspath(branch_c.bzrdir.root_transport.base))
 
270
        # re-open tree as external run_bzr modified it
 
271
        tree_b = branch_b.bzrdir.open_workingtree()
 
272
        tree_b.commit('merge branch_c')
 
273
 
 
274
    def test_merge_bundle(self):
 
275
        from bzrlib.testament import Testament
 
276
        tree_a = self.make_branch_and_tree('branch_a')
 
277
        self.build_tree_contents([('branch_a/a', 'hello')])
 
278
        tree_a.add('a')
 
279
        tree_a.commit('message')
 
280
 
 
281
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
 
282
        self.build_tree_contents([('branch_a/a', 'hey there')])
 
283
        tree_a.commit('message')
 
284
 
 
285
        self.build_tree_contents([('branch_b/a', 'goodbye')])
 
286
        tree_b.commit('message')
 
287
        os.chdir('branch_b')
 
288
        self.run_bzr('bundle ../branch_a -o ../bundle')
 
289
        os.chdir('../branch_a')
 
290
        self.run_bzr('merge ../bundle', retcode=1)
 
291
        testament_a = Testament.from_revision(tree_a.branch.repository,
 
292
                                              tree_b.get_parent_ids()[0])
 
293
        testament_b = Testament.from_revision(tree_b.branch.repository,
 
294
                                              tree_b.get_parent_ids()[0])
 
295
        self.assertEqualDiff(testament_a.as_text(),
 
296
                         testament_b.as_text())
 
297
        tree_a.set_conflicts(ConflictList())
 
298
        tree_a.commit('message')
 
299
        # it is legal to attempt to merge an already-merged bundle
 
300
        output = self.run_bzr('merge ../bundle')[1]
 
301
        # but it does nothing
 
302
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
 
303
        self.assertEqual('Nothing to do.\n', output)
 
304
 
 
305
    def test_merge_uncommitted(self):
 
306
        """Check that merge --uncommitted behaves properly"""
 
307
        tree_a = self.make_branch_and_tree('a')
 
308
        self.build_tree(['a/file_1', 'a/file_2'])
 
309
        tree_a.add(['file_1', 'file_2'])
 
310
        tree_a.commit('commit 1')
 
311
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
312
        self.failUnlessExists('b/file_1')
 
313
        tree_a.rename_one('file_1', 'file_i')
 
314
        tree_a.commit('commit 2')
 
315
        tree_a.rename_one('file_2', 'file_ii')
 
316
        ## os.chdir('b')
 
317
        self.run_bzr('merge a --uncommitted -d b')
 
318
        self.failUnlessExists('b/file_1')
 
319
        self.failUnlessExists('b/file_ii')
 
320
        tree_b.revert()
 
321
        self.run_bzr_error(('Cannot use --uncommitted and --revision',),
 
322
                           'merge /a --uncommitted -r1 -d b')
 
323
 
 
324
    def test_merge_uncommitted_file(self):
 
325
        """It should be possible to merge changes from a single file."""
 
326
        tree_a = self.make_branch_and_tree('tree_a')
 
327
        tree_a.commit('initial commit')
 
328
        tree_a.bzrdir.sprout('tree_b')
 
329
        self.build_tree(['tree_a/file1', 'tree_a/file2'])
 
330
        tree_a.add(['file1', 'file2'])
 
331
        os.chdir('tree_b')
 
332
        self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'])
 
333
        self.failUnlessExists('file1')
 
334
        self.failIfExists('file2')
 
335
 
 
336
    def pullable_branch(self):
 
337
        tree_a = self.make_branch_and_tree('a')
 
338
        self.build_tree(['a/file'])
 
339
        tree_a.add(['file'])
 
340
        self.id1 = tree_a.commit('commit 1')
 
341
 
 
342
        tree_b = self.make_branch_and_tree('b')
 
343
        tree_b.pull(tree_a.branch)
 
344
        file('b/file', 'wb').write('foo')
 
345
        self.id2 = tree_b.commit('commit 2')
 
346
 
 
347
    def test_merge_pull(self):
 
348
        self.pullable_branch()
 
349
        os.chdir('a')
 
350
        (out, err) = self.run_bzr('merge --pull ../b')
 
351
        self.assertContainsRe(out, 'Now on revision 2\\.')
 
352
        tree_a = WorkingTree.open('.')
 
353
        self.assertEqual([self.id2], tree_a.get_parent_ids())
 
354
 
 
355
    def test_merge_kind_change(self):
 
356
        tree_a = self.make_branch_and_tree('tree_a')
 
357
        self.build_tree_contents([('tree_a/file', 'content_1')])
 
358
        tree_a.add('file', 'file-id')
 
359
        tree_a.commit('added file')
 
360
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
 
361
        os.unlink('tree_a/file')
 
362
        self.build_tree(['tree_a/file/'])
 
363
        tree_a.commit('changed file to directory')
 
364
        os.chdir('tree_b')
 
365
        self.run_bzr('merge ../tree_a')
 
366
        self.assertEqual('directory', file_kind('file'))
 
367
        tree_b.revert()
 
368
        self.assertEqual('file', file_kind('file'))
 
369
        self.build_tree_contents([('file', 'content_2')])
 
370
        tree_b.commit('content change')
 
371
        self.run_bzr('merge ../tree_a', retcode=1)
 
372
        self.assertEqual(tree_b.conflicts(),
 
373
                         [ContentsConflict('file', file_id='file-id')])
 
374
 
 
375
    def test_directive_cherrypick(self):
 
376
        source = self.make_branch_and_tree('source')
 
377
        self.build_tree(['source/a'])
 
378
        source.add('a')
 
379
        source.commit('Added a', rev_id='rev1')
 
380
        self.build_tree(['source/b'])
 
381
        source.add('b')
 
382
        source.commit('Added b', rev_id='rev2')
 
383
        target = self.make_branch_and_tree('target')
 
384
        target.commit('empty commit')
 
385
        self.write_directive('directive', source.branch, 'target', 'rev2',
 
386
                             'rev1')
 
387
        out, err = self.run_bzr('merge -d target directive')
 
388
        self.failIfExists('target/a')
 
389
        self.failUnlessExists('target/b')
 
390
        self.assertContainsRe(err, 'Performing cherrypick')
 
391
 
 
392
    def write_directive(self, filename, source, target, revision_id,
 
393
                        base_revision_id=None, mangle_patch=False):
 
394
        md = merge_directive.MergeDirective2.from_objects(
 
395
            source.repository, revision_id, 0, 0, target,
 
396
            base_revision_id=base_revision_id)
 
397
        if mangle_patch:
 
398
            md.patch = 'asdf\n'
 
399
        self.build_tree_contents([(filename, ''.join(md.to_lines()))])
 
400
 
 
401
    def test_directive_verify_warning(self):
 
402
        source = self.make_branch_and_tree('source')
 
403
        self.build_tree(['source/a'])
 
404
        source.add('a')
 
405
        source.commit('Added a', rev_id='rev1')
 
406
        target = self.make_branch_and_tree('target')
 
407
        target.commit('empty commit')
 
408
        self.write_directive('directive', source.branch, 'target', 'rev1')
 
409
        err = self.run_bzr('merge -d target directive')[1]
 
410
        self.assertNotContainsRe(err, 'Preview patch does not match changes')
 
411
        target.revert()
 
412
        self.write_directive('directive', source.branch, 'target', 'rev1',
 
413
                             mangle_patch=True)
 
414
        err = self.run_bzr('merge -d target directive')[1]
 
415
        self.assertContainsRe(err, 'Preview patch does not match changes')
 
416
 
 
417
    def test_merge_arbitrary(self):
 
418
        target = self.make_branch_and_tree('target')
 
419
        target.commit('empty')
 
420
        # We need a revision that has no integer revno
 
421
        branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
 
422
        self.build_tree(['branch_a/file1'])
 
423
        branch_a.add('file1')
 
424
        branch_a.commit('added file1', rev_id='rev2a')
 
425
        branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
 
426
        self.build_tree(['branch_b/file2'])
 
427
        branch_b.add('file2')
 
428
        branch_b.commit('added file2', rev_id='rev2b')
 
429
        branch_b.merge_from_branch(branch_a.branch)
 
430
        self.failUnlessExists('branch_b/file1')
 
431
        branch_b.commit('merged branch_a', rev_id='rev3b')
 
432
 
 
433
        # It works if the revid has an interger revno
 
434
        self.run_bzr('merge -d target -r revid:rev2a branch_a')
 
435
        self.failUnlessExists('target/file1')
 
436
        self.failIfExists('target/file2')
 
437
        target.revert()
 
438
 
 
439
        # It should work if the revid has no integer revno
 
440
        self.run_bzr('merge -d target -r revid:rev2a branch_b')
 
441
        self.failUnlessExists('target/file1')
 
442
        self.failIfExists('target/file2')
 
443
 
 
444
    def assertDirectoryContent(self, directory, entries, message=''):
 
445
        """Assert whether entries (file or directories) exist in a directory.
 
446
 
 
447
        It also checks that there are no extra entries.
 
448
        """
 
449
        ondisk = os.listdir(directory)
 
450
        if set(ondisk) == set(entries):
 
451
            return
 
452
        if message:
 
453
            message += '\n'
 
454
        raise AssertionError(
 
455
            '%s"%s" directory content is different:\na = %s\nb = %s\n'
 
456
            % (message, directory, sorted(entries), sorted(ondisk)))
 
457
 
 
458
    def test_cherrypicking_merge(self):
 
459
        # make source branch
 
460
        source = self.make_branch_and_tree('source')
 
461
        for f in ('a', 'b', 'c', 'd'):
 
462
            self.build_tree(['source/'+f])
 
463
            source.add(f)
 
464
            source.commit('added '+f, rev_id='rev_'+f)
 
465
        # target branch
 
466
        target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
 
467
        self.assertDirectoryContent('target', ['.bzr', 'a'])
 
468
        # pick 1 revision
 
469
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
 
470
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
 
471
        target.revert()
 
472
        # pick 2 revisions
 
473
        self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
 
474
        self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
 
475
        target.revert()
 
476
        # pick 1 revision with option --changes
 
477
        self.run_bzr('merge -d target -c revid:rev_d source')
 
478
        self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
 
479
 
 
480
    def test_merge_criss_cross(self):
 
481
        tree_a = self.make_branch_and_tree('a')
 
482
        tree_a.commit('', rev_id='rev1')
 
483
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
484
        tree_a.commit('', rev_id='rev2a')
 
485
        tree_b.commit('', rev_id='rev2b')
 
486
        tree_a.merge_from_branch(tree_b.branch)
 
487
        tree_b.merge_from_branch(tree_a.branch)
 
488
        tree_a.commit('', rev_id='rev3a')
 
489
        tree_b.commit('', rev_id='rev3b')
 
490
        graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
 
491
        out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
 
492
        self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
 
493
 
 
494
    def test_merge_force(self):
 
495
        tree_a = self.make_branch_and_tree('a')
 
496
        self.build_tree(['a/foo'])
 
497
        tree_a.add(['foo'])
 
498
        tree_a.commit('add file')
 
499
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
500
        self.build_tree_contents([('a/foo', 'change 1')])
 
501
        tree_a.commit('change file')
 
502
        tree_b.merge_from_branch(tree_a.branch)
 
503
        tree_a.commit('empty change to allow merge to run')
 
504
        self.run_bzr(['merge', '../a', '--force'], working_dir='b')
 
505
 
 
506
    def test_merge_from_submit(self):
 
507
        tree_a = self.make_branch_and_tree('a')
 
508
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
509
        tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
 
510
        out, err = self.run_bzr(['merge', '-d', 'c'])
 
511
        self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
 
512
        tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
 
513
        out, err = self.run_bzr(['merge', '-d', 'c'])
 
514
        self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
 
515
 
 
516
    def test_remember_sets_submit(self):
 
517
        tree_a = self.make_branch_and_tree('a')
 
518
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
519
        self.assertIs(tree_b.branch.get_submit_branch(), None)
 
520
 
 
521
        # Remember should not happen if using default from parent
 
522
        out, err = self.run_bzr(['merge', '-d', 'b'])
 
523
        self.assertIs(tree_b.branch.get_submit_branch(), None)
 
524
 
 
525
        # Remember should happen if user supplies location
 
526
        out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
 
527
        self.assertEqual(tree_b.branch.get_submit_branch(),
 
528
                         tree_a.bzrdir.root_transport.base)
 
529
 
 
530
    def test_weave_cherrypick(self):
 
531
        this_tree = self.make_branch_and_tree('this')
 
532
        self.build_tree_contents([('this/file', "a\n")])
 
533
        this_tree.add('file')
 
534
        this_tree.commit('rev1')
 
535
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
 
536
        self.build_tree_contents([('other/file', "a\nb\n")])
 
537
        other_tree.commit('rev2b')
 
538
        self.build_tree_contents([('other/file', "c\na\nb\n")])
 
539
        other_tree.commit('rev3b')
 
540
        self.run_bzr('merge --weave -d this other -r -2..-1')
 
541
        self.assertFileEqual('c\na\n', 'this/file')
 
542
 
 
543
    def test_lca_merge_criss_cross(self):
 
544
        tree_a = self.make_branch_and_tree('a')
 
545
        self.build_tree_contents([('a/file', 'base-contents\n')])
 
546
        tree_a.add('file')
 
547
        tree_a.commit('', rev_id='rev1')
 
548
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
 
549
        self.build_tree_contents([('a/file',
 
550
                                   'base-contents\nthis-contents\n')])
 
551
        tree_a.commit('', rev_id='rev2a')
 
552
        self.build_tree_contents([('b/file',
 
553
                                   'base-contents\nother-contents\n')])
 
554
        tree_b.commit('', rev_id='rev2b')
 
555
        tree_a.merge_from_branch(tree_b.branch)
 
556
        self.build_tree_contents([('a/file',
 
557
                                   'base-contents\nthis-contents\n')])
 
558
        tree_a.set_conflicts(ConflictList())
 
559
        tree_b.merge_from_branch(tree_a.branch)
 
560
        self.build_tree_contents([('b/file',
 
561
                                   'base-contents\nother-contents\n')])
 
562
        tree_b.set_conflicts(ConflictList())
 
563
        tree_a.commit('', rev_id='rev3a')
 
564
        tree_b.commit('', rev_id='rev3b')
 
565
        out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
 
566
        self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
 
567
                             '=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
 
568
                             'a/file')
 
569
 
 
570
    def test_merge_preview(self):
 
571
        this_tree = self.make_branch_and_tree('this')
 
572
        this_tree.commit('rev1')
 
573
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
 
574
        self.build_tree_contents([('other/file', 'new line')])
 
575
        other_tree.add('file')
 
576
        other_tree.commit('rev2a')
 
577
        this_tree.commit('rev2b')
 
578
        out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
 
579
        self.assertContainsRe(out, '\+new line')
 
580
        self.assertNotContainsRe(err, '\+N  file\n')
 
581
        this_tree.lock_read()
 
582
        self.addCleanup(this_tree.unlock)
 
583
        self.assertEqual([],
 
584
                         list(this_tree.iter_changes(this_tree.basis_tree())))