~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 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
27
 
from bzrlib.delta import compare_trees
28
 
from bzrlib.osutils import abspath
29
 
from bzrlib.tests.blackbox import ExternalBase
30
 
import bzrlib.urlutils as urlutils
31
 
from bzrlib.workingtree import WorkingTree
32
 
 
33
 
 
34
 
class TestMerge(ExternalBase):
35
 
 
36
 
    def example_branch(test):
37
 
        test.runbzr('init')
38
 
        file('hello', 'wt').write('foo')
39
 
        test.runbzr('add hello')
40
 
        test.runbzr('commit -m setup hello')
41
 
        file('goodbye', 'wt').write('baz')
42
 
        test.runbzr('add goodbye')
43
 
        test.runbzr('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
44
76
 
45
77
    def test_merge_reprocess(self):
46
 
        d = BzrDir.create_standalone_workingtree('.')
 
78
        d = bzrdir.BzrDir.create_standalone_workingtree('.')
47
79
        d.commit('h')
48
 
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
 
80
        self.run_bzr('merge . --reprocess --merge-type weave')
49
81
 
50
82
    def test_merge(self):
51
 
        from bzrlib.branch import Branch
52
 
        
53
 
        os.mkdir('a')
54
 
        os.chdir('a')
55
 
        self.example_branch()
56
 
        os.chdir('..')
57
 
        self.runbzr('branch a b')
58
 
        os.chdir('b')
59
 
        file('goodbye', 'wt').write('quux')
60
 
        self.runbzr(['commit',  '-m',  "more u's are always good"])
 
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
65
 
        self.runbzr('merge ../b', retcode=3)
66
 
        self.runbzr(['commit', '-m', "Like an epidemic of u's"])
67
 
        self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
 
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',
68
96
                    retcode=3)
69
 
        self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
70
 
        self.runbzr('revert --no-backup')
71
 
        self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
72
 
        self.runbzr('revert --no-backup')
73
 
        self.runbzr('merge ../b -r last:1..last:1 --reprocess')
74
 
        self.runbzr('revert --no-backup')
75
 
        self.runbzr('merge ../b -r last:1')
 
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')
76
110
        self.check_file_contents('goodbye', 'quux')
77
111
        # Merging a branch pulls its revision into the tree
78
 
        a = WorkingTree.open('.')
79
 
        b = Branch.open('../b')
80
 
        a.branch.repository.get_revision_xml(b.last_revision())
81
 
        self.log('pending merges: %s', a.pending_merges())
82
 
        self.assertEquals(a.pending_merges(),
83
 
                          [b.last_revision()])
84
 
        self.runbzr('commit -m merged')
85
 
        self.runbzr('merge ../b -r last:1')
86
 
        self.assertEqual(a.pending_merges(), [])
 
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'))
87
186
 
88
187
    def test_merge_with_missing_file(self):
89
188
        """Merge handles missing file conflicts"""
90
 
        os.mkdir('a')
91
 
        os.chdir('a')
92
 
        os.mkdir('sub')
93
 
        print >> file('sub/a.txt', 'wb'), "hello"
94
 
        print >> file('b.txt', 'wb'), "hello"
95
 
        print >> file('sub/c.txt', 'wb'), "hello"
96
 
        self.runbzr('init')
97
 
        self.runbzr('add')
98
 
        self.runbzr(('commit', '-m', 'added a'))
99
 
        self.runbzr('branch . ../b')
100
 
        print >> file('sub/a.txt', 'ab'), "there"
101
 
        print >> file('b.txt', 'ab'), "there"
102
 
        print >> file('sub/c.txt', 'ab'), "there"
103
 
        self.runbzr(('commit', '-m', 'Added there'))
104
 
        os.unlink('sub/a.txt')
105
 
        os.unlink('sub/c.txt')
106
 
        os.rmdir('sub')
107
 
        os.unlink('b.txt')
108
 
        self.runbzr(('commit', '-m', 'Removed a.txt'))
109
 
        os.chdir('../b')
110
 
        print >> file('sub/a.txt', 'ab'), "something"
111
 
        print >> file('b.txt', 'ab'), "something"
112
 
        print >> file('sub/c.txt', 'ab'), "something"
113
 
        self.runbzr(('commit', '-m', 'Modified a.txt'))
114
 
        self.runbzr('merge ../a/', retcode=1)
115
 
        self.assert_(os.path.exists('sub/a.txt.THIS'))
116
 
        self.assert_(os.path.exists('sub/a.txt.BASE'))
 
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')
117
218
        os.chdir('../a')
118
 
        self.runbzr('merge ../b/', retcode=1)
119
 
        self.assert_(os.path.exists('sub/a.txt.OTHER'))
