~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Alexander Belchenko
  • Date: 2006-08-01 18:27:42 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060801182742-b28d2edf7eea75d1
English

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 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
 
19
19
"""Black-box tests for bzr merge.
20
20
"""
21
21
 
22
 
import doctest
23
22
import os
24
23
 
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
 
24
from bzrlib.branch import Branch
 
25
from bzrlib.bzrdir import BzrDir
 
26
from bzrlib.conflicts import ConflictList
 
27
from bzrlib.osutils import abspath
 
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.runbzr('init')
 
37
        file('hello', 'wt').write('foo')
 
38
        test.runbzr('add hello')
 
39
        test.runbzr('commit -m setup hello')
 
40
        file('goodbye', 'wt').write('baz')
 
41
        test.runbzr('add goodbye')
 
42
        test.runbzr('commit -m setup goodbye')
76
43
 
77
44
    def test_merge_reprocess(self):
78
 
        d = bzrdir.BzrDir.create_standalone_workingtree('.')
 
45
        d = BzrDir.create_standalone_workingtree('.')
79
46
        d.commit('h')
80
 
        self.run_bzr('merge . --reprocess --merge-type weave')
 
47
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
81
48
 
82
49
    def test_merge(self):
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")
 
50
        from bzrlib.branch import Branch
 
51
        
 
52
        os.mkdir('a')
 
53
        os.chdir('a')
 
54
        self.example_branch()
 
55
        os.chdir('..')
 
56
        self.runbzr('branch a b')
 
57
        os.chdir('b')
 
58
        file('goodbye', 'wt').write('quux')
 
59
        self.runbzr(['commit',  '-m',  "more u's are always good"])
88
60
 
89
 
        self.build_tree_contents([('a/hello', 'quuux')])
 
61
        os.chdir('../a')
 
62
        file('hello', 'wt').write('quuux')
90
63
        # We can't merge when there are in-tree changes
91
 
        os.chdir('a')
92
 
        self.run_bzr('merge ../b', retcode=3)
93
 
        a = workingtree.WorkingTree.open('.')
94
 
        a_tip = a.commit("Like an epidemic of u's")
95
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
 
64
        self.runbzr('merge ../b', retcode=3)
 
65
        self.runbzr(['commit', '-m', "Like an epidemic of u's"])
 
66
        self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
96
67
                    retcode=3)
97
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
98
 
        a_tree.revert(backups=False)
99
 
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
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)
107
 
        self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
108
 
        a_tree.revert(backups=False)
109
 
        self.run_bzr('merge ../b -r last:1')
 
68
        self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
 
69
        self.runbzr('revert --no-backup')
 
70
        self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
 
71
        self.runbzr('revert --no-backup')
 
72
        self.runbzr('merge ../b -r last:1..last:1 --reprocess')
 
73
        self.runbzr('revert --no-backup')
 
74
        self.runbzr('merge ../b -r last:1')
110
75
        self.check_file_contents('goodbye', 'quux')
111
76
        # Merging a branch pulls its revision into the tree
112
 
        b = branch.Branch.open('../b')
113
 
        b_tip = b.last_revision()
114
 
        self.assertTrue(a.branch.repository.has_revision(b_tip))
115
 
        self.assertEqual([a_tip, b_tip], a.get_parent_ids())
116
 
        a_tree.revert(backups=False)
117
 
        out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
118
 
        self.assertTrue("Not a branch" in err)
119
 
        self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
120
 
                    %(ancestor,b.revno()))
121
 
        self.assertEquals(a.get_parent_ids(),
122
 
                          [a.branch.last_revision(), b.last_revision()])
123
 
        self.check_file_contents('goodbye', 'quux')
124
 
        a_tree.revert(backups=False)
125
 
        self.run_bzr('merge -r revno:%d:../b'%b.revno())
126
 
        self.assertEquals(a.get_parent_ids(),
127
 
                          [a.branch.last_revision(), b.last_revision()])
128
 
        a_tip = a.commit('merged')
129
 
        self.run_bzr('merge ../b -r last:1')
130
 
        self.assertEqual([a_tip], a.get_parent_ids())
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'))
 
77
        a = WorkingTree.open('.')
 
78
        b = Branch.open('../b')
 
79
        a.branch.repository.get_revision_xml(b.last_revision())
 
80
        self.log('pending merges: %s', a.pending_merges())
 
81
        self.assertEquals(a.pending_merges(),
 
82
                          [b.last_revision()])
 
83
        self.runbzr('commit -m merged')
 
