~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Jelmer Vernooij
  • Date: 2011-02-26 15:39:49 UTC
  • mto: (5691.1.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 5692.
  • Revision ID: jelmer@samba.org-20110226153949-o0fk909b30g7z570
Fix the use of "bzr tags" in branches with ghosts in their mainline /and/ tags on revisions not in the branch ancestry.

Show diffs side-by-side

added added

removed removed

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