~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Packman
  • Date: 2011-12-23 19:38:22 UTC
  • mto: This revision was merged to the branch mainline in revision 6405.
  • Revision ID: martin.packman@canonical.com-20111223193822-hesheea4o8aqwexv
Accept and document passing the medium rather than transport for smart connections

Show diffs side-by-side

added added

removed removed

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