120
 
        self.assert_(os.path.exists('sub/a.txt.BASE'))
 
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')
121
238
 
122
239
    def test_merge_remember(self):
123
 
        """Merge changes from one branch to another and test parent location."""
 
240
        """Merge changes from one branch to another, test submit location."""
124
241
        tree_a = self.make_branch_and_tree('branch_a')
125
242
        branch_a = tree_a.branch
126
243
        self.build_tree(['branch_a/a'])
142
259
        self.assertEqual(None, branch_b.get_parent())
143
260
        # test merge for failure without parent set
144
261
        os.chdir('branch_b')
145
 
        out = self.runbzr('merge', retcode=3)
 
262
        out = self.run_bzr('merge', retcode=3)
146
263
        self.assertEquals(out,
147
264
                ('','bzr: ERROR: No location specified or remembered\n'))
148
 
        # test implicit --remember when no parent set, this merge conflicts
 
265
 
 
266
        # test uncommitted changes
149
267
        self.build_tree(['d'])
150
268
        tree_b.add('d')
151
 
        out = self.runbzr('merge ../branch_a', retcode=3)
152
 
        self.assertEquals(out,
153
 
                ('','bzr: ERROR: Working tree has uncommitted changes.\n'))
154
 
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
155
 
        # 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
156
273
        tree_b.commit('commit d')
157
 
        out, err = self.runbzr('merge')
158
 
        
 
274
        out, err = self.run_bzr('merge ../branch_a')
 
275
 
159
276
        base = urlutils.local_path_from_url(branch_a.base)
160
 
        self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
161
 
        self.assertEquals(err, 'All changes applied successfully.\n')
162
 
        self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
163
 
        # re-open tree as external runbzr modified it
 
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
164
288
        tree_b = branch_b.bzrdir.open_workingtree()
165
289
        tree_b.commit('merge branch_a')
166
290
        # test explicit --remember
167
 
        out, err = self.runbzr('merge ../branch_c --remember')
 
291
        out, err = self.run_bzr('merge ../branch_c --remember')
168
292
        self.assertEquals(out, '')
169
 
        self.assertEquals(err, 'All changes applied successfully.\n')
170
 
        self.assertEquals(abspath(branch_b.get_parent()),
171
 
                          abspath(branch_c.bzrdir.root_transport.base))
172
 
        # re-open tree as external runbzr modified it
 
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
173
297
        tree_b = branch_b.bzrdir.open_workingtree()
174
298
        tree_b.commit('merge branch_c')
175
299
 
176
300
    def test_merge_bundle(self):
177
301
        from bzrlib.testament import Testament
178
302
        tree_a = self.make_branch_and_tree('branch_a')
179
 
        f = file('branch_a/a', 'wb')
180
 
        f.write('hello')
181
 
        f.close()
 
303
        self.build_tree_contents([('branch_a/a', 'hello')])
182
304
        tree_a.add('a')
183
305
        tree_a.commit('message')
184
306
 
185
307
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
186
 
        f = file('branch_a/a', 'wb')
187
 
        f.write('hey there')
188
 
        f.close()
 
308
        self.build_tree_contents([('branch_a/a', 'hey there')])
189
309
        tree_a.commit('message')
190
310
 
191
 
        f = file('branch_b/a', 'wb')
192
 
        f.write('goodbye')
193
 
        f.close()
 
311
        self.build_tree_contents([('branch_b/a', 'goodbye')])
194
312
        tree_b.commit('message')
195
313
        os.chdir('branch_b')
196
 
        file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
 
314
        self.run_bzr('bundle ../branch_a -o ../bundle')
197
315
        os.chdir('../branch_a')
198
 
        self.runbzr('merge ../bundle', retcode=1)
199
 
        testament_a = Testament.from_revision(tree_a.branch.repository, 
200
 
                                              tree_b.last_revision())
 
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])
201
319
        testament_b = Testament.from_revision(tree_b.branch.repository,
202
 
                                              tree_b.last_revision())
 
320
                                              tree_b.get_parent_ids()[0])
203
321
        self.assertEqualDiff(testament_a.as_text(),
204
322
                         testament_b.as_text())
205
 
        tree_a.set_conflicts(ConflictList())
 
323
        tree_a.set_conflicts(conflicts.ConflictList())
206
324
        tree_a.commit('message')
207
325
        # it is legal to attempt to merge an already-merged bundle
208
 
        output = self.runbzr('merge ../bundle')[1]
 
326
        output = self.run_bzr('merge ../bundle')[1]
209
327
        # but it does nothing
210
 
        self.assertFalse(compare_trees(tree_a.basis_tree(), 
211
 
                                       tree_a).has_changed())
 
328
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
212
329
        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
        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')