~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2010-02-03 07:18:36 UTC
  • mto: (5008.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5009.
  • Revision ID: v.ladeuil+lp@free.fr-20100203071836-u9b86q68fr9ri5s6
Fix NEWS.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 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
 
21
21
 
22
22
import os
23
23
 
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')
 
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
43
67
 
44
68
    def test_merge_reprocess(self):
45
 
        d = BzrDir.create_standalone_workingtree('.')
 
69
        d = bzrdir.BzrDir.create_standalone_workingtree('.')
46
70
        d.commit('h')
47
 
        self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
 
71
        self.run_bzr('merge . --reprocess --merge-type weave')
48
72
 
49
73
    def test_merge(self):
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"])
 
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")
60
79
 
61
 
        os.chdir('../a')
62
 
        file('hello', 'wt').write('quuux')
 
80
        self.build_tree_contents([('a/hello', 'quuux')])
63
81
        # We can't merge when there are in-tree changes
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',
 
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',
67
87
                    retcode=3)
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')
 
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')
75
101
        self.check_file_contents('goodbye', 'quux')
76
102
        # Merging a branch pulls its revision into the tree
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(), [])
 
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'))
86
177
 
87
178
    def test_merge_with_missing_file(self):
88
179
        """Merge handles missing file conflicts"""
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'))
 
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')
116
209
        os.chdir('../a')
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'))
 
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')
120
229
 
121
230
    def test_merge_remember(self):
122
 
        """Merge changes from one branch to another and test parent location."""
 
231
        """Merge changes from one branch to another, test submit location."""
123
232
        tree_a = self.make_branch_and_tree('branch_a')
124
233
        branch_a = tree_a.branch
125
234
        self.build_tree(['branch_a/a'])
141
250
        self.assertEqual(None, branch_b.get_parent())
142
251
        # test merge for failure without parent set
143
252
        os.chdir('branch_b')
144
 
        out = self.runbzr('merge', retcode=3)
 
253
        out = self.run_bzr('merge', retcode=3)
145
254
        self.assertEquals(out,
146
255
                ('','bzr: ERROR: No location specified or remembered\n'))
147
 
        # test implicit --remember when no parent set, this merge conflicts
 
256
 
 
257
        # test uncommitted changes
148
258
        self.build_tree(['d'])
149
259
        tree_b.add('d')
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
 
260
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
 
261
                           'merge')
 
262
 
 
263
        # merge should now pass and implicitly remember merge location
155
264
        tree_b.commit('commit d')
156
 
        out, err = self.runbzr('merge')
157
 
        
 
265
        out, err = self.run_bzr('merge ../branch_a')
 
266
 
158
267
        base = urlutils.local_path_from_url(branch_a.base)
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
 
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
163
279
        tree_b = branch_b.bzrdir.open_workingtree()
164
280
        tree_b.commit('merge branch_a')
165
281
        # test explicit --remember
166
 
        out, err = self.runbzr('merge ../branch_c --remember')
 
282
        out, err = self.run_bzr('merge ../branch_c --remember')
167
283
        self.assertEquals(out, '')
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
 
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
172
288
        tree_b = branch_b.bzrdir.open_workingtree()
173
289
        tree_b.commit('merge branch_c')
174
290
 
175
291
    def test_merge_bundle(self):
176
292
        from bzrlib.testament import Testament
177
293
        tree_a = self.make_branch_and_tree('branch_a')
178
 
        f = file('branch_a/a', 'wb')
179
 
        f.write('hello')
180
 
        f.close()
 
294
        self.build_tree_contents([('branch_a/a', 'hello')])
181
295
        tree_a.add('a')
182
296
        tree_a.commit('message')
183
297
 
184
298
        tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
185
 
        f = file('branch_a/a', 'wb')
186
 
        f.write('hey there')
187
 
        f.close()
 
299
        self.build_tree_contents([('branch_a/a', 'hey there')])
188
300
        tree_a.commit('message')
189
301
 
190
 
        f = file('branch_b/a', 'wb')
191
 
        f.write('goodbye')
192
 
        f.close()
 
302
        self.build_tree_contents([('branch_b/a', 'goodbye')])
193
303
        tree_b.commit('message')
194
304
        os.chdir('branch_b')
195
 
        file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
 
305
        self.run_bzr('bundle ../branch_a -o ../bundle')
196
306
        os.chdir('../branch_a')
197
 
        self.runbzr('merge ../bundle', retcode=1)
198
 
        testament_a = Testament.from_revision(tree_a.branch.repository, 
199
 
                                              tree_b.last_revision())
 
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])
200
310
        testament_b = Testament.from_revision(tree_b.branch.repository,
201
 
                                              tree_b.last_revision())
 
311
                                              tree_b.get_parent_ids()[0])
202
312
        self.assertEqualDiff(testament_a.as_text(),
203
313
                         testament_b.as_text())
204
 
        tree_a.set_conflicts(ConflictList())
 
314
        tree_a.set_conflicts(conflicts.ConflictList())
205
315
        tree_a.commit('message')
206
316
        # it is legal to attempt to merge an already-merged bundle
207
 
        output = self.runbzr('merge ../bundle')[1]
 
317
        output = self.run_bzr('merge ../bundle')[1]
208
318
        # but it does nothing
209
319
        self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
210
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')