/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

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