84
        self.runbzr('merge ../b -r last:1')
 
85
        self.assertEqual(a.pending_merges(), [])
186
86
 
187
87
    def test_merge_with_missing_file(self):
188
88
        """Merge handles missing file conflicts"""
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')
215
 
        self.run_bzr('merge ../a/', retcode=1)
216
 
        self.assertPathExists('sub/a.txt.THIS')
217
 
        self.assertPathExists('sub/a.txt.BASE')
 
89
        os.mkdir('a')
 
90
        os.chdir('a')
 
91
        os.mkdir('sub')
 
92
        print >> file('sub/a.txt', 'wb'), "hello"
 
93
        print >> file('b.txt', 'wb'), "hello"
 
94
        print >> file('sub/c.txt', 'wb'), "hello"
 
95
        self.runbzr('init')
 
96
        self.runbzr('add')
 
97
        self.runbzr(('commit', '-m', 'added a'))
 
98
        self.runbzr('branch . ../b')
 
99
        print >> file('sub/a.txt', 'ab'), "there"
 
100
        print >> file('b.txt', 'ab'), "there"
 
101
        print >> file('sub/c.txt', 'ab'), "there"
 
102
        self.runbzr(('commit', '-m', 'Added there'))
 
103
        os.unlink('sub/a.txt')
 
104
        os.unlink('sub/c.txt')
 
105
        os.rmdir('sub')
 
106
        os.unlink('b.txt')
 
107
        self.runbzr(('commit', '-m', 'Removed a.txt'))
 
108
        os.chdir('../b')
 
109
        print >> file('sub/a.txt', 'ab'), "something"
 
110
        print >> file('b.txt', 'ab'), "something"
 
111
        print >> file('sub/c.txt', 'ab'), "something"
 
112
        self.runbzr(('commit', '-m', 'Modified a.txt'))
 
113
        self.runbzr('merge ../a/', retcode=1)
 
114
        self.assert_(os.path.exists('sub/a.txt.THIS'))
 
115
        self.assert_(os.path.exists('sub/a.txt.BASE'))
218
116
        os.chdir('../a')
219
 
        self.run_bzr('merge ../b/', retcode=1)
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')
 
117
        self.runbzr('merge ../b/', retcode=1)
 
118
        self.assert_(os.path.exists('sub/a.txt.OTHER'))
 
119
        self.assert_(os.path.exists('sub/a.txt.BASE'))
238
120
 
239
121
    def test_merge_remember(self):
240
 
        """Merge changes from one branch to another, test submit location."""
 
122
        """Merge changes from one branch to another and test parent location."""
241
123
        tree_a = self.make_branch_and_tree('branch_a')
242
124
        branch_a = tree_a.branch
243
125
        self.build_tree(['branch_a/a'])
259
141
        self.assertEqual(None, branch_b.get_parent())
260
142
        # test merge for failure without parent set
261
143
        os.chdir('branch_b')
262
 
        out = self.run_bzr('merge', retcode=3)
 
144
        out = self.runbzr('merge', retcode=3)
263
145
        self.assertEquals(out,
264
146
                ('','bzr: ERROR: No location specified or remembered\n'))
265
 
 
266
 
        # test uncommitted changes
 
147
        # test implicit --remember when no parent set, this merge conflicts
267
148
        self.build_tree(['d'])
268
149
        tree_b.add('d')
269
 
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
270
 
                           'merge')
271
 
 
272
 
        # merge should now pass and implicitly remember merge location
 
150
        out = self.runbzr('merge ../branch_a', retcode=3)
 
151
        self.assertEquals(out,
 
152
                ('','bzr: ERROR: Working tree has uncommitted changes.\n'))
 
153
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
 
154
        # test implicit --remember after resolving conflict
273
155
        tree_b.commit('commit d')
274
 
        out, err = self.run_bzr('merge ../branch_a')
275
 
 
 
156
        out, err = self.runbzr('merge')
 
157
        
276
158
        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')
284
 
        out, err = self.run_bzr('merge')
285
 
        self.assertStartsWith(err,
286
 
                          'Merging from remembered submit location %s\n' % (base,))
287
 
        # re-open tree as external run_bzr modified it
 
159
        self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
 
160
        self.assertEquals(err, 'All changes applied successfully.\n')
 
161
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
 
162
        # re-open tree as external runbzr modified it
288
163
        tree_b = branch_b.bzrdir.open_workingtree()
289
164
        tree_b.commit('merge branch_a')
290
165
        # test explicit --remember
