~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

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