~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(vila) Make all transport put_bytes() raises TypeError when given unicode
 strings rather than bytes (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

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