291
 
        out, err = self.run_bzr('merge ../branch_c --remember')
 
166
        out, err = self.runbzr('merge ../branch_c --remember')
292
167
        self.assertEquals(out, '')
293
 
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
294
 
        self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
295
 
                          osutils.abspath(branch_c.bzrdir.root_transport.base))
296
 
        # re-open tree as external run_bzr modified it
 
168
        self.assertEquals(err, 'All changes applied successfully.\n')
 
169
        self.assertEquals(abspath(branch_b.get_parent()),
 
170
                          abspath(branch_c.bzrdir.root_transport.base))
 
171
        # re-open tree as external runbzr modified it
297
172
        tree_b = branch_b.bzrdir.open_workingtree()
298
173
        tree_b.commit('merge branch_c')
299
174
 
300
175
    def test_merge_bundle(self):
301
176
        from bzrlib.testament import Testament
302
177
        tree_a = self.make_branch_and_tree('branch_a')
303
 
        self.build_tree_contents([('branch_a/a', 'hello')])
 
178
        f = file('branch_a/a', 'wb')
 
179
        f.write('hello')
 
180
        f.close()
304
181
        tree_a.add('a')
305
182
        tree_a.commit('message')
306
183
 
307
184
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
308
 
        self.build_tree_contents([('branch_a/a', 'hey there')])
 
185
        f = file('branch_a/a', 'wb')
 
186
        f.write('hey there')
 
187
        f.close()
309
188
        tree_a.commit('message')
310
189
 
311
 
        self.build_tree_contents([('branch_b/a', 'goodbye')])
 
190
        f = file('branch_b/a', 'wb')
 
191
        f.write('goodbye')
 
192
        f.close()
312
193
        tree_b.commit('message')
313
194
        os.chdir('branch_b')
314
 
        self.run_bzr('bundle ../branch_a -o ../bundle')
 
195
        file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
315
196
        os.chdir('../branch_a')
316
 
        self.run_bzr('merge ../bundle', retcode=1)
317
 
        testament_a = Testament.from_revision(tree_a.branch.repository,
318
 
                                              tree_b.get_parent_ids()[0])
 
197
        self.runbzr('merge ../bundle', retcode=1)
 
198
        testament_a = Testament.from_revision(tree_a.branch.repository, 
 
199
                                              tree_b.last_revision())
319
200
        testament_b = Testament.from_revision(tree_b.branch.repository,
320
 
                                              tree_b.get_parent_ids()[0])
 
201
                                              tree_b.last_revision())
321
202
        self.assertEqualDiff(testament_a.as_text(),
322
203
                         testament_b.as_text())
323
 
        tree_a.set_conflicts(conflicts.ConflictList())
 
204
        tree_a.set_conflicts(ConflictList())
324
205
        tree_a.commit('message')
325
206
        # it is legal to attempt to merge an already-merged bundle
326
 
        output = self.run_bzr('merge ../bundle')[1]
 
207
        output = self.runbzr('merge ../bundle')[1]
327
208
        # but it does nothing
328
209
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
329
210
        self.assertEqual('Nothing to do.\n', output)
330
 
 
331
 
    def test_merge_uncommitted(self):
332
 
        """Check that merge --uncommitted behaves properly"""
333
 
        tree_a = self.make_branch_and_tree('a')
334
 
        self.build_tree(['a/file_1', 'a/file_2'])
335
 
        tree_a.add(['file_1', 'file_2'])
336
 
        tree_a.commit('commit 1')
337
 
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
338
 
        self.assertPathExists('b/file_1')
339
 
        tree_a.rename_one('file_1', 'file_i')
340
 
        tree_a.commit('commit 2')
341
 
        tree_a.rename_one('file_2', 'file_ii')
