~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-05-19 06:14:38 UTC
  • mfrom: (1704.2.23 bzr.mbp.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060519061438-6300caf3926c3cff
(mbp) small fixes

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