342
 
        ## os.chdir('b')
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()
347
 
        self.run_bzr_error(('Cannot use --uncommitted and --revision',),
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'])
372
 
 
373
 
    def pullable_branch(self):
374
 
        tree_a = self.make_branch_and_tree('a')
375
 
        self.build_tree_contents([('a/file', 'bar\n')])
376
 
        tree_a.add(['file'])
377
 
        self.id1 = tree_a.commit('commit 1')
378
 
 
379
 
        tree_b = self.make_branch_and_tree('b')
380
 
        tree_b.pull(tree_a.branch)
381
 
        self.build_tree_contents([('b/file', 'foo\n')])
382
 
        self.id2 = tree_b.commit('commit 2')
383
 
 
384
 
    def test_merge_pull(self):
385
 
        self.pullable_branch()
386
 
        os.chdir('a')
387
 
        (out, err) = self.run_bzr('merge --pull ../b')
388
 
        self.assertContainsRe(out, 'Now on revision 2\\.')
389
 
        tree_a = workingtree.WorkingTree.open('.')
390
 
        self.assertEqual([self.id2], tree_a.get_parent_ids())
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
 
 
407
 
    def test_merge_kind_change(self):
408
 
        tree_a = self.make_branch_and_tree('tree_a')
409
 
        self.build_tree_contents([('tree_a/file', 'content_1')])
410
 
        tree_a.add('file', 'file-id')
411
 
        tree_a.commit('added file')
412
 
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
413
 
        os.unlink('tree_a/file')
414
 
        self.build_tree(['tree_a/file/'])
415
 
        tree_a.commit('changed file to directory')
416
 
        os.chdir('tree_b')
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'))
421
 
        self.build_tree_contents([('file', 'content_2')])
422
 
        tree_b.commit('content change')
423
 
        self.run_bzr('merge ../tree_a', retcode=1)
424
 
        self.assertEqual(tree_b.conflicts(),
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
 
        builder.build_commit(message="Rev 2b", rev_id='rev-2b')
685
 
        # Merge from source
686
 
        self.run_bzr('merge -d target source')
687
 
        target = target_bzrdir.open_branch()
688
 
        # The tag is present, and so is its revision.
689
 
        self.assertEqual('rev-2a', target.tags.lookup_tag('tag-a'))
690
 
        target.repository.get_revision('rev-2a')
691
 
 
692
 
 
693
 
class TestMergeRevisionRange(tests.TestCaseWithTransport):
694
 
 
695
 
    scenarios = (('whole-tree', dict(context='.')),
696
 
                 ('file-only', dict(context='a')))
697
 
 
698
 
    def setUp(self):
699
 
        super(TestMergeRevisionRange, self).setUp()
700
 
        self.tree = self.make_branch_and_tree(".")
701
 
        self.tree.commit('initial commit')
702
 
        for f in ("a", "b"):
703
 
            self.build_tree([f])
704
 
            self.tree.add(f)
705
 
            self.tree.commit("added " + f)
706
 
 
707
 
    def test_merge_reversed_revision_range(self):
708
 
        self.run_bzr("merge -r 2..1 " + self.context)
709
 
        self.assertPathDoesNotExist("a")
710
 
        self.assertPathExists("b")
711
 
 
712
 
 
713
 
class TestMergeScript(script.TestCaseWithTransportAndScript):
714
 
    def test_merge_empty_branch(self):
715
 
        source = self.make_branch_and_tree('source')
716
 
        self.build_tree(['source/a'])
717
 
        source.add('a')
718
 
        source.commit('Added a', rev_id='rev1')
719
 
        target = self.make_branch_and_tree('target')
720
 
        self.run_script("""\
721
 
$ bzr merge -d target source
722
 
2>bzr: ERROR: Merging into empty branches not currently supported, https://bugs.launchpad.net/bzr/+bug/308562
723
 
""")
724
 
 
725
 
class TestMergeForce(tests.TestCaseWithTransport):
726
 
 
727
 
    def setUp(self):
728
 
        super(TestMergeForce, self).setUp()
729
 
        self.tree_a = self.make_branch_and_tree('a')
730
 
        self.build_tree(['a/foo'])
731
 
        self.tree_a.add(['foo'])
732
 
        self.tree_a.commit('add file')
733
 
        self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
734
 
        self.build_tree_contents([('a/foo', 'change 1')])
735
 
        self.tree_a.commit('change file')
736
 
        self.tree_b.merge_from_branch(self.tree_a.branch)
737
 
 
738
 
    def test_merge_force(self):
739
 
        self.tree_a.commit('empty change to allow merge to run')
740
 
        # Second merge on top of the uncommitted one
741
 
        self.run_bzr(['merge', '../a', '--force'], working_dir='b')
742
 
 
743
 
 
744
 
    def test_merge_with_uncommitted_changes(self):
745
 
        self.run_bzr_error(['Working tree .* has uncommitted changes'],
746
 
                           ['merge', '../a'], working_dir='b')
747
 
 
748
 
    def test_merge_with_pending_merges(self):
749
 
        # Revert the changes keeping the pending merge
750
 
        self.run_bzr(['revert', 'b'])
751
 
        self.run_bzr_error(['Working tree .* has uncommitted changes'],
752
 
                           ['merge', '../a'], working_dir='b')