/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 breezy/tests/test_merge.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
import contextlib
18
17
import os
19
18
 
20
19
from .. import (
21
20
    branch as _mod_branch,
 
21
    cleanup,
22
22
    conflicts,
23
23
    errors,
24
24
    memorytree,
33
33
    knit,
34
34
    versionedfile,
35
35
    )
36
 
from ..conflicts import ConflictList
37
 
from ..bzr.conflicts import TextConflict, MissingParent, UnversionedParent, DeletingParent, ContentsConflict
 
36
from ..conflicts import ConflictList, TextConflict
38
37
from ..errors import UnrelatedBranches, NoCommits
39
38
from ..merge import transform_tree, merge_inner, _PlanMerge
40
39
from ..osutils import basename, pathjoin, file_kind
85
84
        tip = wt1.commit('empty commit')
86
85
        wt2 = self.make_branch_and_tree('branch2')
87
86
        wt2.pull(wt1.branch)
88
 
        with open('branch1/foo', 'wb') as f:
89
 
            f.write(b'foo')
90
 
        with open('branch1/bar', 'wb') as f:
91
 
            f.write(b'bar')
 
87
        with file('branch1/foo', 'wb') as f:
 
88
            f.write('foo')
 
89
        with file('branch1/bar', 'wb') as f:
 
90
            f.write('bar')
92
91
        wt1.add('foo')
93
92
        wt1.add('bar')
94
93
        wt1.commit('add foobar')
107
106
        br1.fetch(wt2.branch)
108
107
        # merge all of branch 2 into branch 1 even though they
109
108
        # are not related.
110
 
        wt1.merge_from_branch(wt2.branch, wt2.last_revision(), b'null:')
 
109
        wt1.merge_from_branch(wt2.branch, wt2.last_revision(), 'null:')
111
110
        self.assertEqual([br1.last_revision(), wt2.branch.last_revision()],
112
 
                         wt1.get_parent_ids())
 
111
            wt1.get_parent_ids())
113
112
        return (wt1, wt2.branch)
114
113
 
115
114
    def test_two_roots(self):
135
134
                                         this_branch=wt.branch,
136
135
                                         do_merge=False)
137
136
        with merger.make_preview_transform() as tt:
138
 
            self.assertEqual([], tt.find_raw_conflicts())
 
137
            self.assertEqual([], tt.find_conflicts())
139
138
            preview = tt.get_preview_tree()
140
 
            self.assertEqual(wt.path2id(''), preview.path2id(''))
 
139
            self.assertEqual(wt.get_root_id(), preview.get_root_id())
141
140
 
142
141
    def test_merge_unrelated_retains_root(self):
143
142
        wt = self.make_branch_and_tree('tree')
146
145
        merger = _mod_merge.Merge3Merger(wt, wt, wt.basis_tree(), other_tree,
147
146
                                         this_branch=wt.branch,
148
147
                                         do_merge=False)
149
 
        with wt.preview_transform() as merger.tt:
 
148
        with transform.TransformPreview(wt) as merger.tt:
150
149
            merger._compute_transform()
151
150
            new_root_id = merger.tt.final_file_id(merger.tt.root)
152
 
            self.assertEqual(wt.path2id(''), new_root_id)
 
151
            self.assertEqual(wt.get_root_id(), new_root_id)
153
152
 
154
153
    def test_create_rename(self):
155
154
        """Rename an inventory entry while creating the file"""
156
 
        tree = self.make_branch_and_tree('.')
157
 
        with open('name1', 'wb') as f:
158
 
            f.write(b'Hello')
 
155
        tree =self.make_branch_and_tree('.')
 
156
        with file('name1', 'wb') as f: f.write('Hello')
159
157
        tree.add('name1')
160
158
        tree.commit(message="hello")
161
159
        tree.rename_one('name1', 'name2')
164
162
 
165
163
    def test_layered_rename(self):
166
164
        """Rename both child and parent at same time"""
167
 
        tree = self.make_branch_and_tree('.')
 
165
        tree =self.make_branch_and_tree('.')
168
166
        os.mkdir('dirname1')
169
167
        tree.add('dirname1')
170
168
        filename = pathjoin('dirname1', 'name1')
171
 
        with open(filename, 'wb') as f:
172
 
            f.write(b'Hello')
 
169
        with file(filename, 'wb') as f: f.write('Hello')
173
170
        tree.add(filename)
174
171
        tree.commit(message="hello")
175
172
        filename2 = pathjoin('dirname1', 'name2')
179
176
 
180
177
    def test_ignore_zero_merge_inner(self):
181
178
        # Test that merge_inner's ignore zero parameter is effective
182
 
        tree_a = self.make_branch_and_tree('a')
 
179
        tree_a =self.make_branch_and_tree('a')
183
180
        tree_a.commit(message="hello")
184
181
        dir_b = tree_a.controldir.sprout('b')
185
182
        tree_b = dir_b.open_workingtree()
189
186
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
190
187
                    this_tree=tree_b, ignore_zero=True)
191
188
        self.assertTrue('All changes applied successfully.\n' not in
192
 
                        self.get_log())
 
189
            self.get_log())
193
190
        tree_b.revert()
194
191
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
195
192
                    this_tree=tree_b, ignore_zero=False)
196
 
        self.assertTrue(
197
 
            'All changes applied successfully.\n' in self.get_log())
 
193
        self.assertTrue('All changes applied successfully.\n' in self.get_log())
198
194
 
199
195
    def test_merge_inner_conflicts(self):
200
196
        tree_a = self.make_branch_and_tree('a')
201
 
        tree_a.set_conflicts([TextConflict('patha')])
 
197
        tree_a.set_conflicts(ConflictList([TextConflict('patha')]))
202
198
        merge_inner(tree_a.branch, tree_a, tree_a, this_tree=tree_a)
203
199
        self.assertEqual(1, len(tree_a.conflicts()))
204
200
 
205
201
    def test_rmdir_conflict(self):
206
202
        tree_a = self.make_branch_and_tree('a')
207
203
        self.build_tree(['a/b/'])
208
 
        tree_a.add('b', b'b-id')
 
204
        tree_a.add('b', 'b-id')
209
205
        tree_a.commit('added b')
210
206
        # basis_tree() is only guaranteed to be valid as long as it is actually
211
207
        # the basis tree. This mutates the tree after grabbing basis, so go to
212
208
        # the repository.
213
 
        base_tree = tree_a.branch.repository.revision_tree(
214
 
            tree_a.last_revision())
 
209
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
215
210
        tree_z = tree_a.controldir.sprout('z').open_workingtree()
216
211
        self.build_tree(['a/b/c'])
217
212
        tree_a.add('b/c')
220
215
        tree_z.commit('removed b')
221
216
        merge_inner(tree_z.branch, tree_a, base_tree, this_tree=tree_z)
222
217
        self.assertEqual([
223
 
            MissingParent('Created directory', 'b', b'b-id'),
224
 
            UnversionedParent('Versioned directory', 'b', b'b-id')],
 
218
            conflicts.MissingParent('Created directory', 'b', 'b-id'),
 
219
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
225
220
            tree_z.conflicts())
226
221
        merge_inner(tree_a.branch, tree_z.basis_tree(), base_tree,
227
222
                    this_tree=tree_a)
228
223
        self.assertEqual([
229
 
            DeletingParent('Not deleting', 'b', b'b-id'),
230
 
            UnversionedParent('Versioned directory', 'b', b'b-id')],
 
224
            conflicts.DeletingParent('Not deleting', 'b', 'b-id'),
 
225
            conflicts.UnversionedParent('Versioned directory', 'b', 'b-id')],
231
226
            tree_a.conflicts())
232
227
 
233
228
    def test_nested_merge(self):
234
229
        self.knownFailure(
235
230
            'iter_changes doesn\'t work with changes in nested trees')
236
231
        tree = self.make_branch_and_tree('tree',
237
 
                                         format='development-subtree')
 
232
            format='development-subtree')
238
233
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
239
 
                                             format='development-subtree')
240
 
        sub_tree.set_root_id(b'sub-tree-root')
241
 
        self.build_tree_contents([('tree/sub-tree/file', b'text1')])
 
234
            format='development-subtree')
 
235
        sub_tree.set_root_id('sub-tree-root')
 
236
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
242
237
        sub_tree.add('file')
243
238
        sub_tree.commit('foo')
244
239
        tree.add_reference(sub_tree)
245
240
        tree.commit('set text to 1')
246
241
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
247
242
        # modify the file in the subtree
248
 
        self.build_tree_contents([('tree2/sub-tree/file', b'text2')])
 
243
        self.build_tree_contents([('tree2/sub-tree/file', 'text2')])
249
244
        # and merge the changes from the diverged subtree into the containing
250
245
        # tree
251
246
        tree2.commit('changed file text')
252
247
        tree.merge_from_branch(tree2.branch)
253
 
        self.assertFileEqual(b'text2', 'tree/sub-tree/file')
 
248
        self.assertFileEqual('text2', 'tree/sub-tree/file')
254
249
 
255
250
    def test_merge_with_missing(self):
256
251
        tree_a = self.make_branch_and_tree('tree_a')
257
 
        self.build_tree_contents([('tree_a/file', b'content_1')])
 
252
        self.build_tree_contents([('tree_a/file', 'content_1')])
258
253
        tree_a.add('file')
259
254
        tree_a.commit('commit base')
260
255
        # basis_tree() is only guaranteed to be valid as long as it is actually
261
256
        # the basis tree. This test commits to the tree after grabbing basis,
262
257
        # so we go to the repository.
263
 
        base_tree = tree_a.branch.repository.revision_tree(
264
 
            tree_a.last_revision())
 
258
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
265
259
        tree_b = tree_a.controldir.sprout('tree_b').open_workingtree()
266
 
        self.build_tree_contents([('tree_a/file', b'content_2')])
 
260
        self.build_tree_contents([('tree_a/file', 'content_2')])
267
261
        tree_a.commit('commit other')
268
262
        other_tree = tree_a.basis_tree()
269
263
        # 'file' is now missing but isn't altered in any commit in b so no
273
267
 
274
268
    def test_merge_kind_change(self):
275
269
        tree_a = self.make_branch_and_tree('tree_a')
276
 
        self.build_tree_contents([('tree_a/file', b'content_1')])
277
 
        tree_a.add('file', b'file-id')
 
270
        self.build_tree_contents([('tree_a/file', 'content_1')])
 
271
        tree_a.add('file', 'file-id')
278
272
        tree_a.commit('added file')
279
273
        tree_b = tree_a.controldir.sprout('tree_b').open_workingtree()
280
274
        os.unlink('tree_a/file')
284
278
        self.assertEqual('directory', file_kind('tree_b/file'))
285
279
        tree_b.revert()
286
280
        self.assertEqual('file', file_kind('tree_b/file'))
287
 
        self.build_tree_contents([('tree_b/file', b'content_2')])
 
281
        self.build_tree_contents([('tree_b/file', 'content_2')])
288
282
        tree_b.commit('content change')
289
283
        tree_b.merge_from_branch(tree_a.branch)
290
284
        self.assertEqual(tree_b.conflicts(),
291
 
                         [ContentsConflict('file', file_id=b'file-id')])
 
285
                         [conflicts.ContentsConflict('file',
 
286
                          file_id='file-id')])
292
287
 
293
288
    def test_merge_type_registry(self):
294
289
        merge_type_option = option.Option.OPTIONS['merge-type']
295
290
        self.assertFalse('merge4' in [x[0] for x in
296
 
                                      merge_type_option.iter_switches()])
 
291
                        merge_type_option.iter_switches()])
297
292
        registry = _mod_merge.get_merge_type_registry()
298
293
        registry.register_lazy('merge4', 'breezy.merge', 'Merge4Merger',
299
294
                               'time-travelling merge')
300
295
        self.assertTrue('merge4' in [x[0] for x in
301
 
                                     merge_type_option.iter_switches()])
 
296
                        merge_type_option.iter_switches()])
302
297
        registry.remove('merge4')
303
298
        self.assertFalse('merge4' in [x[0] for x in
304
 
                                      merge_type_option.iter_switches()])
 
299
                        merge_type_option.iter_switches()])
305
300
 
306
301
    def test_merge_other_moves_we_deleted(self):
307
302
        tree_a = self.make_branch_and_tree('A')
309
304
        self.addCleanup(tree_a.unlock)
310
305
        self.build_tree(['A/a'])
311
306
        tree_a.add('a')
312
 
        tree_a.commit('1', rev_id=b'rev-1')
 
307
        tree_a.commit('1', rev_id='rev-1')
313
308
        tree_a.flush()
314
309
        tree_a.rename_one('a', 'b')
315
310
        tree_a.commit('2')
316
 
        bzrdir_b = tree_a.controldir.sprout('B', revision_id=b'rev-1')
 
311
        bzrdir_b = tree_a.controldir.sprout('B', revision_id='rev-1')
317
312
        tree_b = bzrdir_b.open_workingtree()
318
313
        tree_b.lock_write()
319
314
        self.addCleanup(tree_b.unlock)
364
359
        'c', but not 'b'.
365
360
        """
366
361
        this_tree = self.make_branch_and_tree('this')
367
 
        self.build_tree_contents([('this/file', b"a\n")])
 
362
        self.build_tree_contents([('this/file', "a\n")])
368
363
        this_tree.add('file')
369
364
        this_tree.commit('rev1')
370
365
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
371
 
        self.build_tree_contents([('other/file', b"a\nb\n")])
372
 
        other_tree.commit('rev2b', rev_id=b'rev2b')
373
 
        self.build_tree_contents([('other/file', b"c\na\nb\n")])
374
 
        other_tree.commit('rev3b', rev_id=b'rev3b')
 
366
        self.build_tree_contents([('other/file', "a\nb\n")])
 
367
        other_tree.commit('rev2b', rev_id='rev2b')
 
368
        self.build_tree_contents([('other/file', "c\na\nb\n")])
 
369
        other_tree.commit('rev3b', rev_id='rev3b')
375
370
        this_tree.lock_write()
376
371
        self.addCleanup(this_tree.unlock)
377
372
        return this_tree, other_tree
379
374
    def test_weave_cherrypick(self):
380
375
        this_tree, other_tree = self.prepare_cherrypick()
381
376
        merger = _mod_merge.Merger.from_revision_ids(
382
 
            this_tree, b'rev3b', b'rev2b', other_tree.branch)
 
377
            this_tree, 'rev3b', 'rev2b', other_tree.branch)
383
378
        merger.merge_type = _mod_merge.WeaveMerger
384
379
        merger.do_merge()
385
 
        self.assertFileEqual(b'c\na\n', 'this/file')
 
380
        self.assertFileEqual('c\na\n', 'this/file')
386
381
 
387
382
    def test_weave_cannot_reverse_cherrypick(self):
388
383
        this_tree, other_tree = self.prepare_cherrypick()
389
384
        merger = _mod_merge.Merger.from_revision_ids(
390
 
            this_tree, b'rev2b', b'rev3b', other_tree.branch)
 
385
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
391
386
        merger.merge_type = _mod_merge.WeaveMerger
392
387
        self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
393
388
 
394
389
    def test_merge3_can_reverse_cherrypick(self):
395
390
        this_tree, other_tree = self.prepare_cherrypick()
396
391
        merger = _mod_merge.Merger.from_revision_ids(
397
 
            this_tree, b'rev2b', b'rev3b', other_tree.branch)
 
392
            this_tree, 'rev2b', 'rev3b', other_tree.branch)
398
393
        merger.merge_type = _mod_merge.Merge3Merger
399
394
        merger.do_merge()
400
395
 
401
396
    def test_merge3_will_detect_cherrypick(self):
402
397
        this_tree = self.make_branch_and_tree('this')
403
 
        self.build_tree_contents([('this/file', b"a\n")])
 
398
        self.build_tree_contents([('this/file', "a\n")])
404
399
        this_tree.add('file')
405
400
        this_tree.commit('rev1')
406
401
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
407
 
        self.build_tree_contents([('other/file', b"a\nb\n")])
408
 
        other_tree.commit('rev2b', rev_id=b'rev2b')
409
 
        self.build_tree_contents([('other/file', b"a\nb\nc\n")])
410
 
        other_tree.commit('rev3b', rev_id=b'rev3b')
 
402
        self.build_tree_contents([('other/file', "a\nb\n")])
 
403
        other_tree.commit('rev2b', rev_id='rev2b')
 
404
        self.build_tree_contents([('other/file', "a\nb\nc\n")])
 
405
        other_tree.commit('rev3b', rev_id='rev3b')
411
406
        this_tree.lock_write()
412
407
        self.addCleanup(this_tree.unlock)
413
408
 
414
409
        merger = _mod_merge.Merger.from_revision_ids(
415
 
            this_tree, b'rev3b', b'rev2b', other_tree.branch)
 
410
            this_tree, 'rev3b', 'rev2b', other_tree.branch)
416
411
        merger.merge_type = _mod_merge.Merge3Merger
417
412
        merger.do_merge()
418
 
        self.assertFileEqual(b'a\n'
419
 
                             b'<<<<<<< TREE\n'
420
 
                             b'=======\n'
421
 
                             b'c\n'
422
 
                             b'>>>>>>> MERGE-SOURCE\n',
 
413
        self.assertFileEqual('a\n'
 
414
                             '<<<<<<< TREE\n'
 
415
                             '=======\n'
 
416
                             'c\n'
 
417
                             '>>>>>>> MERGE-SOURCE\n',
423
418
                             'this/file')
424
419
 
425
420
    def test_merge_reverse_revision_range(self):
430
425
        tree.add('a')
431
426
        first_rev = tree.commit("added a")
432
427
        merger = _mod_merge.Merger.from_revision_ids(tree,
433
 
                                                     _mod_revision.NULL_REVISION,
434
 
                                                     first_rev)
 
428
                                          _mod_revision.NULL_REVISION,
 
429
                                          first_rev)
435
430
        merger.merge_type = _mod_merge.Merge3Merger
436
431
        merger.interesting_files = 'a'
437
432
        conflict_count = merger.do_merge()
443
438
 
444
439
    def test_make_merger(self):
445
440
        this_tree = self.make_branch_and_tree('this')
446
 
        this_tree.commit('rev1', rev_id=b'rev1')
 
441
        this_tree.commit('rev1', rev_id='rev1')
447
442
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
448
 
        this_tree.commit('rev2', rev_id=b'rev2a')
449
 
        other_tree.commit('rev2', rev_id=b'rev2b')
 
443
        this_tree.commit('rev2', rev_id='rev2a')
 
444
        other_tree.commit('rev2', rev_id='rev2b')
450
445
        this_tree.lock_write()
451
446
        self.addCleanup(this_tree.unlock)
452
447
        merger = _mod_merge.Merger.from_revision_ids(
453
 
            this_tree, b'rev2b', other_branch=other_tree.branch)
 
448
            this_tree, 'rev2b', other_branch=other_tree.branch)
454
449
        merger.merge_type = _mod_merge.Merge3Merger
455
450
        tree_merger = merger.make_merger()
456
451
        self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
457
 
        self.assertEqual(b'rev2b',
458
 
                         tree_merger.other_tree.get_revision_id())
459
 
        self.assertEqual(b'rev1',
460
 
                         tree_merger.base_tree.get_revision_id())
 
452
        self.assertEqual('rev2b',
 
453
            tree_merger.other_tree.get_revision_id())
 
454
        self.assertEqual('rev1',
 
455
            tree_merger.base_tree.get_revision_id())
461
456
        self.assertEqual(other_tree.branch, tree_merger.other_branch)
462
457
 
463
458
    def test_make_preview_transform(self):
464
459
        this_tree = self.make_branch_and_tree('this')
465
 
        self.build_tree_contents([('this/file', b'1\n')])
466
 
        this_tree.add('file', b'file-id')
467
 
        this_tree.commit('rev1', rev_id=b'rev1')
 
460
        self.build_tree_contents([('this/file', '1\n')])
 
461
        this_tree.add('file', 'file-id')
 
462
        this_tree.commit('rev1', rev_id='rev1')
468
463
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
469
 
        self.build_tree_contents([('this/file', b'1\n2a\n')])
470
 
        this_tree.commit('rev2', rev_id=b'rev2a')
471
 
        self.build_tree_contents([('other/file', b'2b\n1\n')])
472
 
        other_tree.commit('rev2', rev_id=b'rev2b')
 
464
        self.build_tree_contents([('this/file', '1\n2a\n')])
 
465
        this_tree.commit('rev2', rev_id='rev2a')
 
466
        self.build_tree_contents([('other/file', '2b\n1\n')])
 
467
        other_tree.commit('rev2', rev_id='rev2b')
473
468
        this_tree.lock_write()
474
469
        self.addCleanup(this_tree.unlock)
475
470
        merger = _mod_merge.Merger.from_revision_ids(
476
 
            this_tree, b'rev2b', other_branch=other_tree.branch)
 
471
            this_tree, 'rev2b', other_branch=other_tree.branch)
477
472
        merger.merge_type = _mod_merge.Merge3Merger
478
473
        tree_merger = merger.make_merger()
479
 
        with tree_merger.make_preview_transform() as tt:
480
 
            preview_tree = tt.get_preview_tree()
481
 
            with this_tree.get_file('file') as tree_file:
482
 
                self.assertEqual(b'1\n2a\n', tree_file.read())
483
 
            with preview_tree.get_file('file') as preview_file:
484
 
                self.assertEqual(b'2b\n1\n2a\n', preview_file.read())
 
474
        tt = tree_merger.make_preview_transform()
 
475
        self.addCleanup(tt.finalize)
 
476
        preview_tree = tt.get_preview_tree()
 
477
        tree_file = this_tree.get_file('file-id')
 
478
        try:
 
479
            self.assertEqual('1\n2a\n', tree_file.read())
 
480
        finally:
 
481
            tree_file.close()
 
482
        preview_file = preview_tree.get_file('file-id')
 
483
        try:
 
484
            self.assertEqual('2b\n1\n2a\n', preview_file.read())
 
485
        finally:
 
486
            preview_file.close()
485
487
 
486
488
    def test_do_merge(self):
487
489
        this_tree = self.make_branch_and_tree('this')
488
 
        self.build_tree_contents([('this/file', b'1\n')])
489
 
        this_tree.add('file', b'file-id')
490
 
        this_tree.commit('rev1', rev_id=b'rev1')
 
490
        self.build_tree_contents([('this/file', '1\n')])
 
491
        this_tree.add('file', 'file-id')
 
492
        this_tree.commit('rev1', rev_id='rev1')
491
493
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
492
 
        self.build_tree_contents([('this/file', b'1\n2a\n')])
493
 
        this_tree.commit('rev2', rev_id=b'rev2a')
494
 
        self.build_tree_contents([('other/file', b'2b\n1\n')])
495
 
        other_tree.commit('rev2', rev_id=b'rev2b')
 
494
        self.build_tree_contents([('this/file', '1\n2a\n')])
 
495
        this_tree.commit('rev2', rev_id='rev2a')
 
496
        self.build_tree_contents([('other/file', '2b\n1\n')])
 
497
        other_tree.commit('rev2', rev_id='rev2b')
496
498
        this_tree.lock_write()
497
499
        self.addCleanup(this_tree.unlock)
498
500
        merger = _mod_merge.Merger.from_revision_ids(
499
 
            this_tree, b'rev2b', other_branch=other_tree.branch)
 
501
            this_tree, 'rev2b', other_branch=other_tree.branch)
500
502
        merger.merge_type = _mod_merge.Merge3Merger
501
503
        tree_merger = merger.make_merger()
502
504
        tt = tree_merger.do_merge()
503
 
        with this_tree.get_file('file') as tree_file:
504
 
            self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
 
505
        tree_file = this_tree.get_file('file-id')
 
506
        try:
 
507
            self.assertEqual('2b\n1\n2a\n', tree_file.read())
 
508
        finally:
 
509
            tree_file.close()
505
510
 
506
511
    def test_merge_require_tree_root(self):
507
512
        tree = self.make_branch_and_tree(".")
510
515
        self.build_tree(['a'])
511
516
        tree.add('a')
512
517
        first_rev = tree.commit("added a")
513
 
        old_root_id = tree.path2id('')
 
518
        old_root_id = tree.get_root_id()
514
519
        merger = _mod_merge.Merger.from_revision_ids(tree,
515
 
                                                     _mod_revision.NULL_REVISION,
516
 
                                                     first_rev)
 
520
                                          _mod_revision.NULL_REVISION,
 
521
                                          first_rev)
517
522
        merger.merge_type = _mod_merge.Merge3Merger
518
523
        conflict_count = merger.do_merge()
519
524
        self.assertEqual(0, conflict_count)
520
 
        self.assertEqual({''}, set(tree.all_versioned_paths()))
 
525
        self.assertEqual({old_root_id}, tree.all_file_ids())
521
526
        tree.set_parent_ids([])
522
527
 
523
528
    def test_merge_add_into_deleted_root(self):
524
529
        # Yes, people actually do this.  And report bugs if it breaks.
525
530
        source = self.make_branch_and_tree('source', format='rich-root-pack')
526
531
        self.build_tree(['source/foo/'])
527
 
        source.add('foo', b'foo-id')
 
532
        source.add('foo', 'foo-id')
528
533
        source.commit('Add foo')
529
534
        target = source.controldir.sprout('target').open_workingtree()
530
 
        subtree = target.extract('foo')
 
535
        subtree = target.extract('foo-id')
531
536
        subtree.commit('Delete root')
532
537
        self.build_tree(['source/bar'])
533
 
        source.add('bar', b'bar-id')
 
538
        source.add('bar', 'bar-id')
534
539
        source.commit('Add bar')
535
540
        subtree.merge_from_branch(source.branch)
536
541
 
560
565
        mapper = versionedfile.PrefixMapper()
561
566
        factory = knit.make_file_factory(True, mapper)
562
567
        self.vf = factory(self.get_transport())
563
 
        self.plan_merge_vf = versionedfile._PlanMergeVersionedFile(b'root')
 
568
        self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root')
564
569
        self.plan_merge_vf.fallback_versionedfiles.append(self.vf)
565
570
 
566
571
    def add_version(self, key, parents, text):
567
 
        self.vf.add_lines(
568
 
            key, parents, [bytes([c]) + b'\n' for c in bytearray(text)])
 
572
        self.vf.add_lines(key, parents, [c+'\n' for c in text])
569
573
 
570
574
    def add_rev(self, prefix, revision_id, parents, text):
571
575
        self.add_version((prefix, revision_id), [(prefix, p) for p in parents],
573
577
 
574
578
    def add_uncommitted_version(self, key, parents, text):
575
579
        self.plan_merge_vf.add_lines(key, parents,
576
 
                                     [bytes([c]) + b'\n' for c in bytearray(text)])
 
580
                                     [c+'\n' for c in text])
577
581
 
578
582
    def setup_plan_merge(self):
579
 
        self.add_rev(b'root', b'A', [], b'abc')
580
 
        self.add_rev(b'root', b'B', [b'A'], b'acehg')
581
 
        self.add_rev(b'root', b'C', [b'A'], b'fabg')
582
 
        return _PlanMerge(b'B', b'C', self.plan_merge_vf, (b'root',))
 
583
        self.add_rev('root', 'A', [], 'abc')
 
584
        self.add_rev('root', 'B', ['A'], 'acehg')
 
585
        self.add_rev('root', 'C', ['A'], 'fabg')
 
586
        return _PlanMerge('B', 'C', self.plan_merge_vf, ('root',))
583
587
 
584
588
    def setup_plan_merge_uncommitted(self):
585
 
        self.add_version((b'root', b'A'), [], b'abc')
586
 
        self.add_uncommitted_version(
587
 
            (b'root', b'B:'), [(b'root', b'A')], b'acehg')
588
 
        self.add_uncommitted_version(
589
 
            (b'root', b'C:'), [(b'root', b'A')], b'fabg')
590
 
        return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
 
589
        self.add_version(('root', 'A'), [], 'abc')
 
590
        self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
 
591
        self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
 
592
        return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
591
593
 
592
594
    def test_base_from_plan(self):
593
595
        self.setup_plan_merge()
594
 
        plan = self.plan_merge_vf.plan_merge(b'B', b'C')
 
596
        plan = self.plan_merge_vf.plan_merge('B', 'C')
595
597
        pwm = versionedfile.PlanWeaveMerge(plan)
596
 
        self.assertEqual([b'a\n', b'b\n', b'c\n'], pwm.base_from_plan())
 
598
        self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
597
599
 
598
600
    def test_unique_lines(self):
599
601
        plan = self.setup_plan_merge()
600
602
        self.assertEqual(plan._unique_lines(
601
 
            plan._get_matching_blocks(b'B', b'C')),
 
603
            plan._get_matching_blocks('B', 'C')),
602
604
            ([1, 2, 3], [0, 2]))
603
605
 
604
606
    def test_plan_merge(self):
605
607
        self.setup_plan_merge()
606
 
        plan = self.plan_merge_vf.plan_merge(b'B', b'C')
 
608
        plan = self.plan_merge_vf.plan_merge('B', 'C')
607
609
        self.assertEqual([
608
 
            ('new-b', b'f\n'),
609
 
            ('unchanged', b'a\n'),
610
 
            ('killed-a', b'b\n'),
611
 
            ('killed-b', b'c\n'),
612
 
            ('new-a', b'e\n'),
613
 
            ('new-a', b'h\n'),
614
 
            ('new-a', b'g\n'),
615
 
            ('new-b', b'g\n')],
616
 
            list(plan))
 
610
                          ('new-b', 'f\n'),
 
611
                          ('unchanged', 'a\n'),
 
612
                          ('killed-a', 'b\n'),
 
613
                          ('killed-b', 'c\n'),
 
614
                          ('new-a', 'e\n'),
 
615
                          ('new-a', 'h\n'),
 
616
                          ('new-a', 'g\n'),
 
617
                          ('new-b', 'g\n')],
 
618
                         list(plan))
617
619
 
618
620
    def test_plan_merge_cherrypick(self):
619
 
        self.add_rev(b'root', b'A', [], b'abc')
620
 
        self.add_rev(b'root', b'B', [b'A'], b'abcde')
621
 
        self.add_rev(b'root', b'C', [b'A'], b'abcefg')
622
 
        self.add_rev(b'root', b'D', [b'A', b'B', b'C'], b'abcdegh')
623
 
        my_plan = _PlanMerge(b'B', b'D', self.plan_merge_vf, (b'root',))
 
621
        self.add_rev('root', 'A', [], 'abc')
 
622
        self.add_rev('root', 'B', ['A'], 'abcde')
 
623
        self.add_rev('root', 'C', ['A'], 'abcefg')
 
624
        self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
 
625
        my_plan = _PlanMerge('B', 'D', self.plan_merge_vf, ('root',))
624
626
        # We shortcut when one text supersedes the other in the per-file graph.
625
627
        # We don't actually need to compare the texts at this point.
626
628
        self.assertEqual([
627
 
            ('new-b', b'a\n'),
628
 
            ('new-b', b'b\n'),
629
 
            ('new-b', b'c\n'),
630
 
            ('new-b', b'd\n'),
631
 
            ('new-b', b'e\n'),
632
 
            ('new-b', b'g\n'),
633
 
            ('new-b', b'h\n')],
634
 
            list(my_plan.plan_merge()))
 
629
                          ('new-b', 'a\n'),
 
630
                          ('new-b', 'b\n'),
 
631
                          ('new-b', 'c\n'),
 
632
                          ('new-b', 'd\n'),
 
633
                          ('new-b', 'e\n'),
 
634
                          ('new-b', 'g\n'),
 
635
                          ('new-b', 'h\n')],
 
636
                          list(my_plan.plan_merge()))
635
637
 
636
638
    def test_plan_merge_no_common_ancestor(self):
637
 
        self.add_rev(b'root', b'A', [], b'abc')
638
 
        self.add_rev(b'root', b'B', [], b'xyz')
639
 
        my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
 
639
        self.add_rev('root', 'A', [], 'abc')
 
640
        self.add_rev('root', 'B', [], 'xyz')
 
641
        my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
640
642
        self.assertEqual([
641
 
            ('new-a', b'a\n'),
642
 
            ('new-a', b'b\n'),
643
 
            ('new-a', b'c\n'),
644
 
            ('new-b', b'x\n'),
645
 
            ('new-b', b'y\n'),
646
 
            ('new-b', b'z\n')],
647
 
            list(my_plan.plan_merge()))
 
643
                          ('new-a', 'a\n'),
 
644
                          ('new-a', 'b\n'),
 
645
                          ('new-a', 'c\n'),
 
646
                          ('new-b', 'x\n'),
 
647
                          ('new-b', 'y\n'),
 
648
                          ('new-b', 'z\n')],
 
649
                          list(my_plan.plan_merge()))
648
650
 
649
651
    def test_plan_merge_tail_ancestors(self):
650
652
        # The graph looks like this:
666
668
        # are already present in E
667
669
 
668
670
        # Introduce the base text
669
 
        self.add_rev(b'root', b'A', [], b'abc')
 
671
        self.add_rev('root', 'A', [], 'abc')
670
672
        # Introduces a new line B
671
 
        self.add_rev(b'root', b'B', [b'A'], b'aBbc')
 
673
        self.add_rev('root', 'B', ['A'], 'aBbc')
672
674
        # Introduces a new line C
673
 
        self.add_rev(b'root', b'C', [b'A'], b'abCc')
 
675
        self.add_rev('root', 'C', ['A'], 'abCc')
674
676
        # Introduce new line D
675
 
        self.add_rev(b'root', b'D', [b'B'], b'DaBbc')
 
677
        self.add_rev('root', 'D', ['B'], 'DaBbc')
676
678
        # Merges B and C by just incorporating both
677
 
        self.add_rev(b'root', b'E', [b'B', b'C'], b'aBbCc')
 
679
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
678
680
        # Introduce new line F
679
 
        self.add_rev(b'root', b'F', [b'C'], b'abCcF')
 
681
        self.add_rev('root', 'F', ['C'], 'abCcF')
680
682
        # Merge D & E by just combining the texts
681
 
        self.add_rev(b'root', b'G', [b'D', b'E'], b'DaBbCc')
 
683
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
682
684
        # Merge F & E by just combining the texts
683
 
        self.add_rev(b'root', b'H', [b'F', b'E'], b'aBbCcF')
 
685
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
684
686
        # Merge G & H by just combining texts
685
 
        self.add_rev(b'root', b'I', [b'G', b'H'], b'DaBbCcF')
 
687
        self.add_rev('root', 'I', ['G', 'H'], 'DaBbCcF')
686
688
        # Merge G & H but supersede an old line in B
687
 
        self.add_rev(b'root', b'J', [b'H', b'G'], b'DaJbCcF')
688
 
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
 
689
        self.add_rev('root', 'J', ['H', 'G'], 'DaJbCcF')
 
690
        plan = self.plan_merge_vf.plan_merge('I', 'J')
689
691
        self.assertEqual([
690
 
            ('unchanged', b'D\n'),
691
 
            ('unchanged', b'a\n'),
692
 
            ('killed-b', b'B\n'),
693
 
            ('new-b', b'J\n'),
694
 
            ('unchanged', b'b\n'),
695
 
            ('unchanged', b'C\n'),
696
 
            ('unchanged', b'c\n'),
697
 
            ('unchanged', b'F\n')],
698
 
            list(plan))
 
692
                          ('unchanged', 'D\n'),
 
693
                          ('unchanged', 'a\n'),
 
694
                          ('killed-b', 'B\n'),
 
695
                          ('new-b', 'J\n'),
 
696
                          ('unchanged', 'b\n'),
 
697
                          ('unchanged', 'C\n'),
 
698
                          ('unchanged', 'c\n'),
 
699
                          ('unchanged', 'F\n')],
 
700
                         list(plan))
699
701
 
700
702
    def test_plan_merge_tail_triple_ancestors(self):
701
703
        # The graph looks like this:
715
717
        # a third LCA that doesn't add new lines, but will trigger our more
716
718
        # involved ancestry logic
717
719
 
718
 
        self.add_rev(b'root', b'A', [], b'abc')
719
 
        self.add_rev(b'root', b'B', [b'A'], b'aBbc')
720
 
        self.add_rev(b'root', b'C', [b'A'], b'abCc')
721
 
        self.add_rev(b'root', b'D', [b'B'], b'DaBbc')
722
 
        self.add_rev(b'root', b'E', [b'B', b'C'], b'aBbCc')
723
 
        self.add_rev(b'root', b'F', [b'C'], b'abCcF')
724
 
        self.add_rev(b'root', b'G', [b'D', b'E'], b'DaBbCc')
725
 
        self.add_rev(b'root', b'H', [b'F', b'E'], b'aBbCcF')
726
 
        self.add_rev(b'root', b'Q', [b'E'], b'aBbCc')
727
 
        self.add_rev(b'root', b'I', [b'G', b'Q', b'H'], b'DaBbCcF')
 
720
        self.add_rev('root', 'A', [], 'abc')
 
721
        self.add_rev('root', 'B', ['A'], 'aBbc')
 
722
        self.add_rev('root', 'C', ['A'], 'abCc')
 
723
        self.add_rev('root', 'D', ['B'], 'DaBbc')
 
724
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
 
725
        self.add_rev('root', 'F', ['C'], 'abCcF')
 
726
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
 
727
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
 
728
        self.add_rev('root', 'Q', ['E'], 'aBbCc')
 
729
        self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DaBbCcF')
728
730
        # Merge G & H but supersede an old line in B
729
 
        self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DaJbCcF')
730
 
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
 
731
        self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
 
732
        plan = self.plan_merge_vf.plan_merge('I', 'J')
731
733
        self.assertEqual([
732
 
            ('unchanged', b'D\n'),
733
 
            ('unchanged', b'a\n'),
734
 
            ('killed-b', b'B\n'),
735
 
            ('new-b', b'J\n'),
736
 
            ('unchanged', b'b\n'),
737
 
            ('unchanged', b'C\n'),
738
 
            ('unchanged', b'c\n'),
739
 
            ('unchanged', b'F\n')],
740
 
            list(plan))
 
734
                          ('unchanged', 'D\n'),
 
735
                          ('unchanged', 'a\n'),
 
736
                          ('killed-b', 'B\n'),
 
737
                          ('new-b', 'J\n'),
 
738
                          ('unchanged', 'b\n'),
 
739
                          ('unchanged', 'C\n'),
 
740
                          ('unchanged', 'c\n'),
 
741
                          ('unchanged', 'F\n')],
 
742
                         list(plan))
741
743
 
742
744
    def test_plan_merge_2_tail_triple_ancestors(self):
743
745
        # The graph looks like this:
755
757
        # This is meant to test after hitting a 3-way LCA, and multiple tail
756
758
        # ancestors (only have NULL_REVISION in common)
757
759
 
758
 
        self.add_rev(b'root', b'A', [], b'abc')
759
 
        self.add_rev(b'root', b'B', [], b'def')
760
 
        self.add_rev(b'root', b'D', [b'A'], b'Dabc')
761
 
        self.add_rev(b'root', b'E', [b'A', b'B'], b'abcdef')
762
 
        self.add_rev(b'root', b'F', [b'B'], b'defF')
763
 
        self.add_rev(b'root', b'G', [b'D', b'E'], b'Dabcdef')
764
 
        self.add_rev(b'root', b'H', [b'F', b'E'], b'abcdefF')
765
 
        self.add_rev(b'root', b'Q', [b'E'], b'abcdef')
766
 
        self.add_rev(b'root', b'I', [b'G', b'Q', b'H'], b'DabcdefF')
 
760
        self.add_rev('root', 'A', [], 'abc')
 
761
        self.add_rev('root', 'B', [], 'def')
 
762
        self.add_rev('root', 'D', ['A'], 'Dabc')
 
763
        self.add_rev('root', 'E', ['A', 'B'], 'abcdef')
 
764
        self.add_rev('root', 'F', ['B'], 'defF')
 
765
        self.add_rev('root', 'G', ['D', 'E'], 'Dabcdef')
 
766
        self.add_rev('root', 'H', ['F', 'E'], 'abcdefF')
 
767
        self.add_rev('root', 'Q', ['E'], 'abcdef')
 
768
        self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DabcdefF')
767
769
        # Merge G & H but supersede an old line in B
768
 
        self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DabcdJfF')
769
 
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
 
770
        self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DabcdJfF')
 
771
        plan = self.plan_merge_vf.plan_merge('I', 'J')
770
772
        self.assertEqual([
771
 
            ('unchanged', b'D\n'),
772
 
            ('unchanged', b'a\n'),
773
 
            ('unchanged', b'b\n'),
774
 
            ('unchanged', b'c\n'),
775
 
            ('unchanged', b'd\n'),
776
 
            ('killed-b', b'e\n'),
777
 
            ('new-b', b'J\n'),
778
 
            ('unchanged', b'f\n'),
779
 
            ('unchanged', b'F\n')],
780
 
            list(plan))
 
773
                          ('unchanged', 'D\n'),
 
774
                          ('unchanged', 'a\n'),
 
775
                          ('unchanged', 'b\n'),
 
776
                          ('unchanged', 'c\n'),
 
777
                          ('unchanged', 'd\n'),
 
778
                          ('killed-b', 'e\n'),
 
779
                          ('new-b', 'J\n'),
 
780
                          ('unchanged', 'f\n'),
 
781
                          ('unchanged', 'F\n')],
 
782
                         list(plan))
781
783
 
782
784
    def test_plan_merge_uncommitted_files(self):
783
785
        self.setup_plan_merge_uncommitted()
784
 
        plan = self.plan_merge_vf.plan_merge(b'B:', b'C:')
 
786
        plan = self.plan_merge_vf.plan_merge('B:', 'C:')
785
787
        self.assertEqual([
786
 
            ('new-b', b'f\n'),
787
 
            ('unchanged', b'a\n'),
788
 
            ('killed-a', b'b\n'),
789
 
            ('killed-b', b'c\n'),
790
 
            ('new-a', b'e\n'),
791
 
            ('new-a', b'h\n'),
792
 
            ('new-a', b'g\n'),
793
 
            ('new-b', b'g\n')],
794
 
            list(plan))
 
788
                          ('new-b', 'f\n'),
 
789
                          ('unchanged', 'a\n'),
 
790
                          ('killed-a', 'b\n'),
 
791
                          ('killed-b', 'c\n'),
 
792
                          ('new-a', 'e\n'),
 
793
                          ('new-a', 'h\n'),
 
794
                          ('new-a', 'g\n'),
 
795
                          ('new-b', 'g\n')],
 
796
                         list(plan))
795
797
 
796
798
    def test_plan_merge_insert_order(self):
797
799
        """Weave merges are sensitive to the order of insertion.
801
803
        same ordering, then the lines match the parents, if they don't only
802
804
        *some* of the lines match.
803
805
        """
804
 
        self.add_rev(b'root', b'A', [], b'abcdef')
805
 
        self.add_rev(b'root', b'B', [b'A'], b'abwxcdef')
806
 
        self.add_rev(b'root', b'C', [b'A'], b'abyzcdef')
 
806
        self.add_rev('root', 'A', [], 'abcdef')
 
807
        self.add_rev('root', 'B', ['A'], 'abwxcdef')
 
808
        self.add_rev('root', 'C', ['A'], 'abyzcdef')
807
809
        # Merge, and resolve the conflict by adding *both* sets of lines
808
810
        # If we get the ordering wrong, these will look like new lines in D,
809
811
        # rather than carried over from B, C
810
 
        self.add_rev(b'root', b'D', [b'B', b'C'],
811
 
                     b'abwxyzcdef')
 
812
        self.add_rev('root', 'D', ['B', 'C'],
 
813
                         'abwxyzcdef')
812
814
        # Supersede the lines in B and delete the lines in C, which will
813
815
        # conflict if they are treated as being in D
814
 
        self.add_rev(b'root', b'E', [b'C', b'B'],
815
 
                     b'abnocdef')
 
816
        self.add_rev('root', 'E', ['C', 'B'],
 
817
                         'abnocdef')
816
818
        # Same thing for the lines in C
817
 
        self.add_rev(b'root', b'F', [b'C'], b'abpqcdef')
818
 
        plan = self.plan_merge_vf.plan_merge(b'D', b'E')
 
819
        self.add_rev('root', 'F', ['C'], 'abpqcdef')
 
820
        plan = self.plan_merge_vf.plan_merge('D', 'E')
819
821
        self.assertEqual([
820
 
            ('unchanged', b'a\n'),
821
 
            ('unchanged', b'b\n'),
822
 
            ('killed-b', b'w\n'),
823
 
            ('killed-b', b'x\n'),
824
 
            ('killed-b', b'y\n'),
825
 
            ('killed-b', b'z\n'),
826
 
            ('new-b', b'n\n'),
827
 
            ('new-b', b'o\n'),
828
 
            ('unchanged', b'c\n'),
829
 
            ('unchanged', b'd\n'),
830
 
            ('unchanged', b'e\n'),
831
 
            ('unchanged', b'f\n')],
832
 
            list(plan))
833
 
        plan = self.plan_merge_vf.plan_merge(b'E', b'D')
 
822
                          ('unchanged', 'a\n'),
 
823
                          ('unchanged', 'b\n'),
 
824
                          ('killed-b', 'w\n'),
 
825
                          ('killed-b', 'x\n'),
 
826
                          ('killed-b', 'y\n'),
 
827
                          ('killed-b', 'z\n'),
 
828
                          ('new-b', 'n\n'),
 
829
                          ('new-b', 'o\n'),
 
830
                          ('unchanged', 'c\n'),
 
831
                          ('unchanged', 'd\n'),
 
832
                          ('unchanged', 'e\n'),
 
833
                          ('unchanged', 'f\n')],
 
834
                         list(plan))
 
835
        plan = self.plan_merge_vf.plan_merge('E', 'D')
834
836
        # Going in the opposite direction shows the effect of the opposite plan
835
837
        self.assertEqual([
836
 
            ('unchanged', b'a\n'),
837
 
            ('unchanged', b'b\n'),
838
 
            ('new-b', b'w\n'),
839
 
            ('new-b', b'x\n'),
840
 
            ('killed-a', b'y\n'),
841
 
            ('killed-a', b'z\n'),
842
 
            ('killed-both', b'w\n'),
843
 
            ('killed-both', b'x\n'),
844
 
            ('new-a', b'n\n'),
845
 
            ('new-a', b'o\n'),
846
 
            ('unchanged', b'c\n'),
847
 
            ('unchanged', b'd\n'),
848
 
            ('unchanged', b'e\n'),
849
 
            ('unchanged', b'f\n')],
850
 
            list(plan))
 
838
                          ('unchanged', 'a\n'),
 
839
                          ('unchanged', 'b\n'),
 
840
                          ('new-b', 'w\n'),
 
841
                          ('new-b', 'x\n'),
 
842
                          ('killed-a', 'y\n'),
 
843
                          ('killed-a', 'z\n'),
 
844
                          ('killed-both', 'w\n'),
 
845
                          ('killed-both', 'x\n'),
 
846
                          ('new-a', 'n\n'),
 
847
                          ('new-a', 'o\n'),
 
848
                          ('unchanged', 'c\n'),
 
849
                          ('unchanged', 'd\n'),
 
850
                          ('unchanged', 'e\n'),
 
851
                          ('unchanged', 'f\n')],
 
852
                         list(plan))
851
853
 
852
854
    def test_plan_merge_criss_cross(self):
853
855
        # This is specificly trying to trigger problems when using limited
874
876
        #   'foo', it should appear as superseding the value in F (since it
875
877
        #   came from B), rather than conflict because of the resolution during
876
878
        #   C & D.
877
 
        self.add_rev(b'root', b'XX', [], b'qrs')
878
 
        self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
879
 
        self.add_rev(b'root', b'B', [b'A'], b'axcdef')
880
 
        self.add_rev(b'root', b'C', [b'B'], b'axcdefg')
881
 
        self.add_rev(b'root', b'D', [b'B'], b'haxcdef')
882
 
        self.add_rev(b'root', b'E', [b'A'], b'abcdyf')
 
879
        self.add_rev('root', 'XX', [], 'qrs')
 
880
        self.add_rev('root', 'A', ['XX'], 'abcdef')
 
881
        self.add_rev('root', 'B', ['A'], 'axcdef')
 
882
        self.add_rev('root', 'C', ['B'], 'axcdefg')
 
883
        self.add_rev('root', 'D', ['B'], 'haxcdef')
 
884
        self.add_rev('root', 'E', ['A'], 'abcdyf')
883
885
        # Simple combining of all texts
884
 
        self.add_rev(b'root', b'F', [b'C', b'D', b'E'], b'haxcdyfg')
 
886
        self.add_rev('root', 'F', ['C', 'D', 'E'], 'haxcdyfg')
885
887
        # combine and supersede 'x'
886
 
        self.add_rev(b'root', b'G', [b'C', b'D', b'E'], b'hazcdyfg')
887
 
        plan = self.plan_merge_vf.plan_merge(b'F', b'G')
 
888
        self.add_rev('root', 'G', ['C', 'D', 'E'], 'hazcdyfg')
 
889
        plan = self.plan_merge_vf.plan_merge('F', 'G')
888
890
        self.assertEqual([
889
 
            ('unchanged', b'h\n'),
890
 
            ('unchanged', b'a\n'),
891
 
            ('killed-base', b'b\n'),
892
 
            ('killed-b', b'x\n'),
893
 
            ('new-b', b'z\n'),
894
 
            ('unchanged', b'c\n'),
895
 
            ('unchanged', b'd\n'),
896
 
            ('killed-base', b'e\n'),
897
 
            ('unchanged', b'y\n'),
898
 
            ('unchanged', b'f\n'),
899
 
            ('unchanged', b'g\n')],
900
 
            list(plan))
901
 
        plan = self.plan_merge_vf.plan_lca_merge(b'F', b'G')
 
891
                          ('unchanged', 'h\n'),
 
892
                          ('unchanged', 'a\n'),
 
893
                          ('killed-base', 'b\n'),
 
894
                          ('killed-b', 'x\n'),
 
895
                          ('new-b', 'z\n'),
 
896
                          ('unchanged', 'c\n'),
 
897
                          ('unchanged', 'd\n'),
 
898
                          ('killed-base', 'e\n'),
 
899
                          ('unchanged', 'y\n'),
 
900
                          ('unchanged', 'f\n'),
 
901
                          ('unchanged', 'g\n')],
 
902
                         list(plan))
 
903
        plan = self.plan_merge_vf.plan_lca_merge('F', 'G')
902
904
        # This is one of the main differences between plan_merge and
903
905
        # plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
904
906
        # because 'x' was not present in one of the bases. However, in this
905
907
        # case it is spurious because 'x' does not exist in the global base A.
906
908
        self.assertEqual([
907
 
            ('unchanged', b'h\n'),
908
 
            ('unchanged', b'a\n'),
909
 
            ('conflicted-a', b'x\n'),
910
 
            ('new-b', b'z\n'),
911
 
            ('unchanged', b'c\n'),
912
 
            ('unchanged', b'd\n'),
913
 
            ('unchanged', b'y\n'),
914
 
            ('unchanged', b'f\n'),
915
 
            ('unchanged', b'g\n')],
916
 
            list(plan))
 
909
                          ('unchanged', 'h\n'),
 
910
                          ('unchanged', 'a\n'),
 
911
                          ('conflicted-a', 'x\n'),
 
912
                          ('new-b', 'z\n'),
 
913
                          ('unchanged', 'c\n'),
 
914
                          ('unchanged', 'd\n'),
 
915
                          ('unchanged', 'y\n'),
 
916
                          ('unchanged', 'f\n'),
 
917
                          ('unchanged', 'g\n')],
 
918
                         list(plan))
917
919
 
918
920
    def test_criss_cross_flip_flop(self):
919
921
        # This is specificly trying to trigger problems when using limited
921
923
        #       XX      unused ancestor, should not show up in the weave
922
924
        #       |
923
925
        #       A       Unique LCA
924
 
        #      / \
 
926
        #      / \  
925
927
        #     B   C     B & C both introduce a new line
926
 
        #     |\ /|
927
 
        #     | X |
928
 
        #     |/ \|
 
928
        #     |\ /|  
 
929
        #     | X |  
 
930
        #     |/ \| 
929
931
        #     D   E     B & C are both merged, so both are common ancestors
930
932
        #               In the process of merging, both sides order the new
931
933
        #               lines differently
932
934
        #
933
 
        self.add_rev(b'root', b'XX', [], b'qrs')
934
 
        self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
935
 
        self.add_rev(b'root', b'B', [b'A'], b'abcdgef')
936
 
        self.add_rev(b'root', b'C', [b'A'], b'abcdhef')
937
 
        self.add_rev(b'root', b'D', [b'B', b'C'], b'abcdghef')
938
 
        self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
939
 
        plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
 
935
        self.add_rev('root', 'XX', [], 'qrs')
 
936
        self.add_rev('root', 'A', ['XX'], 'abcdef')
 
937
        self.add_rev('root', 'B', ['A'], 'abcdgef')
 
938
        self.add_rev('root', 'C', ['A'], 'abcdhef')
 
939
        self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
 
940
        self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
 
941
        plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
940
942
        self.assertEqual([
941
 
            ('unchanged', b'a\n'),
942
 
            ('unchanged', b'b\n'),
943
 
            ('unchanged', b'c\n'),
944
 
            ('unchanged', b'd\n'),
945
 
            ('new-b', b'h\n'),
946
 
            ('unchanged', b'g\n'),
947
 
            ('killed-b', b'h\n'),
948
 
            ('unchanged', b'e\n'),
949
 
            ('unchanged', b'f\n'),
950
 
            ], plan)
 
943
                          ('unchanged', 'a\n'),
 
944
                          ('unchanged', 'b\n'),
 
945
                          ('unchanged', 'c\n'),
 
946
                          ('unchanged', 'd\n'),
 
947
                          ('new-b', 'h\n'),
 
948
                          ('unchanged', 'g\n'),
 
949
                          ('killed-b', 'h\n'),
 
950
                          ('unchanged', 'e\n'),
 
951
                          ('unchanged', 'f\n'),
 
952
                         ], plan)
951
953
        pwm = versionedfile.PlanWeaveMerge(plan)
952
 
        self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
953
 
                             b''.join(pwm.base_from_plan()))
 
954
        self.assertEqualDiff('\n'.join('abcdghef') + '\n',
 
955
                             ''.join(pwm.base_from_plan()))
954
956
        # Reversing the order reverses the merge plan, and final order of 'hg'
955
957
        # => 'gh'
956
 
        plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
 
958
        plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
957
959
        self.assertEqual([
958
 
            ('unchanged', b'a\n'),
959
 
            ('unchanged', b'b\n'),
960
 
            ('unchanged', b'c\n'),
961
 
            ('unchanged', b'd\n'),
962
 
            ('new-b', b'g\n'),
963
 
            ('unchanged', b'h\n'),
964
 
            ('killed-b', b'g\n'),
965
 
            ('unchanged', b'e\n'),
966
 
            ('unchanged', b'f\n'),
967
 
            ], plan)
 
960
                          ('unchanged', 'a\n'),
 
961
                          ('unchanged', 'b\n'),
 
962
                          ('unchanged', 'c\n'),
 
963
                          ('unchanged', 'd\n'),
 
964
                          ('new-b', 'g\n'),
 
965
                          ('unchanged', 'h\n'),
 
966
                          ('killed-b', 'g\n'),
 
967
                          ('unchanged', 'e\n'),
 
968
                          ('unchanged', 'f\n'),
 
969
                         ], plan)
968
970
        pwm = versionedfile.PlanWeaveMerge(plan)
969
 
        self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
970
 
                             b''.join(pwm.base_from_plan()))
 
971
        self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
 
972
                             ''.join(pwm.base_from_plan()))
971
973
        # This is where lca differs, in that it (fairly correctly) determines
972
974
        # that there is a conflict because both sides resolved the merge
973
975
        # differently
974
 
        plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
 
976
        plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
975
977
        self.assertEqual([
976
 
            ('unchanged', b'a\n'),
977
 
            ('unchanged', b'b\n'),
978
 
            ('unchanged', b'c\n'),
979
 
            ('unchanged', b'd\n'),
980
 
            ('conflicted-b', b'h\n'),
981
 
            ('unchanged', b'g\n'),
982
 
            ('conflicted-a', b'h\n'),
983
 
            ('unchanged', b'e\n'),
984
 
            ('unchanged', b'f\n'),
985
 
            ], plan)
 
978
                          ('unchanged', 'a\n'),
 
979
                          ('unchanged', 'b\n'),
 
980
                          ('unchanged', 'c\n'),
 
981
                          ('unchanged', 'd\n'),
 
982
                          ('conflicted-b', 'h\n'),
 
983
                          ('unchanged', 'g\n'),
 
984
                          ('conflicted-a', 'h\n'),
 
985
                          ('unchanged', 'e\n'),
 
986
                          ('unchanged', 'f\n'),
 
987
                         ], plan)
986
988
        pwm = versionedfile.PlanWeaveMerge(plan)
987
 
        self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
988
 
                             b''.join(pwm.base_from_plan()))
 
989
        self.assertEqualDiff('\n'.join('abcdgef') + '\n',
 
990
                             ''.join(pwm.base_from_plan()))
989
991
        # Reversing it changes what line is doubled, but still gives a
990
992
        # double-conflict
991
 
        plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
 
993
        plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
992
994
        self.assertEqual([
993
 
            ('unchanged', b'a\n'),
994
 
            ('unchanged', b'b\n'),
995
 
            ('unchanged', b'c\n'),
996
 
            ('unchanged', b'd\n'),
997
 
            ('conflicted-b', b'g\n'),
998
 
            ('unchanged', b'h\n'),
999
 
            ('conflicted-a', b'g\n'),
1000
 
            ('unchanged', b'e\n'),
1001
 
            ('unchanged', b'f\n'),
1002
 
            ], plan)
 
995
                          ('unchanged', 'a\n'),
 
996
                          ('unchanged', 'b\n'),
 
997
                          ('unchanged', 'c\n'),
 
998
                          ('unchanged', 'd\n'),
 
999
                          ('conflicted-b', 'g\n'),
 
1000
                          ('unchanged', 'h\n'),
 
1001
                          ('conflicted-a', 'g\n'),
 
1002
                          ('unchanged', 'e\n'),
 
1003
                          ('unchanged', 'f\n'),
 
1004
                         ], plan)
1003
1005
        pwm = versionedfile.PlanWeaveMerge(plan)
1004
 
        self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
1005
 
                             b''.join(pwm.base_from_plan()))
 
1006
        self.assertEqualDiff('\n'.join('abcdhef') + '\n',
 
1007
                             ''.join(pwm.base_from_plan()))
1006
1008
 
1007
1009
    def assertRemoveExternalReferences(self, filtered_parent_map,
1008
1010
                                       child_map, tails, parent_map):
1023
1025
    def test__remove_external_references(self):
1024
1026
        # First, nothing to remove
1025
1027
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
1026
 
                                            {1: [2], 2: [3], 3: []}, [1], {3: [2], 2: [1], 1: []})
 
1028
            {1: [2], 2: [3], 3: []}, [1], {3: [2], 2: [1], 1: []})
1027
1029
        # The reverse direction
1028
1030
        self.assertRemoveExternalReferences({1: [2], 2: [3], 3: []},
1029
 
                                            {3: [2], 2: [1], 1: []}, [3], {1: [2], 2: [3], 3: []})
 
1031
            {3: [2], 2: [1], 1: []}, [3], {1: [2], 2: [3], 3: []})
1030
1032
        # Extra references
1031
1033
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
1032
 
                                            {1: [2], 2: [3], 3: []}, [1], {3: [2, 4], 2: [1, 5], 1: [6]})
 
1034
            {1: [2], 2: [3], 3: []}, [1], {3: [2, 4], 2: [1, 5], 1: [6]})
1033
1035
        # Multiple tails
1034
1036
        self.assertRemoveExternalReferences(
1035
1037
            {4: [2, 3], 3: [], 2: [1], 1: []},
1066
1068
        self.assertPruneTails({1: []}, [5],
1067
1069
                              {1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
1068
1070
        # Prune a partial chain
1069
 
        self.assertPruneTails({1: [6], 6: []}, [5],
 
1071
        self.assertPruneTails({1: [6], 6:[]}, [5],
1070
1072
                              {1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
1071
1073
                               6: []})
1072
1074
        # Prune a chain with multiple tips, that pulls out intermediates
1073
 
        self.assertPruneTails({1: [3], 3: []}, [4, 5],
1074
 
                              {1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
1075
 
        self.assertPruneTails({1: [3], 3: []}, [5, 4],
1076
 
                              {1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
 
1075
        self.assertPruneTails({1:[3], 3:[]}, [4, 5],
 
1076
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
 
1077
        self.assertPruneTails({1:[3], 3:[]}, [5, 4],
 
1078
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1077
1079
 
1078
1080
    def test_subtract_plans(self):
1079
1081
        old_plan = [
1080
 
            ('unchanged', b'a\n'),
1081
 
            ('new-a', b'b\n'),
1082
 
            ('killed-a', b'c\n'),
1083
 
            ('new-b', b'd\n'),
1084
 
            ('new-b', b'e\n'),
1085
 
            ('killed-b', b'f\n'),
1086
 
            ('killed-b', b'g\n'),
 
1082
        ('unchanged', 'a\n'),
 
1083
        ('new-a', 'b\n'),
 
1084
        ('killed-a', 'c\n'),
 
1085
        ('new-b', 'd\n'),
 
1086
        ('new-b', 'e\n'),
 
1087
        ('killed-b', 'f\n'),
 
1088
        ('killed-b', 'g\n'),
1087
1089
        ]
1088
1090
        new_plan = [
1089
 
            ('unchanged', b'a\n'),
1090
 
            ('new-a', b'b\n'),
1091
 
            ('killed-a', b'c\n'),
1092
 
            ('new-b', b'd\n'),
1093
 
            ('new-b', b'h\n'),
1094
 
            ('killed-b', b'f\n'),
1095
 
            ('killed-b', b'i\n'),
 
1091
        ('unchanged', 'a\n'),
 
1092
        ('new-a', 'b\n'),
 
1093
        ('killed-a', 'c\n'),
 
1094
        ('new-b', 'd\n'),
 
1095
        ('new-b', 'h\n'),
 
1096
        ('killed-b', 'f\n'),
 
1097
        ('killed-b', 'i\n'),
1096
1098
        ]
1097
1099
        subtracted_plan = [
1098
 
            ('unchanged', b'a\n'),
1099
 
            ('new-a', b'b\n'),
1100
 
            ('killed-a', b'c\n'),
1101
 
            ('new-b', b'h\n'),
1102
 
            ('unchanged', b'f\n'),
1103
 
            ('killed-b', b'i\n'),
 
1100
        ('unchanged', 'a\n'),
 
1101
        ('new-a', 'b\n'),
 
1102
        ('killed-a', 'c\n'),
 
1103
        ('new-b', 'h\n'),
 
1104
        ('unchanged', 'f\n'),
 
1105
        ('killed-b', 'i\n'),
1104
1106
        ]
1105
1107
        self.assertEqual(subtracted_plan,
1106
 
                         list(_PlanMerge._subtract_plans(old_plan, new_plan)))
 
1108
            list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1107
1109
 
1108
1110
    def setup_merge_with_base(self):
1109
 
        self.add_rev(b'root', b'COMMON', [], b'abc')
1110
 
        self.add_rev(b'root', b'THIS', [b'COMMON'], b'abcd')
1111
 
        self.add_rev(b'root', b'BASE', [b'COMMON'], b'eabc')
1112
 
        self.add_rev(b'root', b'OTHER', [b'BASE'], b'eafb')
 
1111
        self.add_rev('root', 'COMMON', [], 'abc')
 
1112
        self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
 
1113
        self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
 
1114
        self.add_rev('root', 'OTHER', ['BASE'], 'eafb')
1113
1115
 
1114
1116
    def test_plan_merge_with_base(self):
1115
1117
        self.setup_merge_with_base()
1116
 
        plan = self.plan_merge_vf.plan_merge(b'THIS', b'OTHER', b'BASE')
1117
 
        self.assertEqual([('unchanged', b'a\n'),
1118
 
                          ('new-b', b'f\n'),
1119
 
                          ('unchanged', b'b\n'),
1120
 
                          ('killed-b', b'c\n'),
1121
 
                          ('new-a', b'd\n')
1122
 
                          ], list(plan))
 
1118
        plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
 
1119
        self.assertEqual([('unchanged', 'a\n'),
 
1120
                          ('new-b', 'f\n'),
 
1121
                          ('unchanged', 'b\n'),
 
1122
                          ('killed-b', 'c\n'),
 
1123
                          ('new-a', 'd\n')
 
1124
                         ], list(plan))
1123
1125
 
1124
1126
    def test_plan_lca_merge(self):
1125
1127
        self.setup_plan_merge()
1126
 
        plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
 
1128
        plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1127
1129
        self.assertEqual([
1128
 
            ('new-b', b'f\n'),
1129
 
            ('unchanged', b'a\n'),
1130
 
            ('killed-b', b'c\n'),
1131
 
            ('new-a', b'e\n'),
1132
 
            ('new-a', b'h\n'),
1133
 
            ('killed-a', b'b\n'),
1134
 
            ('unchanged', b'g\n')],
1135
 
            list(plan))
 
1130
                          ('new-b', 'f\n'),
 
1131
                          ('unchanged', 'a\n'),
 
1132
                          ('killed-b', 'c\n'),
 
1133
                          ('new-a', 'e\n'),
 
1134
                          ('new-a', 'h\n'),
 
1135
                          ('killed-a', 'b\n'),
 
1136
                          ('unchanged', 'g\n')],
 
1137
                         list(plan))
1136
1138
 
1137
1139
    def test_plan_lca_merge_uncommitted_files(self):
1138
1140
        self.setup_plan_merge_uncommitted()
1139
 
        plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
 
1141
        plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1140
1142
        self.assertEqual([
1141
 
            ('new-b', b'f\n'),
1142
 
            ('unchanged', b'a\n'),
1143
 
            ('killed-b', b'c\n'),
1144
 
            ('new-a', b'e\n'),
1145
 
            ('new-a', b'h\n'),
1146
 
            ('killed-a', b'b\n'),
1147
 
            ('unchanged', b'g\n')],
1148
 
            list(plan))
 
1143
                          ('new-b', 'f\n'),
 
1144
                          ('unchanged', 'a\n'),
 
1145
                          ('killed-b', 'c\n'),
 
1146
                          ('new-a', 'e\n'),
 
1147
                          ('new-a', 'h\n'),
 
1148
                          ('killed-a', 'b\n'),
 
1149
                          ('unchanged', 'g\n')],
 
1150
                         list(plan))
1149
1151
 
1150
1152
    def test_plan_lca_merge_with_base(self):
1151
1153
        self.setup_merge_with_base()
1152
 
        plan = self.plan_merge_vf.plan_lca_merge(b'THIS', b'OTHER', b'BASE')
1153
 
        self.assertEqual([('unchanged', b'a\n'),
1154
 
                          ('new-b', b'f\n'),
1155
 
                          ('unchanged', b'b\n'),
1156
 
                          ('killed-b', b'c\n'),
1157
 
                          ('new-a', b'd\n')
1158
 
                          ], list(plan))
 
1154
        plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
 
1155
        self.assertEqual([('unchanged', 'a\n'),
 
1156
                          ('new-b', 'f\n'),
 
1157
                          ('unchanged', 'b\n'),
 
1158
                          ('killed-b', 'c\n'),
 
1159
                          ('new-a', 'd\n')
 
1160
                         ], list(plan))
1159
1161
 
1160
1162
    def test_plan_lca_merge_with_criss_cross(self):
1161
 
        self.add_version((b'root', b'ROOT'), [], b'abc')
 
1163
        self.add_version(('root', 'ROOT'), [], 'abc')
1162
1164
        # each side makes a change
1163
 
        self.add_version((b'root', b'REV1'), [(b'root', b'ROOT')], b'abcd')
1164
 
        self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
 
1165
        self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
 
1166
        self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
1165
1167
        # both sides merge, discarding others' changes
1166
 
        self.add_version((b'root', b'LCA1'),
1167
 
                         [(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1168
 
        self.add_version((b'root', b'LCA2'),
1169
 
                         [(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1170
 
        plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1171
 
        self.assertEqual([('new-b', b'f\n'),
1172
 
                          ('unchanged', b'a\n'),
1173
 
                          ('unchanged', b'b\n'),
1174
 
                          ('unchanged', b'c\n'),
1175
 
                          ('conflicted-a', b'd\n'),
1176
 
                          ('conflicted-b', b'e\n'),
1177
 
                          ], list(plan))
 
1168
        self.add_version(('root', 'LCA1'),
 
1169
            [('root', 'REV1'), ('root', 'REV2')], 'abcd')
 
1170
        self.add_version(('root', 'LCA2'),
 
1171
            [('root', 'REV1'), ('root', 'REV2')], 'fabce')
 
1172
        plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
 
1173
        self.assertEqual([('new-b', 'f\n'),
 
1174
                          ('unchanged', 'a\n'),
 
1175
                          ('unchanged', 'b\n'),
 
1176
                          ('unchanged', 'c\n'),
 
1177
                          ('conflicted-a', 'd\n'),
 
1178
                          ('conflicted-b', 'e\n'),
 
1179
                         ], list(plan))
1178
1180
 
1179
1181
    def test_plan_lca_merge_with_null(self):
1180
 
        self.add_version((b'root', b'A'), [], b'ab')
1181
 
        self.add_version((b'root', b'B'), [], b'bc')
1182
 
        plan = self.plan_merge_vf.plan_lca_merge(b'A', b'B')
1183
 
        self.assertEqual([('new-a', b'a\n'),
1184
 
                          ('unchanged', b'b\n'),
1185
 
                          ('new-b', b'c\n'),
1186
 
                          ], list(plan))
 
1182
        self.add_version(('root', 'A'), [], 'ab')
 
1183
        self.add_version(('root', 'B'), [], 'bc')
 
1184
        plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
 
1185
        self.assertEqual([('new-a', 'a\n'),
 
1186
                          ('unchanged', 'b\n'),
 
1187
                          ('new-b', 'c\n'),
 
1188
                         ], list(plan))
1187
1189
 
1188
1190
    def test_plan_merge_with_delete_and_change(self):
1189
 
        self.add_rev(b'root', b'C', [], b'a')
1190
 
        self.add_rev(b'root', b'A', [b'C'], b'b')
1191
 
        self.add_rev(b'root', b'B', [b'C'], b'')
1192
 
        plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1193
 
        self.assertEqual([('killed-both', b'a\n'),
1194
 
                          ('new-a', b'b\n'),
1195
 
                          ], list(plan))
 
1191
        self.add_rev('root', 'C', [], 'a')
 
1192
        self.add_rev('root', 'A', ['C'], 'b')
 
1193
        self.add_rev('root', 'B', ['C'], '')
 
1194
        plan = self.plan_merge_vf.plan_merge('A', 'B')
 
1195
        self.assertEqual([('killed-both', 'a\n'),
 
1196
                          ('new-a', 'b\n'),
 
1197
                         ], list(plan))
1196
1198
 
1197
1199
    def test_plan_merge_with_move_and_change(self):
1198
 
        self.add_rev(b'root', b'C', [], b'abcd')
1199
 
        self.add_rev(b'root', b'A', [b'C'], b'acbd')
1200
 
        self.add_rev(b'root', b'B', [b'C'], b'aBcd')
1201
 
        plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1202
 
        self.assertEqual([('unchanged', b'a\n'),
1203
 
                          ('new-a', b'c\n'),
1204
 
                          ('killed-b', b'b\n'),
1205
 
                          ('new-b', b'B\n'),
1206
 
                          ('killed-a', b'c\n'),
1207
 
                          ('unchanged', b'd\n'),
1208
 
                          ], list(plan))
 
1200
        self.add_rev('root', 'C', [], 'abcd')
 
1201
        self.add_rev('root', 'A', ['C'], 'acbd')
 
1202
        self.add_rev('root', 'B', ['C'], 'aBcd')
 
1203
        plan = self.plan_merge_vf.plan_merge('A', 'B')
 
1204
        self.assertEqual([('unchanged', 'a\n'),
 
1205
                          ('new-a', 'c\n'),
 
1206
                          ('killed-b', 'b\n'),
 
1207
                          ('new-b', 'B\n'),
 
1208
                          ('killed-a', 'c\n'),
 
1209
                          ('unchanged', 'd\n'),
 
1210
                         ], list(plan))
1209
1211
 
1210
1212
 
1211
1213
class LoggingMerger(object):
1241
1243
        #  B C
1242
1244
        #
1243
1245
        builder = self.get_builder()
1244
 
        builder.build_snapshot(None,
1245
 
                               [('add', ('', None, 'directory', None))],
1246
 
                               revision_id=b'A-id')
1247
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1248
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
 
1246
        builder.build_snapshot('A-id', None,
 
1247
            [('add', ('', None, 'directory', None))])
 
1248
        builder.build_snapshot('C-id', ['A-id'], [])
 
1249
        builder.build_snapshot('B-id', ['A-id'], [])
1249
1250
        return builder
1250
1251
 
1251
1252
    def setup_criss_cross_graph(self):
1259
1260
        # |X|
1260
1261
        # D E
1261
1262
        builder = self.setup_simple_graph()
1262
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1263
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
 
1263
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1264
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1264
1265
        return builder
1265
1266
 
1266
 
    def make_Merger(self, builder, other_revision_id, interesting_files=None):
 
1267
    def make_Merger(self, builder, other_revision_id,
 
1268
                    interesting_files=None, interesting_ids=None):
1267
1269
        """Make a Merger object from a branch builder"""
1268
1270
        mem_tree = memorytree.MemoryTree.create_on_branch(builder.get_branch())
1269
1271
        mem_tree.lock_write()
1271
1273
        merger = _mod_merge.Merger.from_revision_ids(
1272
1274
            mem_tree, other_revision_id)
1273
1275
        merger.set_interesting_files(interesting_files)
 
1276
        # It seems there is no matching function for set_interesting_ids
 
1277
        merger.interesting_ids = interesting_ids
1274
1278
        merger.merge_type = _mod_merge.Merge3Merger
1275
1279
        return merger
1276
1280
 
1278
1282
class TestMergerInMemory(TestMergerBase):
1279
1283
 
1280
1284
    def test_cache_trees_with_revision_ids_None(self):
1281
 
        merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
 
1285
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1282
1286
        original_cache = dict(merger._cached_trees)
1283
1287
        merger.cache_trees_with_revision_ids([None])
1284
1288
        self.assertEqual(original_cache, merger._cached_trees)
1285
1289
 
1286
1290
    def test_cache_trees_with_revision_ids_no_revision_id(self):
1287
 
        merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
 
1291
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1288
1292
        original_cache = dict(merger._cached_trees)
1289
1293
        tree = self.make_branch_and_memory_tree('tree')
1290
1294
        merger.cache_trees_with_revision_ids([tree])
1291
1295
        self.assertEqual(original_cache, merger._cached_trees)
1292
1296
 
1293
1297
    def test_cache_trees_with_revision_ids_having_revision_id(self):
1294
 
        merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
 
1298
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1295
1299
        original_cache = dict(merger._cached_trees)
1296
 
        tree = merger.this_branch.repository.revision_tree(b'B-id')
1297
 
        original_cache[b'B-id'] = tree
 
1300
        tree = merger.this_branch.repository.revision_tree('B-id')
 
1301
        original_cache['B-id'] = tree
1298
1302
        merger.cache_trees_with_revision_ids([tree])
1299
1303
        self.assertEqual(original_cache, merger._cached_trees)
1300
1304
 
1301
1305
    def test_find_base(self):
1302
 
        merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1303
 
        self.assertEqual(b'A-id', merger.base_rev_id)
 
1306
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
 
1307
        self.assertEqual('A-id', merger.base_rev_id)
1304
1308
        self.assertFalse(merger._is_criss_cross)
1305
1309
        self.assertIs(None, merger._lca_trees)
1306
1310
 
1307
1311
    def test_find_base_criss_cross(self):
1308
1312
        builder = self.setup_criss_cross_graph()
1309
 
        merger = self.make_Merger(builder, b'E-id')
1310
 
        self.assertEqual(b'A-id', merger.base_rev_id)
 
1313
        merger = self.make_Merger(builder, 'E-id')
 
1314
        self.assertEqual('A-id', merger.base_rev_id)
1311
1315
        self.assertTrue(merger._is_criss_cross)
1312
 
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1313
 
                                              for t in merger._lca_trees])
 
1316
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
 
1317
                                            for t in merger._lca_trees])
1314
1318
        # If we swap the order, we should get a different lca order
1315
 
        builder.build_snapshot([b'E-id'], [], revision_id=b'F-id')
1316
 
        merger = self.make_Merger(builder, b'D-id')
1317
 
        self.assertEqual([b'C-id', b'B-id'], [t.get_revision_id()
1318
 
                                              for t in merger._lca_trees])
 
1319
        builder.build_snapshot('F-id', ['E-id'], [])
 
1320
        merger = self.make_Merger(builder, 'D-id')
 
1321
        self.assertEqual(['C-id', 'B-id'], [t.get_revision_id()
 
1322
                                            for t in merger._lca_trees])
1319
1323
 
1320
1324
    def test_find_base_triple_criss_cross(self):
1321
1325
        #       A-.
1328
1332
        #    \|   |/
1329
1333
        #     G   H
1330
1334
        builder = self.setup_criss_cross_graph()
1331
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'F-id')
1332
 
        builder.build_snapshot([b'E-id', b'F-id'], [], revision_id=b'H-id')
1333
 
        builder.build_snapshot([b'D-id', b'F-id'], [], revision_id=b'G-id')
1334
 
        merger = self.make_Merger(builder, b'H-id')
1335
 
        self.assertEqual([b'B-id', b'C-id', b'F-id'],
 
1335
        builder.build_snapshot('F-id', ['A-id'], [])
 
1336
        builder.build_snapshot('H-id', ['E-id', 'F-id'], [])
 
1337
        builder.build_snapshot('G-id', ['D-id', 'F-id'], [])
 
1338
        merger = self.make_Merger(builder, 'H-id')
 
1339
        self.assertEqual(['B-id', 'C-id', 'F-id'],
1336
1340
                         [t.get_revision_id() for t in merger._lca_trees])
1337
1341
 
1338
1342
    def test_find_base_new_root_criss_cross(self):
1341
1345
        # | X |
1342
1346
        # |/ \|
1343
1347
        # C   D
 
1348
        
1344
1349
        builder = self.get_builder()
1345
 
        builder.build_snapshot(None,
1346
 
                               [('add', ('', None, 'directory', None))],
1347
 
                               revision_id=b'A-id')
1348
 
        builder.build_snapshot([],
1349
 
                               [('add', ('', None, 'directory', None))],
1350
 
                               revision_id=b'B-id')
1351
 
        builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'D-id')
1352
 
        builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'C-id')
1353
 
        merger = self.make_Merger(builder, b'D-id')
1354
 
        self.assertEqual(b'A-id', merger.base_rev_id)
 
1350
        builder.build_snapshot('A-id', None,
 
1351
            [('add', ('', None, 'directory', None))])
 
1352
        builder.build_snapshot('B-id', [],
 
1353
            [('add', ('', None, 'directory', None))])
 
1354
        builder.build_snapshot('D-id', ['A-id', 'B-id'], [])
 
1355
        builder.build_snapshot('C-id', ['A-id', 'B-id'], [])
 
1356
        merger = self.make_Merger(builder, 'D-id')
 
1357
        self.assertEqual('A-id', merger.base_rev_id)
1355
1358
        self.assertTrue(merger._is_criss_cross)
1356
 
        self.assertEqual([b'A-id', b'B-id'], [t.get_revision_id()
1357
 
                                              for t in merger._lca_trees])
 
1359
        self.assertEqual(['A-id', 'B-id'], [t.get_revision_id()
 
1360
                                            for t in merger._lca_trees])
1358
1361
 
1359
1362
    def test_no_criss_cross_passed_to_merge_type(self):
1360
1363
        class LCATreesMerger(LoggingMerger):
1361
1364
            supports_lca_trees = True
1362
1365
 
1363
 
        merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
 
1366
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1364
1367
        merger.merge_type = LCATreesMerger
1365
1368
        merge_obj = merger.make_merger()
1366
1369
        self.assertIsInstance(merge_obj, LCATreesMerger)
1367
1370
        self.assertFalse('lca_trees' in merge_obj.kwargs)
1368
1371
 
1369
1372
    def test_criss_cross_passed_to_merge_type(self):
1370
 
        merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
 
1373
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1371
1374
        merger.merge_type = _mod_merge.Merge3Merger
1372
1375
        merge_obj = merger.make_merger()
1373
 
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1374
 
                                              for t in merger._lca_trees])
 
1376
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
 
1377
                                            for t in merger._lca_trees])
1375
1378
 
1376
1379
    def test_criss_cross_not_supported_merge_type(self):
1377
 
        merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
 
1380
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1378
1381
        # We explicitly do not define supports_lca_trees
1379
1382
        merger.merge_type = LoggingMerger
1380
1383
        merge_obj = merger.make_merger()
1385
1388
        class UnsupportedLCATreesMerger(LoggingMerger):
1386
1389
            supports_lca_trees = False
1387
1390
 
1388
 
        merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
 
1391
        merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1389
1392
        merger.merge_type = UnsupportedLCATreesMerger
1390
1393
        merge_obj = merger.make_merger()
1391
1394
        self.assertIsInstance(merge_obj, UnsupportedLCATreesMerger)
1395
1398
class TestMergerEntriesLCA(TestMergerBase):
1396
1399
 
1397
1400
    def make_merge_obj(self, builder, other_revision_id,
1398
 
                       interesting_files=None):
 
1401
                       interesting_files=None, interesting_ids=None):
1399
1402
        merger = self.make_Merger(builder, other_revision_id,
1400
 
                                  interesting_files=interesting_files)
 
1403
            interesting_files=interesting_files,
 
1404
            interesting_ids=interesting_ids)
1401
1405
        return merger.make_merger()
1402
1406
 
1403
1407
    def test_simple(self):
1404
1408
        builder = self.get_builder()
1405
 
        builder.build_snapshot(None,
1406
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1407
 
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1408
 
                               revision_id=b'A-id')
1409
 
        builder.build_snapshot([b'A-id'],
1410
 
                               [('modify', ('a', b'a\nb\nC\nc\n'))],
1411
 
                               revision_id=b'C-id')
1412
 
        builder.build_snapshot([b'A-id'],
1413
 
                               [('modify', ('a', b'a\nB\nb\nc\n'))],
1414
 
                               revision_id=b'B-id')
1415
 
        builder.build_snapshot([b'C-id', b'B-id'],
1416
 
                               [('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1417
 
                               revision_id=b'E-id')
1418
 
        builder.build_snapshot([b'B-id', b'C-id'],
1419
 
                               [('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1420
 
                               revision_id=b'D-id', )
1421
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1409
        builder.build_snapshot('A-id', None,
 
1410
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1411
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1412
        builder.build_snapshot('C-id', ['A-id'],
 
1413
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
 
1414
        builder.build_snapshot('B-id', ['A-id'],
 
1415
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
 
1416
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1417
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
 
1418
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1419
            [('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
 
1420
        merge_obj = self.make_merge_obj(builder, 'E-id')
1422
1421
 
1423
 
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1424
 
                                              for t in merge_obj._lca_trees])
1425
 
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
 
1422
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
 
1423
                                            for t in merge_obj._lca_trees])
 
1424
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1426
1425
        entries = list(merge_obj._entries_lca())
1427
1426
 
1428
1427
        # (file_id, changed, parents, names, executable)
1429
1428
        # BASE, lca1, lca2, OTHER, THIS
1430
 
        root_id = b'a-root-id'
1431
 
        self.assertEqual([(b'a-id', True,
1432
 
                           ((u'a', [u'a', u'a']), u'a', u'a'),
 
1429
        root_id = 'a-root-id'
 
1430
        self.assertEqual([('a-id', True,
1433
1431
                           ((root_id, [root_id, root_id]), root_id, root_id),
1434
1432
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1435
 
                           ((False, [False, False]), False, False),
1436
 
                           False),
1437
 
                          ], entries)
 
1433
                           ((False, [False, False]), False, False)),
 
1434
                         ], entries)
1438
1435
 
1439
1436
    def test_not_in_base(self):
1440
1437
        # LCAs all have the same last-modified revision for the file, as do
1449
1446
        #            G modifies 'bar'
1450
1447
 
1451
1448
        builder = self.get_builder()
1452
 
        builder.build_snapshot(None,
1453
 
                               [('add', (u'', b'a-root-id', 'directory', None))],
1454
 
                               revision_id=b'A-id')
1455
 
        builder.build_snapshot([b'A-id'],
1456
 
                               [('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1457
 
                               revision_id=b'B-id')
1458
 
        builder.build_snapshot([b'A-id'],
1459
 
                               [('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1460
 
                               revision_id=b'C-id')
1461
 
        builder.build_snapshot([b'B-id', b'C-id'],
1462
 
                               [('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1463
 
                               revision_id=b'D-id')
1464
 
        builder.build_snapshot([b'C-id', b'B-id'],
1465
 
                               [('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1466
 
                               revision_id=b'E-id')
1467
 
        builder.build_snapshot([b'E-id', b'D-id'],
1468
 
                               [('modify', (u'bar', b'd\ne\nf\nG\n'))],
1469
 
                               revision_id=b'G-id')
1470
 
        builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1471
 
        merge_obj = self.make_merge_obj(builder, b'G-id')
 
1449
        builder.build_snapshot('A-id', None,
 
1450
            [('add', (u'', 'a-root-id', 'directory', None))])
 
1451
        builder.build_snapshot('B-id', ['A-id'],
 
1452
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
1453
        builder.build_snapshot('C-id', ['A-id'],
 
1454
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
 
1455
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1456
            [('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
 
1457
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1458
            [('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
1459
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
 
1460
            [('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
 
1461
        builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
 
1462
        merge_obj = self.make_merge_obj(builder, 'G-id')
1472
1463
 
1473
 
        self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1474
 
                                              for t in merge_obj._lca_trees])
1475
 
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
 
1464
        self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
 
1465
                                            for t in merge_obj._lca_trees])
 
1466
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1476
1467
        entries = list(merge_obj._entries_lca())
1477
 
        root_id = b'a-root-id'
1478
 
        self.assertEqual([(b'bar-id', True,
1479
 
                           ((None, [u'bar', u'bar']), u'bar', u'bar'),
 
1468
        root_id = 'a-root-id'
 
1469
        self.assertEqual([('bar-id', True,
1480
1470
                           ((None, [root_id, root_id]), root_id, root_id),
1481
1471
                           ((None, [u'bar', u'bar']), u'bar', u'bar'),
1482
 
                           ((None, [False, False]), False, False),
1483
 
                           False),
1484
 
                          ], entries)
 
1472
                           ((None, [False, False]), False, False)),
 
1473
                         ], entries)
1485
1474
 
1486
1475
    def test_not_in_this(self):
1487
1476
        builder = self.get_builder()
1488
 
        builder.build_snapshot(None,
1489
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1490
 
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1491
 
                               revision_id=b'A-id')
1492
 
        builder.build_snapshot([b'A-id'],
1493
 
                               [('modify', ('a', b'a\nB\nb\nc\n'))],
1494
 
                               revision_id=b'B-id')
1495
 
        builder.build_snapshot([b'A-id'],
1496
 
                               [('modify', ('a', b'a\nb\nC\nc\n'))],
1497
 
                               revision_id=b'C-id')
1498
 
        builder.build_snapshot([b'C-id', b'B-id'],
1499
 
                               [('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1500
 
                               revision_id=b'E-id')
1501
 
        builder.build_snapshot([b'B-id', b'C-id'],
1502
 
                               [('unversion', 'a')],
1503
 
                               revision_id=b'D-id')
1504
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1477
        builder.build_snapshot('A-id', None,
 
1478
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1479
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1480
        builder.build_snapshot('B-id', ['A-id'],
 
1481
            [('modify', ('a-id', 'a\nB\nb\nc\n'))])
 
1482
        builder.build_snapshot('C-id', ['A-id'],
 
1483
            [('modify', ('a-id', 'a\nb\nC\nc\n'))])
 
1484
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1485
            [('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
 
1486
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1487
            [('unversion', 'a-id')])
 
1488
        merge_obj = self.make_merge_obj(builder, 'E-id')
1505
1489
 
1506
 
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1507
 
                                              for t in merge_obj._lca_trees])
1508
 
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
 
1490
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
 
1491
                                            for t in merge_obj._lca_trees])
 
1492
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1509
1493
 
1510
1494
        entries = list(merge_obj._entries_lca())
1511
 
        root_id = b'a-root-id'
1512
 
        self.assertEqual([(b'a-id', True,
1513
 
                           ((u'a', [u'a', u'a']), u'a', None),
 
1495
        root_id = 'a-root-id'
 
1496
        self.assertEqual([('a-id', True,
1514
1497
                           ((root_id, [root_id, root_id]), root_id, None),
1515
1498
                           ((u'a', [u'a', u'a']), u'a', None),
1516
 
                           ((False, [False, False]), False, None),
1517
 
                           False),
1518
 
                          ], entries)
 
1499
                           ((False, [False, False]), False, None)),
 
1500
                         ], entries)
1519
1501
 
1520
1502
    def test_file_not_in_one_lca(self):
1521
1503
        #   A   # just root
1524
1506
        #   |X|
1525
1507
        #   D E # D and E both have the file, unchanged from C
1526
1508
        builder = self.get_builder()
1527
 
        builder.build_snapshot(None,
1528
 
                               [('add', (u'', b'a-root-id', 'directory', None))],
1529
 
                               revision_id=b'A-id')
1530
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1531
 
        builder.build_snapshot([b'A-id'],
1532
 
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1533
 
                               revision_id=b'C-id')
1534
 
        builder.build_snapshot([b'C-id', b'B-id'],
1535
 
                               [], revision_id=b'E-id')  # Inherited from C
1536
 
        builder.build_snapshot([b'B-id', b'C-id'],  # Merged from C
1537
 
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1538
 
                               revision_id=b'D-id')
1539
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1509
        builder.build_snapshot('A-id', None,
 
1510
            [('add', (u'', 'a-root-id', 'directory', None))])
 
1511
        builder.build_snapshot('B-id', ['A-id'], [])
 
1512
        builder.build_snapshot('C-id', ['A-id'],
 
1513
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1514
        builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
 
1515
        builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
 
1516
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1517
        merge_obj = self.make_merge_obj(builder, 'E-id')
1540
1518
 
1541
 
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1542
 
                                              for t in merge_obj._lca_trees])
1543
 
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
 
1519
        self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
 
1520
                                            for t in merge_obj._lca_trees])
 
1521
        self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1544
1522
 
1545
1523
        entries = list(merge_obj._entries_lca())
1546
1524
        self.assertEqual([], entries)
1547
1525
 
1548
1526
    def test_not_in_other(self):
1549
1527
        builder = self.get_builder()
1550
 
        builder.build_snapshot(None,
1551
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1552
 
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1553
 
                               revision_id=b'A-id')
1554
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1555
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1556
 
        builder.build_snapshot(
1557
 
            [b'C-id', b'B-id'],
1558
 
            [('unversion', 'a')], revision_id=b'E-id')
1559
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1560
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1528
        builder.build_snapshot('A-id', None,
 
1529
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1530
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1531
        builder.build_snapshot('B-id', ['A-id'], [])
 
1532
        builder.build_snapshot('C-id', ['A-id'], [])
 
1533
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1534
            [('unversion', 'a-id')])
 
1535
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1536
        merge_obj = self.make_merge_obj(builder, 'E-id')
1561
1537
 
1562
1538
        entries = list(merge_obj._entries_lca())
1563
 
        root_id = b'a-root-id'
1564
 
        self.assertEqual([(b'a-id', True,
1565
 
                           ((u'a', [u'a', u'a']), None, u'a'),
 
1539
        root_id = 'a-root-id'
 
1540
        self.assertEqual([('a-id', True,
1566
1541
                           ((root_id, [root_id, root_id]), None, root_id),
1567
1542
                           ((u'a', [u'a', u'a']), None, u'a'),
1568
 
                           ((False, [False, False]), None, False),
1569
 
                           False),
1570
 
                          ], entries)
 
1543
                           ((False, [False, False]), None, False)),
 
1544
                         ], entries)
1571
1545
 
1572
1546
    def test_not_in_other_or_lca(self):
1573
1547
        #       A    base, introduces 'foo'
1582
1556
        #   C => E, no changes
1583
1557
        # D would then win 'cleanly' and no record would be given
1584
1558
        builder = self.get_builder()
1585
 
        builder.build_snapshot(None,
1586
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1587
 
                                ('add', (u'foo', b'foo-id', 'file', b'content\n'))],
1588
 
                               revision_id=b'A-id')
1589
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1590
 
        builder.build_snapshot([b'A-id'],
1591
 
                               [('unversion', 'foo')], revision_id=b'C-id')
1592
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1593
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1594
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1559
        builder.build_snapshot('A-id', None,
 
1560
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1561
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
 
1562
        builder.build_snapshot('B-id', ['A-id'], [])
 
1563
        builder.build_snapshot('C-id', ['A-id'],
 
1564
            [('unversion', 'foo-id')])
 
1565
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1566
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1567
        merge_obj = self.make_merge_obj(builder, 'E-id')
1595
1568
 
1596
1569
        entries = list(merge_obj._entries_lca())
1597
1570
        self.assertEqual([], entries)
1612
1585
        # In this case, we have a conflict of how the changes were resolved. E
1613
1586
        # picked C and D picked B, so we should issue a conflict
1614
1587
        builder = self.get_builder()
1615
 
        builder.build_snapshot(None,
1616
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1617
 
                                ('add', (u'foo', b'foo-id', 'file', b'content\n'))],
1618
 
                               revision_id=b'A-id')
1619
 
        builder.build_snapshot([b'A-id'], [
1620
 
            ('modify', ('foo', b'new-content\n'))],
1621
 
            revision_id=b'B-id')
1622
 
        builder.build_snapshot([b'A-id'],
1623
 
                               [('unversion', 'foo')],
1624
 
                               revision_id=b'C-id')
1625
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1626
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1627
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1588
        builder.build_snapshot('A-id', None,
 
1589
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1590
             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
 
1591
        builder.build_snapshot('B-id', ['A-id'], [
 
1592
            ('modify', ('foo-id', 'new-content\n'))])
 
1593
        builder.build_snapshot('C-id', ['A-id'],
 
1594
            [('unversion', 'foo-id')])
 
1595
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1596
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1597
        merge_obj = self.make_merge_obj(builder, 'E-id')
1628
1598
 
1629
1599
        entries = list(merge_obj._entries_lca())
1630
 
        root_id = b'a-root-id'
1631
 
        self.assertEqual([(b'foo-id', True,
1632
 
                           ((u'foo', [u'foo', None]), None, u'foo'),
 
1600
        root_id = 'a-root-id'
 
1601
        self.assertEqual([('foo-id', True,
1633
1602
                           ((root_id, [root_id, None]), None, root_id),
1634
1603
                           ((u'foo', [u'foo', None]), None, 'foo'),
1635
 
                           ((False, [False, None]), None, False),
1636
 
                           False),
1637
 
                          ], entries)
 
1604
                           ((False, [False, None]), None, False)),
 
1605
                         ], entries)
1638
1606
 
1639
1607
    def test_only_in_one_lca(self):
1640
1608
        #   A   add only root
1653
1621
        #   A => C, add file, thus C supersedes B
1654
1622
        #   w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1655
1623
        builder = self.get_builder()
1656
 
        builder.build_snapshot(None,
1657
 
                               [('add', (u'', b'a-root-id', 'directory', None))],
1658
 
                               revision_id=b'A-id')
1659
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1660
 
        builder.build_snapshot([b'A-id'],
1661
 
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1662
 
                               revision_id=b'C-id')
1663
 
        builder.build_snapshot([b'C-id', b'B-id'],
1664
 
                               [('unversion', 'a')],
1665
 
                               revision_id=b'E-id')
1666
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1667
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1624
        builder.build_snapshot('A-id', None,
 
1625
            [('add', (u'', 'a-root-id', 'directory', None))])
 
1626
        builder.build_snapshot('B-id', ['A-id'], [])
 
1627
        builder.build_snapshot('C-id', ['A-id'],
 
1628
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1629
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1630
            [('unversion', 'a-id')])
 
1631
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1632
        merge_obj = self.make_merge_obj(builder, 'E-id')
1668
1633
 
1669
1634
        entries = list(merge_obj._entries_lca())
1670
1635
        self.assertEqual([], entries)
1671
1636
 
1672
1637
    def test_only_in_other(self):
1673
1638
        builder = self.get_builder()
1674
 
        builder.build_snapshot(None,
1675
 
                               [('add', (u'', b'a-root-id', 'directory', None))],
1676
 
                               revision_id=b'A-id')
1677
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1678
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1679
 
        builder.build_snapshot([b'C-id', b'B-id'],
1680
 
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1681
 
                               revision_id=b'E-id')
1682
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1683
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1639
        builder.build_snapshot('A-id', None,
 
1640
            [('add', (u'', 'a-root-id', 'directory', None))])
 
1641
        builder.build_snapshot('B-id', ['A-id'], [])
 
1642
        builder.build_snapshot('C-id', ['A-id'], [])
 
1643
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1644
            [('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
1645
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1646
        merge_obj = self.make_merge_obj(builder, 'E-id')
1684
1647
 
1685
1648
        entries = list(merge_obj._entries_lca())
1686
 
        root_id = b'a-root-id'
1687
 
        self.assertEqual([(b'a-id', True,
1688
 
                           ((None, [None, None]), u'a', None),
 
1649
        root_id = 'a-root-id'
 
1650
        self.assertEqual([('a-id', True,
1689
1651
                           ((None, [None, None]), root_id, None),
1690
1652
                           ((None, [None, None]), u'a', None),
1691
 
                           ((None, [None, None]), False, None),
1692
 
                           False),
1693
 
                          ], entries)
 
1653
                           ((None, [None, None]), False, None)),
 
1654
                         ], entries)
1694
1655
 
1695
1656
    def test_one_lca_supersedes(self):
1696
1657
        # One LCA supersedes the other LCAs last modified value, but the
1707
1668
        #   though its LCAs disagree. This is because the modification in E
1708
1669
        #   completely supersedes the value in D.
1709
1670
        builder = self.get_builder()
1710
 
        builder.build_snapshot(None,
1711
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1712
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1713
 
                               revision_id=b'A-id')
1714
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1715
 
        builder.build_snapshot([b'A-id'],
1716
 
                               [('modify', ('foo', b'B content\n'))],
1717
 
                               revision_id=b'B-id')
1718
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1719
 
        builder.build_snapshot([b'C-id', b'B-id'],
1720
 
                               [('modify', ('foo', b'E content\n'))],
1721
 
                               revision_id=b'E-id')
1722
 
        builder.build_snapshot([b'E-id', b'D-id'], [], revision_id=b'G-id')
1723
 
        builder.build_snapshot([b'D-id', b'E-id'],
1724
 
                               [('modify', ('foo', b'F content\n'))],
1725
 
                               revision_id=b'F-id')
1726
 
        merge_obj = self.make_merge_obj(builder, b'G-id')
 
1671
        builder.build_snapshot('A-id', None,
 
1672
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1673
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1674
        builder.build_snapshot('C-id', ['A-id'], [])
 
1675
        builder.build_snapshot('B-id', ['A-id'],
 
1676
            [('modify', ('foo-id', 'B content\n'))])
 
1677
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1678
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1679
            [('modify', ('foo-id', 'E content\n'))])
 
1680
        builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
 
1681
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
 
1682
            [('modify', ('foo-id', 'F content\n'))])
 
1683
        merge_obj = self.make_merge_obj(builder, 'G-id')
1727
1684
 
1728
1685
        self.assertEqual([], list(merge_obj._entries_lca()))
1729
1686
 
1757
1714
        # aren't supporting it yet.
1758
1715
        #
1759
1716
        builder = self.get_builder()
1760
 
        builder.build_snapshot(None,
1761
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1762
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1763
 
                               revision_id=b'A-id')
1764
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1765
 
        builder.build_snapshot([b'A-id'],
1766
 
                               [('rename', ('foo', 'bar'))],
1767
 
                               revision_id=b'B-id')
1768
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1769
 
        builder.build_snapshot([b'C-id', b'B-id'],
1770
 
                               [('rename', ('foo', 'bing'))],
1771
 
                               revision_id=b'E-id')  # override to bing
1772
 
        builder.build_snapshot([b'E-id', b'D-id'],
1773
 
                               [('rename', ('bing', 'barry'))],
1774
 
                               revision_id=b'G-id')  # override to barry
1775
 
        builder.build_snapshot([b'D-id', b'E-id'],
1776
 
                               [('rename', ('bar', 'bing'))],
1777
 
                               revision_id=b'F-id')  # Merge in E's change
1778
 
        merge_obj = self.make_merge_obj(builder, b'G-id')
 
1717
        builder.build_snapshot('A-id', None,
 
1718
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1719
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1720
        builder.build_snapshot('C-id', ['A-id'], [])
 
1721
        builder.build_snapshot('B-id', ['A-id'],
 
1722
            [('rename', ('foo', 'bar'))])
 
1723
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1724
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1725
            [('rename', ('foo', 'bing'))]) # override to bing
 
1726
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
 
1727
            [('rename', ('bing', 'barry'))]) # override to barry
 
1728
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
 
1729
            [('rename', ('bar', 'bing'))]) # Merge in E's change
 
1730
        merge_obj = self.make_merge_obj(builder, 'G-id')
1779
1731
 
1780
1732
        self.expectFailure("We don't do an actual heads() check on lca values,"
1781
 
                           " or use the per-attribute graph",
1782
 
                           self.assertEqual, [], list(merge_obj._entries_lca()))
 
1733
            " or use the per-attribute graph",
 
1734
            self.assertEqual, [], list(merge_obj._entries_lca()))
1783
1735
 
1784
1736
    def test_one_lca_accidentally_pruned(self):
1785
1737
        # Another incorrect resolution from the same basic flaw:
1795
1747
        # be pruned from the LCAs, even though it was newly introduced by E
1796
1748
        # (superseding B).
1797
1749
        builder = self.get_builder()
1798
 
        builder.build_snapshot(None,
1799
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1800
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1801
 
                               revision_id=b'A-id')
1802
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1803
 
        builder.build_snapshot([b'A-id'],
1804
 
                               [('rename', ('foo', 'bar'))],
1805
 
                               revision_id=b'B-id')
1806
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1807
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1808
 
        builder.build_snapshot([b'E-id', b'D-id'],
1809
 
                               [('rename', ('foo', 'bar'))],
1810
 
                               revision_id=b'G-id')
1811
 
        builder.build_snapshot([b'D-id', b'E-id'],
1812
 
                               [('rename', ('bar', 'bing'))],
1813
 
                               revision_id=b'F-id')  # should end up conflicting
1814
 
        merge_obj = self.make_merge_obj(builder, b'G-id')
 
1750
        builder.build_snapshot('A-id', None,
 
1751
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1752
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1753
        builder.build_snapshot('C-id', ['A-id'], [])
 
1754
        builder.build_snapshot('B-id', ['A-id'],
 
1755
            [('rename', ('foo', 'bar'))])
 
1756
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1757
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1758
        builder.build_snapshot('G-id', ['E-id', 'D-id'],
 
1759
            [('rename', ('foo', 'bar'))])
 
1760
        builder.build_snapshot('F-id', ['D-id', 'E-id'],
 
1761
            [('rename', ('bar', 'bing'))]) # should end up conflicting
 
1762
        merge_obj = self.make_merge_obj(builder, 'G-id')
1815
1763
 
1816
1764
        entries = list(merge_obj._entries_lca())
1817
 
        root_id = b'a-root-id'
 
1765
        root_id = 'a-root-id'
1818
1766
        self.expectFailure("We prune values from BASE even when relevant.",
1819
 
                           self.assertEqual,
1820
 
                           [(b'foo-id', False,
1821
 
                             ((root_id, [root_id, root_id]), root_id, root_id),
1822
 
                               ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1823
 
                               ((False, [False, False]), False, False),
1824
 
                             False),
1825
 
                            ], entries)
 
1767
            self.assertEqual,
 
1768
                [('foo-id', False,
 
1769
                  ((root_id, [root_id, root_id]), root_id, root_id),
 
1770
                  ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
 
1771
                  ((False, [False, False]), False, False)),
 
1772
                ], entries)
1826
1773
 
1827
1774
    def test_both_sides_revert(self):
1828
1775
        # Both sides of a criss-cross revert the text to the lca
1833
1780
        #       D E  D reverts to B, E reverts to C
1834
1781
        # This should conflict
1835
1782
        builder = self.get_builder()
1836
 
        builder.build_snapshot(None,
1837
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1838
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1839
 
                               revision_id=b'A-id')
1840
 
        builder.build_snapshot([b'A-id'],
1841
 
                               [('modify', ('foo', b'B content\n'))],
1842
 
                               revision_id=b'B-id')
1843
 
        builder.build_snapshot([b'A-id'],
1844
 
                               [('modify', ('foo', b'C content\n'))],
1845
 
                               revision_id=b'C-id')
1846
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1847
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1848
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1783
        builder.build_snapshot('A-id', None,
 
1784
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1785
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1786
        builder.build_snapshot('B-id', ['A-id'],
 
1787
            [('modify', ('foo-id', 'B content\n'))])
 
1788
        builder.build_snapshot('C-id', ['A-id'],
 
1789
            [('modify', ('foo-id', 'C content\n'))])
 
1790
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1791
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1792
        merge_obj = self.make_merge_obj(builder, 'E-id')
1849
1793
 
1850
1794
        entries = list(merge_obj._entries_lca())
1851
 
        root_id = b'a-root-id'
1852
 
        self.assertEqual([(b'foo-id', True,
1853
 
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
 
1795
        root_id = 'a-root-id'
 
1796
        self.assertEqual([('foo-id', True,
1854
1797
                           ((root_id, [root_id, root_id]), root_id, root_id),
1855
1798
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1856
 
                           ((False, [False, False]), False, False),
1857
 
                           False),
1858
 
                          ], entries)
 
1799
                           ((False, [False, False]), False, False)),
 
1800
                         ], entries)
1859
1801
 
1860
1802
    def test_different_lca_resolve_one_side_updates_content(self):
1861
1803
        # Both sides converge, but then one side updates the text.
1869
1811
        # We need to emit an entry for 'foo', because D & E differed on the
1870
1812
        # merge resolution
1871
1813
        builder = self.get_builder()
1872
 
        builder.build_snapshot(None,
1873
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1874
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1875
 
                               revision_id=b'A-id')
1876
 
        builder.build_snapshot([b'A-id'],
1877
 
                               [('modify', ('foo', b'B content\n'))],
1878
 
                               revision_id=b'B-id')
1879
 
        builder.build_snapshot([b'A-id'],
1880
 
                               [('modify', ('foo', b'C content\n'))],
1881
 
                               revision_id=b'C-id', )
1882
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1883
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1884
 
        builder.build_snapshot([b'D-id'],
1885
 
                               [('modify', ('foo', b'F content\n'))],
1886
 
                               revision_id=b'F-id')
1887
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1814
        builder.build_snapshot('A-id', None,
 
1815
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1816
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1817
        builder.build_snapshot('B-id', ['A-id'],
 
1818
            [('modify', ('foo-id', 'B content\n'))])
 
1819
        builder.build_snapshot('C-id', ['A-id'],
 
1820
            [('modify', ('foo-id', 'C content\n'))])
 
1821
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1822
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1823
        builder.build_snapshot('F-id', ['D-id'],
 
1824
            [('modify', ('foo-id', 'F content\n'))])
 
1825
        merge_obj = self.make_merge_obj(builder, 'E-id')
1888
1826
 
1889
1827
        entries = list(merge_obj._entries_lca())
1890
 
        root_id = b'a-root-id'
1891
 
        self.assertEqual([(b'foo-id', True,
1892
 
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
 
1828
        root_id = 'a-root-id'
 
1829
        self.assertEqual([('foo-id', True,
1893
1830
                           ((root_id, [root_id, root_id]), root_id, root_id),
1894
1831
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1895
 
                           ((False, [False, False]), False, False),
1896
 
                           False),
1897
 
                          ], entries)
 
1832
                           ((False, [False, False]), False, False)),
 
1833
                         ], entries)
1898
1834
 
1899
1835
    def test_same_lca_resolution_one_side_updates_content(self):
1900
1836
        # Both sides converge, but then one side updates the text.
1911
1847
        # We need to conflict.
1912
1848
 
1913
1849
        builder = self.get_builder()
1914
 
        builder.build_snapshot(None,
1915
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1916
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1917
 
                               revision_id=b'A-id')
1918
 
        builder.build_snapshot([b'A-id'],
1919
 
                               [('modify', ('foo', b'B content\n'))],
1920
 
                               revision_id=b'B-id')
1921
 
        builder.build_snapshot([b'A-id'],
1922
 
                               [('modify', ('foo', b'C content\n'))],
1923
 
                               revision_id=b'C-id')
1924
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1925
 
        builder.build_snapshot([b'B-id', b'C-id'],
1926
 
                               [('modify', ('foo', b'C content\n'))],
1927
 
                               revision_id=b'D-id')  # Same as E
1928
 
        builder.build_snapshot([b'D-id'],
1929
 
                               [('modify', ('foo', b'F content\n'))],
1930
 
                               revision_id=b'F-id')
1931
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1850
        builder.build_snapshot('A-id', None,
 
1851
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1852
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
1853
        builder.build_snapshot('B-id', ['A-id'],
 
1854
            [('modify', ('foo-id', 'B content\n'))])
 
1855
        builder.build_snapshot('C-id', ['A-id'],
 
1856
            [('modify', ('foo-id', 'C content\n'))])
 
1857
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1858
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1859
            [('modify', ('foo-id', 'C content\n'))]) # Same as E
 
1860
        builder.build_snapshot('F-id', ['D-id'],
 
1861
            [('modify', ('foo-id', 'F content\n'))])
 
1862
        merge_obj = self.make_merge_obj(builder, 'E-id')
1932
1863
 
1933
1864
        entries = list(merge_obj._entries_lca())
1934
1865
        self.expectFailure("We don't detect that LCA resolution was the"
1935
1866
                           " same on both sides",
1936
 
                           self.assertEqual, [], entries)
 
1867
            self.assertEqual, [], entries)
1937
1868
 
1938
1869
    def test_only_path_changed(self):
1939
1870
        builder = self.get_builder()
1940
 
        builder.build_snapshot(None,
1941
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1942
 
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
1943
 
                               revision_id=b'A-id')
1944
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1945
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1946
 
        builder.build_snapshot([b'C-id', b'B-id'],
1947
 
                               [('rename', (u'a', u'b'))],
1948
 
                               revision_id=b'E-id')
1949
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1950
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1871
        builder.build_snapshot('A-id', None,
 
1872
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1873
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
 
1874
        builder.build_snapshot('B-id', ['A-id'], [])
 
1875
        builder.build_snapshot('C-id', ['A-id'], [])
 
1876
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1877
            [('rename', (u'a', u'b'))])
 
1878
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1879
        merge_obj = self.make_merge_obj(builder, 'E-id')
1951
1880
        entries = list(merge_obj._entries_lca())
1952
 
        root_id = b'a-root-id'
 
1881
        root_id = 'a-root-id'
1953
1882
        # The content was not changed, only the path
1954
 
        self.assertEqual([(b'a-id', False,
1955
 
                           ((u'a', [u'a', u'a']), u'b', u'a'),
 
1883
        self.assertEqual([('a-id', False,
1956
1884
                           ((root_id, [root_id, root_id]), root_id, root_id),
1957
1885
                           ((u'a', [u'a', u'a']), u'b', u'a'),
1958
 
                           ((False, [False, False]), False, False),
1959
 
                           False),
1960
 
                          ], entries)
 
1886
                           ((False, [False, False]), False, False)),
 
1887
                         ], entries)
1961
1888
 
1962
1889
    def test_kind_changed(self):
1963
1890
        # Identical content, except 'D' changes a-id into a directory
1964
1891
        builder = self.get_builder()
1965
 
        builder.build_snapshot(None,
1966
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1967
 
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
1968
 
                               revision_id=b'A-id')
1969
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1970
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1971
 
        builder.build_snapshot([b'C-id', b'B-id'],
1972
 
                               [('unversion', 'a'),
1973
 
                                ('flush', None),
1974
 
                                ('add', (u'a', b'a-id', 'directory', None))],
1975
 
                               revision_id=b'E-id')
1976
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1977
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1892
        builder.build_snapshot('A-id', None,
 
1893
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1894
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
 
1895
        builder.build_snapshot('B-id', ['A-id'], [])
 
1896
        builder.build_snapshot('C-id', ['A-id'], [])
 
1897
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1898
            [('unversion', 'a-id'),
 
1899
             ('flush', None),
 
1900
             ('add', (u'a', 'a-id', 'directory', None))])
 
1901
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1902
        merge_obj = self.make_merge_obj(builder, 'E-id')
1978
1903
        entries = list(merge_obj._entries_lca())
1979
 
        root_id = b'a-root-id'
 
1904
        root_id = 'a-root-id'
1980
1905
        # Only the kind was changed (content)
1981
 
        self.assertEqual([(b'a-id', True,
1982
 
                           ((u'a', [u'a', u'a']), u'a', u'a'),
 
1906
        self.assertEqual([('a-id', True,
1983
1907
                           ((root_id, [root_id, root_id]), root_id, root_id),
1984
1908
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1985
 
                           ((False, [False, False]), False, False),
1986
 
                           False),
1987
 
                          ], entries)
 
1909
                           ((False, [False, False]), False, False)),
 
1910
                         ], entries)
1988
1911
 
1989
1912
    def test_this_changed_kind(self):
1990
1913
        # Identical content, but THIS changes a file to a directory
1991
1914
        builder = self.get_builder()
1992
 
        builder.build_snapshot(None,
1993
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
1994
 
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
1995
 
                               revision_id=b'A-id')
1996
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1997
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1998
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1999
 
        builder.build_snapshot([b'B-id', b'C-id'],
2000
 
                               [('unversion', 'a'),
2001
 
                                ('flush', None),
2002
 
                                ('add', (u'a', b'a-id', 'directory', None))],
2003
 
                               revision_id=b'D-id')
2004
 
        merge_obj = self.make_merge_obj(builder, b'E-id')
 
1915
        builder.build_snapshot('A-id', None,
 
1916
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1917
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
 
1918
        builder.build_snapshot('B-id', ['A-id'], [])
 
1919
        builder.build_snapshot('C-id', ['A-id'], [])
 
1920
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
1921
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1922
            [('unversion', 'a-id'),
 
1923
             ('flush', None),
 
1924
             ('add', (u'a', 'a-id', 'directory', None))])
 
1925
        merge_obj = self.make_merge_obj(builder, 'E-id')
2005
1926
        entries = list(merge_obj._entries_lca())
2006
1927
        # Only the kind was changed (content)
2007
1928
        self.assertEqual([], entries)
2009
1930
    def test_interesting_files(self):
2010
1931
        # Two files modified, but we should filter one of them
2011
1932
        builder = self.get_builder()
2012
 
        builder.build_snapshot(None,
2013
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2014
 
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
2015
 
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
2016
 
                               revision_id=b'A-id')
2017
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2018
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2019
 
        builder.build_snapshot([b'C-id', b'B-id'],
2020
 
                               [('modify', ('a', b'new-content\n')),
2021
 
                                ('modify', ('b', b'new-content\n'))],
2022
 
                               revision_id=b'E-id')
2023
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2024
 
        merge_obj = self.make_merge_obj(builder, b'E-id',
 
1933
        builder.build_snapshot('A-id', None,
 
1934
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1935
             ('add', (u'a', 'a-id', 'file', 'content\n')),
 
1936
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
 
1937
        builder.build_snapshot('B-id', ['A-id'], [])
 
1938
        builder.build_snapshot('C-id', ['A-id'], [])
 
1939
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1940
            [('modify', ('a-id', 'new-content\n')),
 
1941
             ('modify', ('b-id', 'new-content\n'))])
 
1942
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1943
        merge_obj = self.make_merge_obj(builder, 'E-id',
2025
1944
                                        interesting_files=['b'])
2026
1945
        entries = list(merge_obj._entries_lca())
2027
 
        root_id = b'a-root-id'
2028
 
        self.assertEqual([(b'b-id', True,
2029
 
                           ((u'b', [u'b', u'b']), u'b', u'b'),
 
1946
        root_id = 'a-root-id'
 
1947
        self.assertEqual([('b-id', True,
2030
1948
                           ((root_id, [root_id, root_id]), root_id, root_id),
2031
1949
                           ((u'b', [u'b', u'b']), u'b', u'b'),
2032
 
                           ((False, [False, False]), False, False),
2033
 
                           False),
2034
 
                          ], entries)
 
1950
                           ((False, [False, False]), False, False)),
 
1951
                         ], entries)
2035
1952
 
2036
1953
    def test_interesting_file_in_this(self):
2037
1954
        # This renamed the file, but it should still match the entry in other
2038
1955
        builder = self.get_builder()
2039
 
        builder.build_snapshot(None,
2040
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2041
 
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
2042
 
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
2043
 
                               revision_id=b'A-id')
2044
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2045
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2046
 
        builder.build_snapshot([b'C-id', b'B-id'],
2047
 
                               [('modify', ('a', b'new-content\n')),
2048
 
                                ('modify', ('b', b'new-content\n'))],
2049
 
                               revision_id=b'E-id')
2050
 
        builder.build_snapshot([b'B-id', b'C-id'],
2051
 
                               [('rename', ('b', 'c'))],
2052
 
                               revision_id=b'D-id')
2053
 
        merge_obj = self.make_merge_obj(builder, b'E-id',
 
1956
        builder.build_snapshot('A-id', None,
 
1957
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1958
             ('add', (u'a', 'a-id', 'file', 'content\n')),
 
1959
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
 
1960
        builder.build_snapshot('B-id', ['A-id'], [])
 
1961
        builder.build_snapshot('C-id', ['A-id'], [])
 
1962
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1963
            [('modify', ('a-id', 'new-content\n')),
 
1964
             ('modify', ('b-id', 'new-content\n'))])
 
1965
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
1966
            [('rename', ('b', 'c'))])
 
1967
        merge_obj = self.make_merge_obj(builder, 'E-id',
2054
1968
                                        interesting_files=['c'])
2055
1969
        entries = list(merge_obj._entries_lca())
2056
 
        root_id = b'a-root-id'
2057
 
        self.assertEqual([(b'b-id', True,
2058
 
                           ((u'b', [u'b', u'b']), u'b', u'c'),
 
1970
        root_id = 'a-root-id'
 
1971
        self.assertEqual([('b-id', True,
2059
1972
                           ((root_id, [root_id, root_id]), root_id, root_id),
2060
1973
                           ((u'b', [u'b', u'b']), u'b', u'c'),
2061
 
                           ((False, [False, False]), False, False),
2062
 
                           False),
2063
 
                          ], entries)
 
1974
                           ((False, [False, False]), False, False)),
 
1975
                         ], entries)
2064
1976
 
2065
1977
    def test_interesting_file_in_base(self):
2066
1978
        # This renamed the file, but it should still match the entry in BASE
2067
1979
        builder = self.get_builder()
2068
 
        builder.build_snapshot(None,
2069
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2070
 
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
2071
 
                                   ('add', (u'c', b'c-id', 'file', b'content\n'))],
2072
 
                               revision_id=b'A-id')
2073
 
        builder.build_snapshot([b'A-id'],
2074
 
                               [('rename', ('c', 'b'))],
2075
 
                               revision_id=b'B-id')
2076
 
        builder.build_snapshot([b'A-id'],
2077
 
                               [('rename', ('c', 'b'))],
2078
 
                               revision_id=b'C-id')
2079
 
        builder.build_snapshot([b'C-id', b'B-id'],
2080
 
                               [('modify', ('a', b'new-content\n')),
2081
 
                                ('modify', ('b', b'new-content\n'))],
2082
 
                               revision_id=b'E-id')
2083
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2084
 
        merge_obj = self.make_merge_obj(builder, b'E-id',
 
1980
        builder.build_snapshot('A-id', None,
 
1981
            [('add', (u'', 'a-root-id', 'directory', None)),
 
1982
             ('add', (u'a', 'a-id', 'file', 'content\n')),
 
1983
             ('add', (u'c', 'c-id', 'file', 'content\n'))])
 
1984
        builder.build_snapshot('B-id', ['A-id'],
 
1985
            [('rename', ('c', 'b'))])
 
1986
        builder.build_snapshot('C-id', ['A-id'],
 
1987
            [('rename', ('c', 'b'))])
 
1988
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
1989
            [('modify', ('a-id', 'new-content\n')),
 
1990
             ('modify', ('c-id', 'new-content\n'))])
 
1991
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
1992
        merge_obj = self.make_merge_obj(builder, 'E-id',
2085
1993
                                        interesting_files=['c'])
2086
1994
        entries = list(merge_obj._entries_lca())
2087
 
        root_id = b'a-root-id'
2088
 
        self.assertEqual([(b'c-id', True,
2089
 
                           ((u'c', [u'b', u'b']), u'b', u'b'),
 
1995
        root_id = 'a-root-id'
 
1996
        self.assertEqual([('c-id', True,
2090
1997
                           ((root_id, [root_id, root_id]), root_id, root_id),
2091
1998
                           ((u'c', [u'b', u'b']), u'b', u'b'),
2092
 
                           ((False, [False, False]), False, False),
2093
 
                           False),
2094
 
                          ], entries)
 
1999
                           ((False, [False, False]), False, False)),
 
2000
                         ], entries)
2095
2001
 
2096
2002
    def test_interesting_file_in_lca(self):
2097
2003
        # This renamed the file, but it should still match the entry in LCA
2098
2004
        builder = self.get_builder()
2099
 
        builder.build_snapshot(None,
2100
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2101
 
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
2102
 
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
2103
 
                               revision_id=b'A-id')
2104
 
        builder.build_snapshot([b'A-id'],
2105
 
                               [('rename', ('b', 'c'))], revision_id=b'B-id')
2106
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2107
 
        builder.build_snapshot([b'C-id', b'B-id'],
2108
 
                               [('modify', ('a', b'new-content\n')),
2109
 
                                ('modify', ('b', b'new-content\n'))],
2110
 
                               revision_id=b'E-id')
2111
 
        builder.build_snapshot([b'B-id', b'C-id'],
2112
 
                               [('rename', ('c', 'b'))], revision_id=b'D-id')
2113
 
        merge_obj = self.make_merge_obj(builder, b'E-id',
 
2005
        builder.build_snapshot('A-id', None,
 
2006
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2007
             ('add', (u'a', 'a-id', 'file', 'content\n')),
 
2008
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
 
2009
        builder.build_snapshot('B-id', ['A-id'],
 
2010
            [('rename', ('b', 'c'))])
 
2011
        builder.build_snapshot('C-id', ['A-id'], [])
 
2012
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2013
            [('modify', ('a-id', 'new-content\n')),
 
2014
             ('modify', ('b-id', 'new-content\n'))])
 
2015
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
2016
            [('rename', ('c', 'b'))])
 
2017
        merge_obj = self.make_merge_obj(builder, 'E-id',
2114
2018
                                        interesting_files=['c'])
2115
2019
        entries = list(merge_obj._entries_lca())
2116
 
        root_id = b'a-root-id'
2117
 
        self.assertEqual([(b'b-id', True,
2118
 
                           ((u'b', [u'c', u'b']), u'b', u'b'),
 
2020
        root_id = 'a-root-id'
 
2021
        self.assertEqual([('b-id', True,
2119
2022
                           ((root_id, [root_id, root_id]), root_id, root_id),
2120
2023
                           ((u'b', [u'c', u'b']), u'b', u'b'),
2121
 
                           ((False, [False, False]), False, False),
2122
 
                           False),
2123
 
                          ], entries)
 
2024
                           ((False, [False, False]), False, False)),
 
2025
                         ], entries)
2124
2026
 
2125
 
    def test_interesting_files(self):
 
2027
    def test_interesting_ids(self):
2126
2028
        # Two files modified, but we should filter one of them
2127
2029
        builder = self.get_builder()
2128
 
        builder.build_snapshot(None,
2129
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2130
 
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
2131
 
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
2132
 
                               revision_id=b'A-id')
2133
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2134
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2135
 
        builder.build_snapshot([b'C-id', b'B-id'],
2136
 
                               [('modify', ('a', b'new-content\n')),
2137
 
                                ('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2138
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2139
 
        merge_obj = self.make_merge_obj(builder, b'E-id',
2140
 
                                        interesting_files=['b'])
 
2030
        builder.build_snapshot('A-id', None,
 
2031
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2032
             ('add', (u'a', 'a-id', 'file', 'content\n')),
 
2033
             ('add', (u'b', 'b-id', 'file', 'content\n'))])
 
2034
        builder.build_snapshot('B-id', ['A-id'], [])
 
2035
        builder.build_snapshot('C-id', ['A-id'], [])
 
2036
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2037
            [('modify', ('a-id', 'new-content\n')),
 
2038
             ('modify', ('b-id', 'new-content\n'))])
 
2039
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2040
        merge_obj = self.make_merge_obj(builder, 'E-id',
 
2041
                                        interesting_ids=['b-id'])
2141
2042
        entries = list(merge_obj._entries_lca())
2142
 
        root_id = b'a-root-id'
2143
 
        self.assertEqual([(b'b-id', True,
2144
 
                           ((u'b', [u'b', u'b']), u'b', u'b'),
 
2043
        root_id = 'a-root-id'
 
2044
        self.assertEqual([('b-id', True,
2145
2045
                           ((root_id, [root_id, root_id]), root_id, root_id),
2146
2046
                           ((u'b', [u'b', u'b']), u'b', u'b'),
2147
 
                           ((False, [False, False]), False, False),
2148
 
                           False),
2149
 
                          ], entries)
 
2047
                           ((False, [False, False]), False, False)),
 
2048
                         ], entries)
 
2049
 
2150
2050
 
2151
2051
 
2152
2052
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2179
2079
 
2180
2080
    def test_simple_lca(self):
2181
2081
        builder = self.get_builder()
2182
 
        builder.build_snapshot(None,
2183
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2184
 
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
2185
 
                               revision_id=b'A-id')
2186
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2187
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2188
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2189
 
        builder.build_snapshot([b'B-id', b'C-id'],
2190
 
                               [('modify', ('a', b'a\nb\nc\nd\ne\nf\n'))],
2191
 
                               revision_id=b'D-id')
2192
 
        wt, conflicts = self.do_merge(builder, b'E-id')
 
2082
        builder.build_snapshot('A-id', None,
 
2083
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2084
             ('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
 
2085
        builder.build_snapshot('C-id', ['A-id'], [])
 
2086
        builder.build_snapshot('B-id', ['A-id'], [])
 
2087
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
2088
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
2089
            [('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
 
2090
        wt, conflicts = self.do_merge(builder, 'E-id')
2193
2091
        self.assertEqual(0, conflicts)
2194
2092
        # The merge should have simply update the contents of 'a'
2195
 
        self.assertEqual(b'a\nb\nc\nd\ne\nf\n', wt.get_file_text('a'))
 
2093
        self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
2196
2094
 
2197
2095
    def test_conflict_without_lca(self):
2198
2096
        # This test would cause a merge conflict, unless we use the lca trees
2207
2105
        #     |
2208
2106
        #     F     Path at 'baz' in F, which supersedes 'bar' and 'foo'
2209
2107
        builder = self.get_builder()
2210
 
        builder.build_snapshot(None,
2211
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2212
 
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2213
 
                               revision_id=b'A-id')
2214
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2215
 
        builder.build_snapshot([b'A-id'],
2216
 
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id', )
2217
 
        builder.build_snapshot([b'C-id', b'B-id'],  # merge the rename
2218
 
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')
2219
 
        builder.build_snapshot([b'E-id'],
2220
 
                               [('rename', ('bar', 'baz'))], revision_id=b'F-id')
2221
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2222
 
        wt, conflicts = self.do_merge(builder, b'F-id')
 
2108
        builder.build_snapshot('A-id', None,
 
2109
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2110
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
2111
        builder.build_snapshot('C-id', ['A-id'], [])
 
2112
        builder.build_snapshot('B-id', ['A-id'],
 
2113
            [('rename', ('foo', 'bar'))])
 
2114
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
 
2115
            [('rename', ('foo', 'bar'))])
 
2116
        builder.build_snapshot('F-id', ['E-id'],
 
2117
            [('rename', ('bar', 'baz'))])
 
2118
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2119
        wt, conflicts = self.do_merge(builder, 'F-id')
2223
2120
        self.assertEqual(0, conflicts)
2224
2121
        # The merge should simply recognize that the final rename takes
2225
2122
        # precedence
2226
 
        self.assertEqual('baz', wt.id2path(b'foo-id'))
 
2123
        self.assertEqual('baz', wt.id2path('foo-id'))
2227
2124
 
2228
2125
    def test_other_deletes_lca_renames(self):
2229
2126
        # This test would cause a merge conflict, unless we use the lca trees
2238
2135
        #     |
2239
2136
        #     F     F deletes 'bar'
2240
2137
        builder = self.get_builder()
2241
 
        builder.build_snapshot(None,
2242
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2243
 
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2244
 
                               revision_id=b'A-id')
2245
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2246
 
        builder.build_snapshot([b'A-id'],
2247
 
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id')
2248
 
        builder.build_snapshot([b'C-id', b'B-id'],  # merge the rename
2249
 
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')
2250
 
        builder.build_snapshot([b'E-id'],
2251
 
                               [('unversion', 'bar')], revision_id=b'F-id')
2252
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2253
 
        wt, conflicts = self.do_merge(builder, b'F-id')
 
2138
        builder.build_snapshot('A-id', None,
 
2139
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2140
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
2141
        builder.build_snapshot('C-id', ['A-id'], [])
 
2142
        builder.build_snapshot('B-id', ['A-id'],
 
2143
            [('rename', ('foo', 'bar'))])
 
2144
        builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
 
2145
            [('rename', ('foo', 'bar'))])
 
2146
        builder.build_snapshot('F-id', ['E-id'],
 
2147
            [('unversion', 'foo-id')])
 
2148
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2149
        wt, conflicts = self.do_merge(builder, 'F-id')
2254
2150
        self.assertEqual(0, conflicts)
2255
 
        self.assertRaises(errors.NoSuchId, wt.id2path, b'foo-id')
 
2151
        self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
2256
2152
 
2257
2153
    def test_executable_changes(self):
2258
2154
        #   A       Path at 'foo'
2265
2161
        #     |
2266
2162
        #     F     Executable bit changed
2267
2163
        builder = self.get_builder()
2268
 
        builder.build_snapshot(None,
2269
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2270
 
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2271
 
                               revision_id=b'A-id')
2272
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2273
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2274
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2275
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
 
2164
        builder.build_snapshot('A-id', None,
 
2165
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2166
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
2167
        builder.build_snapshot('C-id', ['A-id'], [])
 
2168
        builder.build_snapshot('B-id', ['A-id'], [])
 
2169
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2170
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2276
2171
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2277
2172
        wt = self.get_wt_from_builder(builder)
2278
 
        with wt.transform() as tt:
2279
 
            tt.set_executability(True, tt.trans_id_tree_path('foo'))
 
2173
        tt = transform.TreeTransform(wt)
 
2174
        try:
 
2175
            tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2280
2176
            tt.apply()
2281
 
        self.assertTrue(wt.is_executable('foo'))
2282
 
        wt.commit('F-id', rev_id=b'F-id')
 
2177
        except:
 
2178
            tt.finalize()
 
2179
            raise
 
2180
        self.assertTrue(wt.is_executable('foo-id'))
 
2181
        wt.commit('F-id', rev_id='F-id')
2283
2182
        # Reset to D, so that we can merge F
2284
 
        wt.set_parent_ids([b'D-id'])
2285
 
        wt.branch.set_last_revision_info(3, b'D-id')
 
2183
        wt.set_parent_ids(['D-id'])
 
2184
        wt.branch.set_last_revision_info(3, 'D-id')
2286
2185
        wt.revert()
2287
 
        self.assertFalse(wt.is_executable('foo'))
2288
 
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
 
2186
        self.assertFalse(wt.is_executable('foo-id'))
 
2187
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2289
2188
        self.assertEqual(0, conflicts)
2290
 
        self.assertTrue(wt.is_executable('foo'))
 
2189
        self.assertTrue(wt.is_executable('foo-id'))
2291
2190
 
2292
2191
    def test_create_symlink(self):
2293
2192
        self.requireFeature(features.SymlinkFeature)
2303
2202
        # Have to use a real WT, because BranchBuilder and MemoryTree don't
2304
2203
        # have symlink support
2305
2204
        builder = self.get_builder()
2306
 
        builder.build_snapshot(None,
2307
 
                               [('add', (u'', b'a-root-id', 'directory', None))],
2308
 
                               revision_id=b'A-id')
2309
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2310
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2311
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2312
 
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
 
2205
        builder.build_snapshot('A-id', None,
 
2206
            [('add', (u'', 'a-root-id', 'directory', None))])
 
2207
        builder.build_snapshot('C-id', ['A-id'], [])
 
2208
        builder.build_snapshot('B-id', ['A-id'], [])
 
2209
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2210
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2313
2211
        # Have to use a real WT, because BranchBuilder doesn't support exec bit
2314
2212
        wt = self.get_wt_from_builder(builder)
2315
2213
        os.symlink('bar', 'path/foo')
2316
 
        wt.add(['foo'], [b'foo-id'])
2317
 
        self.assertEqual('bar', wt.get_symlink_target('foo'))
2318
 
        wt.commit('add symlink', rev_id=b'F-id')
 
2214
        wt.add(['foo'], ['foo-id'])
 
2215
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
 
2216
        wt.commit('add symlink', rev_id='F-id')
2319
2217
        # Reset to D, so that we can merge F
2320
 
        wt.set_parent_ids([b'D-id'])
2321
 
        wt.branch.set_last_revision_info(3, b'D-id')
 
2218
        wt.set_parent_ids(['D-id'])
 
2219
        wt.branch.set_last_revision_info(3, 'D-id')
2322
2220
        wt.revert()
2323
 
        self.assertFalse(wt.is_versioned('foo'))
2324
 
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
 
2221
        self.assertIs(None, wt.path2id('foo'))
 
2222
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2325
2223
        self.assertEqual(0, conflicts)
2326
 
        self.assertEqual(b'foo-id', wt.path2id('foo'))
2327
 
        self.assertEqual('bar', wt.get_symlink_target('foo'))
 
2224
        self.assertEqual('foo-id', wt.path2id('foo'))
 
2225
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2328
2226
 
2329
2227
    def test_both_sides_revert(self):
2330
2228
        # Both sides of a criss-cross revert the text to the lca
2337
2235
        # This must be done with a real WorkingTree, because normally their
2338
2236
        # inventory contains "None" rather than a real sha1
2339
2237
        builder = self.get_builder()
2340
 
        builder.build_snapshot(None,
2341
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2342
 
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
2343
 
                               revision_id=b'A-id')
2344
 
        builder.build_snapshot([b'A-id'],
2345
 
                               [('modify', ('foo', b'B content\n'))],
2346
 
                               revision_id=b'B-id')
2347
 
        builder.build_snapshot([b'A-id'],
2348
 
                               [('modify', ('foo', b'C content\n'))],
2349
 
                               revision_id=b'C-id')
2350
 
        builder.build_snapshot([b'C-id', b'B-id'], [],
2351
 
                               revision_id=b'E-id')
2352
 
        builder.build_snapshot([b'B-id', b'C-id'], [],
2353
 
                               revision_id=b'D-id')
2354
 
        wt, conflicts = self.do_merge(builder, b'E-id')
 
2238
        builder.build_snapshot('A-id', None,
 
2239
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2240
             ('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
 
2241
        builder.build_snapshot('B-id', ['A-id'],
 
2242
            [('modify', ('foo-id', 'B content\n'))])
 
2243
        builder.build_snapshot('C-id', ['A-id'],
 
2244
            [('modify', ('foo-id', 'C content\n'))])
 
2245
        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
 
2246
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2247
        wt, conflicts = self.do_merge(builder, 'E-id')
2355
2248
        self.assertEqual(1, conflicts)
2356
 
        self.assertEqualDiff(b'<<<<<<< TREE\n'
2357
 
                             b'B content\n'
2358
 
                             b'=======\n'
2359
 
                             b'C content\n'
2360
 
                             b'>>>>>>> MERGE-SOURCE\n',
2361
 
                             wt.get_file_text('foo'))
 
2249
        self.assertEqualDiff('<<<<<<< TREE\n'
 
2250
                             'B content\n'
 
2251
                             '=======\n'
 
2252
                             'C content\n'
 
2253
                             '>>>>>>> MERGE-SOURCE\n',
 
2254
                             wt.get_file_text('foo-id'))
2362
2255
 
2363
2256
    def test_modified_symlink(self):
2364
2257
        self.requireFeature(features.SymlinkFeature)
2381
2274
        wt.lock_write()
2382
2275
        self.addCleanup(wt.unlock)
2383
2276
        os.symlink('bar', 'path/foo')
2384
 
        wt.add(['foo'], [b'foo-id'])
2385
 
        wt.commit('add symlink', rev_id=b'A-id')
 
2277
        wt.add(['foo'], ['foo-id'])
 
2278
        wt.commit('add symlink', rev_id='A-id')
2386
2279
        os.remove('path/foo')
2387
2280
        os.symlink('baz', 'path/foo')
2388
 
        wt.commit('foo => baz', rev_id=b'B-id')
2389
 
        wt.set_last_revision(b'A-id')
2390
 
        wt.branch.set_last_revision_info(1, b'A-id')
 
2281
        wt.commit('foo => baz', rev_id='B-id')
 
2282
        wt.set_last_revision('A-id')
 
2283
        wt.branch.set_last_revision_info(1, 'A-id')
2391
2284
        wt.revert()
2392
 
        wt.commit('C', rev_id=b'C-id')
2393
 
        wt.merge_from_branch(wt.branch, b'B-id')
2394
 
        self.assertEqual('baz', wt.get_symlink_target('foo'))
2395
 
        wt.commit('E merges C & B', rev_id=b'E-id')
 
2285
        wt.commit('C', rev_id='C-id')
 
2286
        wt.merge_from_branch(wt.branch, 'B-id')
 
2287
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
 
2288
        wt.commit('E merges C & B', rev_id='E-id')
2396
2289
        os.remove('path/foo')
2397
2290
        os.symlink('bing', 'path/foo')
2398
 
        wt.commit('F foo => bing', rev_id=b'F-id')
2399
 
        wt.set_last_revision(b'B-id')
2400
 
        wt.branch.set_last_revision_info(2, b'B-id')
 
2291
        wt.commit('F foo => bing', rev_id='F-id')
 
2292
        wt.set_last_revision('B-id')
 
2293
        wt.branch.set_last_revision_info(2, 'B-id')
2401
2294
        wt.revert()
2402
 
        wt.merge_from_branch(wt.branch, b'C-id')
2403
 
        wt.commit('D merges B & C', rev_id=b'D-id')
2404
 
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
 
2295
        wt.merge_from_branch(wt.branch, 'C-id')
 
2296
        wt.commit('D merges B & C', rev_id='D-id')
 
2297
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2405
2298
        self.assertEqual(0, conflicts)
2406
 
        self.assertEqual('bing', wt.get_symlink_target('foo'))
 
2299
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2407
2300
 
2408
2301
    def test_renamed_symlink(self):
2409
2302
        self.requireFeature(features.SymlinkFeature)
2424
2317
        wt.lock_write()
2425
2318
        self.addCleanup(wt.unlock)
2426
2319
        os.symlink('bar', 'path/foo')
2427
 
        wt.add(['foo'], [b'foo-id'])
2428
 
        wt.commit('A add symlink', rev_id=b'A-id')
 
2320
        wt.add(['foo'], ['foo-id'])
 
2321
        wt.commit('A add symlink', rev_id='A-id')
2429
2322
        wt.rename_one('foo', 'barry')
2430
 
        wt.commit('B foo => barry', rev_id=b'B-id')
2431
 
        wt.set_last_revision(b'A-id')
2432
 
        wt.branch.set_last_revision_info(1, b'A-id')
 
2323
        wt.commit('B foo => barry', rev_id='B-id')
 
2324
        wt.set_last_revision('A-id')
 
2325
        wt.branch.set_last_revision_info(1, 'A-id')
2433
2326
        wt.revert()
2434
 
        wt.commit('C', rev_id=b'C-id')
2435
 
        wt.merge_from_branch(wt.branch, b'B-id')
2436
 
        self.assertEqual('barry', wt.id2path(b'foo-id'))
2437
 
        self.assertEqual('bar', wt.get_symlink_target('barry'))
2438
 
        wt.commit('E merges C & B', rev_id=b'E-id')
 
2327
        wt.commit('C', rev_id='C-id')
 
2328
        wt.merge_from_branch(wt.branch, 'B-id')
 
2329
        self.assertEqual('barry', wt.id2path('foo-id'))
 
2330
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
 
2331
        wt.commit('E merges C & B', rev_id='E-id')
2439
2332
        wt.rename_one('barry', 'blah')
2440
 
        wt.commit('F barry => blah', rev_id=b'F-id')
2441
 
        wt.set_last_revision(b'B-id')
2442
 
        wt.branch.set_last_revision_info(2, b'B-id')
 
2333
        wt.commit('F barry => blah', rev_id='F-id')
 
2334
        wt.set_last_revision('B-id')
 
2335
        wt.branch.set_last_revision_info(2, 'B-id')
2443
2336
        wt.revert()
2444
 
        wt.merge_from_branch(wt.branch, b'C-id')
2445
 
        wt.commit('D merges B & C', rev_id=b'D-id')
2446
 
        self.assertEqual('barry', wt.id2path(b'foo-id'))
 
2337
        wt.merge_from_branch(wt.branch, 'C-id')
 
2338
        wt.commit('D merges B & C', rev_id='D-id')
 
2339
        self.assertEqual('barry', wt.id2path('foo-id'))
2447
2340
        # Check the output of the Merger object directly
2448
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
 
2341
        merger = _mod_merge.Merger.from_revision_ids(wt, 'F-id')
2449
2342
        merger.merge_type = _mod_merge.Merge3Merger
2450
2343
        merge_obj = merger.make_merger()
2451
2344
        root_id = wt.path2id('')
2452
2345
        entries = list(merge_obj._entries_lca())
2453
2346
        # No content change, just a path change
2454
 
        self.assertEqual([(b'foo-id', False,
2455
 
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
 
2347
        self.assertEqual([('foo-id', False,
2456
2348
                           ((root_id, [root_id, root_id]), root_id, root_id),
2457
2349
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2458
 
                           ((False, [False, False]), False, False),
2459
 
                           False),
2460
 
                          ], entries)
2461
 
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
 
2350
                           ((False, [False, False]), False, False)),
 
2351
                         ], entries)
 
2352
        conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2462
2353
        self.assertEqual(0, conflicts)
2463
 
        self.assertEqual('blah', wt.id2path(b'foo-id'))
 
2354
        self.assertEqual('blah', wt.id2path('foo-id'))
2464
2355
 
2465
2356
    def test_symlink_no_content_change(self):
2466
2357
        self.requireFeature(features.SymlinkFeature)
2480
2371
        wt.lock_write()
2481
2372
        self.addCleanup(wt.unlock)
2482
2373
        os.symlink('bar', 'path/foo')
2483
 
        wt.add(['foo'], [b'foo-id'])
2484
 
        wt.commit('add symlink', rev_id=b'A-id')
 
2374
        wt.add(['foo'], ['foo-id'])
 
2375
        wt.commit('add symlink', rev_id='A-id')
2485
2376
        os.remove('path/foo')
2486
2377
        os.symlink('baz', 'path/foo')
2487
 
        wt.commit('foo => baz', rev_id=b'B-id')
2488
 
        wt.set_last_revision(b'A-id')
2489
 
        wt.branch.set_last_revision_info(1, b'A-id')
2490
 
        wt.revert()
2491
 
        wt.commit('C', rev_id=b'C-id')
2492
 
        wt.merge_from_branch(wt.branch, b'B-id')
2493
 
        self.assertEqual('baz', wt.get_symlink_target('foo'))
2494
 
        wt.commit('E merges C & B', rev_id=b'E-id')
2495
 
        wt.set_last_revision(b'B-id')
2496
 
        wt.branch.set_last_revision_info(2, b'B-id')
2497
 
        wt.revert()
2498
 
        wt.merge_from_branch(wt.branch, b'C-id')
2499
 
        wt.commit('D merges B & C', rev_id=b'D-id')
 
2378
        wt.commit('foo => baz', rev_id='B-id')
 
2379
        wt.set_last_revision('A-id')
 
2380
        wt.branch.set_last_revision_info(1, 'A-id')
 
2381
        wt.revert()
 
2382
        wt.commit('C', rev_id='C-id')
 
2383
        wt.merge_from_branch(wt.branch, 'B-id')
 
2384
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
 
2385
        wt.commit('E merges C & B', rev_id='E-id')
 
2386
        wt.set_last_revision('B-id')
 
2387
        wt.branch.set_last_revision_info(2, 'B-id')
 
2388
        wt.revert()
 
2389
        wt.merge_from_branch(wt.branch, 'C-id')
 
2390
        wt.commit('D merges B & C', rev_id='D-id')
2500
2391
        os.remove('path/foo')
2501
2392
        os.symlink('bing', 'path/foo')
2502
 
        wt.commit('F foo => bing', rev_id=b'F-id')
 
2393
        wt.commit('F foo => bing', rev_id='F-id')
2503
2394
 
2504
2395
        # Check the output of the Merger object directly
2505
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2396
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2506
2397
        merger.merge_type = _mod_merge.Merge3Merger
2507
2398
        merge_obj = merger.make_merger()
2508
2399
        # Nothing interesting happened in OTHER relative to BASE
2509
2400
        self.assertEqual([], list(merge_obj._entries_lca()))
2510
2401
        # Now do a real merge, just to test the rest of the stack
2511
 
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'E-id')
 
2402
        conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2512
2403
        self.assertEqual(0, conflicts)
2513
 
        self.assertEqual('bing', wt.get_symlink_target('foo'))
 
2404
        self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2514
2405
 
2515
2406
    def test_symlink_this_changed_kind(self):
2516
2407
        self.requireFeature(features.SymlinkFeature)
2527
2418
        wt = self.make_branch_and_tree('path')
2528
2419
        wt.lock_write()
2529
2420
        self.addCleanup(wt.unlock)
2530
 
        wt.commit('base', rev_id=b'A-id')
 
2421
        wt.commit('base', rev_id='A-id')
2531
2422
        os.symlink('bar', 'path/foo')
2532
 
        wt.add(['foo'], [b'foo-id'])
2533
 
        wt.commit('add symlink foo => bar', rev_id=b'B-id')
2534
 
        wt.set_last_revision(b'A-id')
2535
 
        wt.branch.set_last_revision_info(1, b'A-id')
 
2423
        wt.add(['foo'], ['foo-id'])
 
2424
        wt.commit('add symlink foo => bar', rev_id='B-id')
 
2425
        wt.set_last_revision('A-id')
 
2426
        wt.branch.set_last_revision_info(1, 'A-id')
2536
2427
        wt.revert()
2537
 
        wt.commit('C', rev_id=b'C-id')
2538
 
        wt.merge_from_branch(wt.branch, b'B-id')
2539
 
        self.assertEqual('bar', wt.get_symlink_target('foo'))
 
2428
        wt.commit('C', rev_id='C-id')
 
2429
        wt.merge_from_branch(wt.branch, 'B-id')
 
2430
        self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2540
2431
        os.remove('path/foo')
2541
2432
        # We have to change the link in E, or it won't try to do a comparison
2542
2433
        os.symlink('bing', 'path/foo')
2543
 
        wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2544
 
        wt.set_last_revision(b'B-id')
2545
 
        wt.branch.set_last_revision_info(2, b'B-id')
 
2434
        wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
 
2435
        wt.set_last_revision('B-id')
 
2436
        wt.branch.set_last_revision_info(2, 'B-id')
2546
2437
        wt.revert()
2547
 
        wt.merge_from_branch(wt.branch, b'C-id')
 
2438
        wt.merge_from_branch(wt.branch, 'C-id')
2548
2439
        os.remove('path/foo')
2549
 
        self.build_tree_contents([('path/foo', b'file content\n')])
 
2440
        self.build_tree_contents([('path/foo', 'file content\n')])
2550
2441
        # XXX: workaround, WT doesn't detect kind changes unless you do
2551
2442
        # iter_changes()
2552
2443
        list(wt.iter_changes(wt.basis_tree()))
2553
 
        wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
 
2444
        wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2554
2445
 
2555
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2446
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2556
2447
        merger.merge_type = _mod_merge.Merge3Merger
2557
2448
        merge_obj = merger.make_merger()
2558
2449
        entries = list(merge_obj._entries_lca())
2559
2450
        root_id = wt.path2id('')
2560
 
        self.assertEqual([(b'foo-id', True,
2561
 
                           ((None, [u'foo', None]), u'foo', u'foo'),
 
2451
        self.assertEqual([('foo-id', True,
2562
2452
                           ((None, [root_id, None]), root_id, root_id),
2563
2453
                           ((None, [u'foo', None]), u'foo', u'foo'),
2564
 
                           ((None, [False, None]), False, False),
2565
 
                           False),
2566
 
                          ], entries)
 
2454
                           ((None, [False, None]), False, False)),
 
2455
                         ], entries)
2567
2456
 
2568
2457
    def test_symlink_all_wt(self):
2569
2458
        """Check behavior if all trees are Working Trees."""
2584
2473
        wt.lock_write()
2585
2474
        self.addCleanup(wt.unlock)
2586
2475
        os.symlink('bar', 'path/foo')
2587
 
        wt.add(['foo'], [b'foo-id'])
2588
 
        wt.commit('add symlink', rev_id=b'A-id')
 
2476
        wt.add(['foo'], ['foo-id'])
 
2477
        wt.commit('add symlink', rev_id='A-id')
2589
2478
        os.remove('path/foo')
2590
2479
        os.symlink('baz', 'path/foo')
2591
 
        wt.commit('foo => baz', rev_id=b'B-id')
2592
 
        wt.set_last_revision(b'A-id')
2593
 
        wt.branch.set_last_revision_info(1, b'A-id')
 
2480
        wt.commit('foo => baz', rev_id='B-id')
 
2481
        wt.set_last_revision('A-id')
 
2482
        wt.branch.set_last_revision_info(1, 'A-id')
2594
2483
        wt.revert()
2595
 
        wt.commit('C', rev_id=b'C-id')
2596
 
        wt.merge_from_branch(wt.branch, b'B-id')
2597
 
        self.assertEqual('baz', wt.get_symlink_target('foo'))
2598
 
        wt.commit('E merges C & B', rev_id=b'E-id')
 
2484
        wt.commit('C', rev_id='C-id')
 
2485
        wt.merge_from_branch(wt.branch, 'B-id')
 
2486
        self.assertEqual('baz', wt.get_symlink_target('foo-id'))
 
2487
        wt.commit('E merges C & B', rev_id='E-id')
2599
2488
        os.remove('path/foo')
2600
2489
        os.symlink('bing', 'path/foo')
2601
 
        wt.commit('F foo => bing', rev_id=b'F-id')
2602
 
        wt.set_last_revision(b'B-id')
2603
 
        wt.branch.set_last_revision_info(2, b'B-id')
 
2490
        wt.commit('F foo => bing', rev_id='F-id')
 
2491
        wt.set_last_revision('B-id')
 
2492
        wt.branch.set_last_revision_info(2, 'B-id')
2604
2493
        wt.revert()
2605
 
        wt.merge_from_branch(wt.branch, b'C-id')
2606
 
        wt.commit('D merges B & C', rev_id=b'D-id')
2607
 
        wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
 
2494
        wt.merge_from_branch(wt.branch, 'C-id')
 
2495
        wt.commit('D merges B & C', rev_id='D-id')
 
2496
        wt_base = wt.controldir.sprout('base', 'A-id').open_workingtree()
2608
2497
        wt_base.lock_read()
2609
2498
        self.addCleanup(wt_base.unlock)
2610
 
        wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
 
2499
        wt_lca1 = wt.controldir.sprout('b-tree', 'B-id').open_workingtree()
2611
2500
        wt_lca1.lock_read()
2612
2501
        self.addCleanup(wt_lca1.unlock)
2613
 
        wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
 
2502
        wt_lca2 = wt.controldir.sprout('c-tree', 'C-id').open_workingtree()
2614
2503
        wt_lca2.lock_read()
2615
2504
        self.addCleanup(wt_lca2.unlock)
2616
 
        wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
 
2505
        wt_other = wt.controldir.sprout('other', 'F-id').open_workingtree()
2617
2506
        wt_other.lock_read()
2618
2507
        self.addCleanup(wt_other.unlock)
2619
2508
        merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2620
 
                                            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
 
2509
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2621
2510
        entries = list(merge_obj._entries_lca())
2622
2511
        root_id = wt.path2id('')
2623
 
        self.assertEqual([(b'foo-id', True,
2624
 
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
 
2512
        self.assertEqual([('foo-id', True,
2625
2513
                           ((root_id, [root_id, root_id]), root_id, root_id),
2626
2514
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2627
 
                           ((False, [False, False]), False, False),
2628
 
                           False),
2629
 
                          ], entries)
 
2515
                           ((False, [False, False]), False, False)),
 
2516
                         ], entries)
2630
2517
 
2631
2518
    def test_other_reverted_path_to_base(self):
2632
2519
        #   A       Path at 'foo'
2639
2526
        #     |
2640
2527
        #     F     Path at 'foo'
2641
2528
        builder = self.get_builder()
2642
 
        builder.build_snapshot(None,
2643
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2644
 
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2645
 
                               revision_id=b'A-id')
2646
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2647
 
        builder.build_snapshot([b'A-id'],
2648
 
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id')
2649
 
        builder.build_snapshot([b'C-id', b'B-id'],
2650
 
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')  # merge the rename
2651
 
        builder.build_snapshot([b'E-id'],
2652
 
                               [('rename', ('bar', 'foo'))], revision_id=b'F-id')  # Rename back to BASE
2653
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2654
 
        wt, conflicts = self.do_merge(builder, b'F-id')
 
2529
        builder.build_snapshot('A-id', None,
 
2530
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2531
             ('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
 
2532
        builder.build_snapshot('C-id', ['A-id'], [])
 
2533
        builder.build_snapshot('B-id', ['A-id'],
 
2534
            [('rename', ('foo', 'bar'))])
 
2535
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2536
            [('rename', ('foo', 'bar'))]) # merge the rename
 
2537
        builder.build_snapshot('F-id', ['E-id'],
 
2538
            [('rename', ('bar', 'foo'))]) # Rename back to BASE
 
2539
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2540
        wt, conflicts = self.do_merge(builder, 'F-id')
2655
2541
        self.assertEqual(0, conflicts)
2656
 
        self.assertEqual('foo', wt.id2path(b'foo-id'))
 
2542
        self.assertEqual('foo', wt.id2path('foo-id'))
2657
2543
 
2658
2544
    def test_other_reverted_content_to_base(self):
2659
2545
        builder = self.get_builder()
2660
 
        builder.build_snapshot(None,
2661
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2662
 
                                ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2663
 
                               revision_id=b'A-id')
2664
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2665
 
        builder.build_snapshot([b'A-id'],
2666
 
                               [('modify', ('foo', b'B content\n'))],
2667
 
                               revision_id=b'B-id')
2668
 
        builder.build_snapshot([b'C-id', b'B-id'],
2669
 
                               [('modify', ('foo', b'B content\n'))],
2670
 
                               revision_id=b'E-id')  # merge the content
2671
 
        builder.build_snapshot([b'E-id'],
2672
 
                               [('modify', ('foo', b'base content\n'))],
2673
 
                               revision_id=b'F-id')  # Revert back to BASE
2674
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2675
 
        wt, conflicts = self.do_merge(builder, b'F-id')
 
2546
        builder.build_snapshot('A-id', None,
 
2547
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2548
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
 
2549
        builder.build_snapshot('C-id', ['A-id'], [])
 
2550
        builder.build_snapshot('B-id', ['A-id'],
 
2551
            [('modify', ('foo-id', 'B content\n'))])
 
2552
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2553
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
 
2554
        builder.build_snapshot('F-id', ['E-id'],
 
2555
            [('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
 
2556
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2557
        wt, conflicts = self.do_merge(builder, 'F-id')
2676
2558
        self.assertEqual(0, conflicts)
2677
2559
        # TODO: We need to use the per-file graph to properly select a BASE
2678
2560
        #       before this will work. Or at least use the LCA trees to find
2679
2561
        #       the appropriate content base. (which is B, not A).
2680
 
        self.assertEqual(b'base content\n', wt.get_file_text('foo'))
 
2562
        self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2681
2563
 
2682
2564
    def test_other_modified_content(self):
2683
2565
        builder = self.get_builder()
2684
 
        builder.build_snapshot(None,
2685
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2686
 
                                ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2687
 
                               revision_id=b'A-id')
2688
 
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2689
 
        builder.build_snapshot([b'A-id'],
2690
 
                               [('modify', ('foo', b'B content\n'))],
2691
 
                               revision_id=b'B-id')
2692
 
        builder.build_snapshot([b'C-id', b'B-id'],
2693
 
                               [('modify', ('foo', b'B content\n'))],
2694
 
                               revision_id=b'E-id')  # merge the content
2695
 
        builder.build_snapshot([b'E-id'],
2696
 
                               [('modify', ('foo', b'F content\n'))],
2697
 
                               revision_id=b'F-id')  # Override B content
2698
 
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2699
 
        wt, conflicts = self.do_merge(builder, b'F-id')
 
2566
        builder.build_snapshot('A-id', None,
 
2567
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2568
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
 
2569
        builder.build_snapshot('C-id', ['A-id'], [])
 
2570
        builder.build_snapshot('B-id', ['A-id'],
 
2571
            [('modify', ('foo-id', 'B content\n'))])
 
2572
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2573
            [('modify', ('foo-id', 'B content\n'))]) # merge the content
 
2574
        builder.build_snapshot('F-id', ['E-id'],
 
2575
            [('modify', ('foo-id', 'F content\n'))]) # Override B content
 
2576
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
 
2577
        wt, conflicts = self.do_merge(builder, 'F-id')
2700
2578
        self.assertEqual(0, conflicts)
2701
 
        self.assertEqual(b'F content\n', wt.get_file_text('foo'))
 
2579
        self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2702
2580
 
2703
2581
    def test_all_wt(self):
2704
2582
        """Check behavior if all trees are Working Trees."""
2711
2589
        #   |X|
2712
2590
        #   D E E updates content, renames 'b' => 'c'
2713
2591
        builder = self.get_builder()
2714
 
        builder.build_snapshot(None,
2715
 
                               [('add', (u'', b'a-root-id', 'directory', None)),
2716
 
                                ('add', (u'a', b'a-id', 'file', b'base content\n')),
2717
 
                                   ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2718
 
                               revision_id=b'A-id')
2719
 
        builder.build_snapshot([b'A-id'],
2720
 
                               [('modify', ('foo', b'B content\n'))],
2721
 
                               revision_id=b'B-id')
2722
 
        builder.build_snapshot([b'A-id'],
2723
 
                               [('rename', ('a', 'b'))],
2724
 
                               revision_id=b'C-id')
2725
 
        builder.build_snapshot([b'C-id', b'B-id'],
2726
 
                               [('rename', ('b', 'c')),
2727
 
                                ('modify', ('foo', b'E content\n'))],
2728
 
                               revision_id=b'E-id')
2729
 
        builder.build_snapshot([b'B-id', b'C-id'],
2730
 
                               [('rename', ('a', 'b'))], revision_id=b'D-id')  # merged change
 
2592
        builder.build_snapshot('A-id', None,
 
2593
            [('add', (u'', 'a-root-id', 'directory', None)),
 
2594
             ('add', (u'a', 'a-id', 'file', 'base content\n')),
 
2595
             ('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
 
2596
        builder.build_snapshot('B-id', ['A-id'],
 
2597
            [('modify', ('foo-id', 'B content\n'))])
 
2598
        builder.build_snapshot('C-id', ['A-id'],
 
2599
            [('rename', ('a', 'b'))])
 
2600
        builder.build_snapshot('E-id', ['C-id', 'B-id'],
 
2601
            [('rename', ('b', 'c')),
 
2602
             ('modify', ('foo-id', 'E content\n'))])
 
2603
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
 
2604
            [('rename', ('a', 'b'))]) # merged change
2731
2605
        wt_this = self.get_wt_from_builder(builder)
2732
 
        wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
 
2606
        wt_base = wt_this.controldir.sprout('base', 'A-id').open_workingtree()
2733
2607
        wt_base.lock_read()
2734
2608
        self.addCleanup(wt_base.unlock)
2735
 
        wt_lca1 = wt_this.controldir.sprout(
2736
 
            'b-tree', b'B-id').open_workingtree()
 
2609
        wt_lca1 = wt_this.controldir.sprout('b-tree', 'B-id').open_workingtree()
2737
2610
        wt_lca1.lock_read()
2738
2611
        self.addCleanup(wt_lca1.unlock)
2739
 
        wt_lca2 = wt_this.controldir.sprout(
2740
 
            'c-tree', b'C-id').open_workingtree()
 
2612
        wt_lca2 = wt_this.controldir.sprout('c-tree', 'C-id').open_workingtree()
2741
2613
        wt_lca2.lock_read()
2742
2614
        self.addCleanup(wt_lca2.unlock)
2743
 
        wt_other = wt_this.controldir.sprout(
2744
 
            'other', b'E-id').open_workingtree()
 
2615
        wt_other = wt_this.controldir.sprout('other', 'E-id').open_workingtree()
2745
2616
        wt_other.lock_read()
2746
2617
        self.addCleanup(wt_other.unlock)
2747
2618
        merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2748
 
                                            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
 
2619
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2749
2620
        entries = list(merge_obj._entries_lca())
2750
 
        root_id = b'a-root-id'
2751
 
        self.assertEqual([(b'a-id', False,
2752
 
                           ((u'a', [u'a', u'b']), u'c', u'b'),
2753
 
                           ((root_id, [root_id, root_id]), root_id, root_id),
2754
 
                           ((u'a', [u'a', u'b']), u'c', u'b'),
2755
 
                           ((False, [False, False]), False, False),
2756
 
                           False),
2757
 
                          (b'foo-id', True,
2758
 
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2759
 
                           ((root_id, [root_id, root_id]), root_id, root_id),
2760
 
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2761
 
                           ((False, [False, False]), False, False),
2762
 
                           False),
2763
 
                          ], entries)
 
2621
        root_id = 'a-root-id'
 
2622
        self.assertEqual([('a-id', False,
 
2623
                           ((root_id, [root_id, root_id]), root_id, root_id),
 
2624
                           ((u'a', [u'a', u'b']), u'c', u'b'),
 
2625
                           ((False, [False, False]), False, False)),
 
2626
                          ('foo-id', True,
 
2627
                           ((root_id, [root_id, root_id]), root_id, root_id),
 
2628
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
 
2629
                           ((False, [False, False]), False, False)),
 
2630
                         ], entries)
2764
2631
 
2765
2632
    def test_nested_tree_unmodified(self):
2766
2633
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2767
2634
        # 'tree-reference'
2768
2635
        wt = self.make_branch_and_tree('tree',
2769
 
                                       format='development-subtree')
 
2636
            format='development-subtree')
2770
2637
        wt.lock_write()
2771
2638
        self.addCleanup(wt.unlock)
2772
2639
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2773
 
                                             format='development-subtree')
2774
 
        wt.set_root_id(b'a-root-id')
2775
 
        sub_tree.set_root_id(b'sub-tree-root')
2776
 
        self.build_tree_contents([('tree/sub-tree/file', b'text1')])
 
2640
            format='development-subtree')
 
2641
        wt.set_root_id('a-root-id')
 
2642
        sub_tree.set_root_id('sub-tree-root')
 
2643
        self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2777
2644
        sub_tree.add('file')
2778
 
        sub_tree.commit('foo', rev_id=b'sub-A-id')
 
2645
        sub_tree.commit('foo', rev_id='sub-A-id')
2779
2646
        wt.add_reference(sub_tree)
2780
 
        wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
 
2647
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2781
2648
        # Now create a criss-cross merge in the parent, without modifying the
2782
2649
        # subtree
2783
 
        wt.commit('B', rev_id=b'B-id', recursive=None)
2784
 
        wt.set_last_revision(b'A-id')
2785
 
        wt.branch.set_last_revision_info(1, b'A-id')
2786
 
        wt.commit('C', rev_id=b'C-id', recursive=None)
2787
 
        wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2788
 
        wt.commit('E', rev_id=b'E-id', recursive=None)
2789
 
        wt.set_parent_ids([b'B-id', b'C-id'])
2790
 
        wt.branch.set_last_revision_info(2, b'B-id')
2791
 
        wt.commit('D', rev_id=b'D-id', recursive=None)
 
2650
        wt.commit('B', rev_id='B-id', recursive=None)
 
2651
        wt.set_last_revision('A-id')
 
2652
        wt.branch.set_last_revision_info(1, 'A-id')
 
2653
        wt.commit('C', rev_id='C-id', recursive=None)
 
2654
        wt.merge_from_branch(wt.branch, to_revision='B-id')
 
2655
        wt.commit('E', rev_id='E-id', recursive=None)
 
2656
        wt.set_parent_ids(['B-id', 'C-id'])
 
2657
        wt.branch.set_last_revision_info(2, 'B-id')
 
2658
        wt.commit('D', rev_id='D-id', recursive=None)
2792
2659
 
2793
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2660
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2794
2661
        merger.merge_type = _mod_merge.Merge3Merger
2795
2662
        merge_obj = merger.make_merger()
2796
2663
        entries = list(merge_obj._entries_lca())
2800
2667
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2801
2668
        # 'tree-reference'
2802
2669
        wt = self.make_branch_and_tree('tree',
2803
 
                                       format='development-subtree')
 
2670
            format='development-subtree')
2804
2671
        wt.lock_write()
2805
2672
        self.addCleanup(wt.unlock)
2806
2673
        sub_tree = self.make_branch_and_tree('tree/sub',
2807
 
                                             format='development-subtree')
2808
 
        wt.set_root_id(b'a-root-id')
2809
 
        sub_tree.set_root_id(b'sub-tree-root')
2810
 
        self.build_tree_contents([('tree/sub/file', b'text1')])
 
2674
            format='development-subtree')
 
2675
        wt.set_root_id('a-root-id')
 
2676
        sub_tree.set_root_id('sub-tree-root')
 
2677
        self.build_tree_contents([('tree/sub/file', 'text1')])
2811
2678
        sub_tree.add('file')
2812
 
        sub_tree.commit('foo', rev_id=b'sub-A-id')
 
2679
        sub_tree.commit('foo', rev_id='sub-A-id')
2813
2680
        wt.add_reference(sub_tree)
2814
 
        wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
 
2681
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2815
2682
        # Now create a criss-cross merge in the parent, without modifying the
2816
2683
        # subtree
2817
 
        wt.commit('B', rev_id=b'B-id', recursive=None)
2818
 
        wt.set_last_revision(b'A-id')
2819
 
        wt.branch.set_last_revision_info(1, b'A-id')
2820
 
        wt.commit('C', rev_id=b'C-id', recursive=None)
2821
 
        wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2822
 
        self.build_tree_contents([('tree/sub/file', b'text2')])
2823
 
        sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2824
 
        wt.commit('E', rev_id=b'E-id', recursive=None)
2825
 
        wt.set_parent_ids([b'B-id', b'C-id'])
2826
 
        wt.branch.set_last_revision_info(2, b'B-id')
2827
 
        wt.commit('D', rev_id=b'D-id', recursive=None)
 
2684
        wt.commit('B', rev_id='B-id', recursive=None)
 
2685
        wt.set_last_revision('A-id')
 
2686
        wt.branch.set_last_revision_info(1, 'A-id')
 
2687
        wt.commit('C', rev_id='C-id', recursive=None)
 
2688
        wt.merge_from_branch(wt.branch, to_revision='B-id')
 
2689
        self.build_tree_contents([('tree/sub/file', 'text2')])
 
2690
        sub_tree.commit('modify contents', rev_id='sub-B-id')
 
2691
        wt.commit('E', rev_id='E-id', recursive=None)
 
2692
        wt.set_parent_ids(['B-id', 'C-id'])
 
2693
        wt.branch.set_last_revision_info(2, 'B-id')
 
2694
        wt.commit('D', rev_id='D-id', recursive=None)
2828
2695
 
2829
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2696
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2830
2697
        merger.merge_type = _mod_merge.Merge3Merger
2831
2698
        merge_obj = merger.make_merger()
2832
2699
        entries = list(merge_obj._entries_lca())
2838
2705
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2839
2706
        # 'tree-reference'
2840
2707
        wt = self.make_branch_and_tree('tree',
2841
 
                                       format='development-subtree')
 
2708
            format='development-subtree')
2842
2709
        wt.lock_write()
2843
2710
        self.addCleanup(wt.unlock)
2844
2711
        sub_tree = self.make_branch_and_tree('tree/sub',
2845
 
                                             format='development-subtree')
2846
 
        wt.set_root_id(b'a-root-id')
2847
 
        sub_tree.set_root_id(b'sub-tree-root')
2848
 
        self.build_tree_contents([('tree/sub/file', b'text1')])
 
2712
            format='development-subtree')
 
2713
        wt.set_root_id('a-root-id')
 
2714
        sub_tree.set_root_id('sub-tree-root')
 
2715
        self.build_tree_contents([('tree/sub/file', 'text1')])
2849
2716
        sub_tree.add('file')
2850
 
        sub_tree.commit('foo', rev_id=b'sub-A-id')
 
2717
        sub_tree.commit('foo', rev_id='sub-A-id')
2851
2718
        wt.add_reference(sub_tree)
2852
 
        wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
 
2719
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2853
2720
        # Now create a criss-cross merge in the parent, without modifying the
2854
2721
        # subtree
2855
 
        wt.commit('B', rev_id=b'B-id', recursive=None)
2856
 
        wt.set_last_revision(b'A-id')
2857
 
        wt.branch.set_last_revision_info(1, b'A-id')
2858
 
        wt.commit('C', rev_id=b'C-id', recursive=None)
2859
 
        wt.merge_from_branch(wt.branch, to_revision=b'B-id')
 
2722
        wt.commit('B', rev_id='B-id', recursive=None)
 
2723
        wt.set_last_revision('A-id')
 
2724
        wt.branch.set_last_revision_info(1, 'A-id')
 
2725
        wt.commit('C', rev_id='C-id', recursive=None)
 
2726
        wt.merge_from_branch(wt.branch, to_revision='B-id')
2860
2727
        wt.rename_one('sub', 'alt_sub')
2861
 
        wt.commit('E', rev_id=b'E-id', recursive=None)
2862
 
        wt.set_last_revision(b'B-id')
 
2728
        wt.commit('E', rev_id='E-id', recursive=None)
 
2729
        wt.set_last_revision('B-id')
2863
2730
        wt.revert()
2864
 
        wt.set_parent_ids([b'B-id', b'C-id'])
2865
 
        wt.branch.set_last_revision_info(2, b'B-id')
2866
 
        wt.commit('D', rev_id=b'D-id', recursive=None)
 
2731
        wt.set_parent_ids(['B-id', 'C-id'])
 
2732
        wt.branch.set_last_revision_info(2, 'B-id')
 
2733
        wt.commit('D', rev_id='D-id', recursive=None)
2867
2734
 
2868
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2735
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2869
2736
        merger.merge_type = _mod_merge.Merge3Merger
2870
2737
        merge_obj = merger.make_merger()
2871
2738
        entries = list(merge_obj._entries_lca())
2872
 
        root_id = b'a-root-id'
2873
 
        self.assertEqual([(b'sub-tree-root', False,
2874
 
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
 
2739
        root_id = 'a-root-id'
 
2740
        self.assertEqual([('sub-tree-root', False,
2875
2741
                           ((root_id, [root_id, root_id]), root_id, root_id),
2876
2742
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2877
 
                           ((False, [False, False]), False, False),
2878
 
                           False),
2879
 
                          ], entries)
 
2743
                           ((False, [False, False]), False, False)),
 
2744
                         ], entries)
2880
2745
 
2881
2746
    def test_nested_tree_subtree_renamed_and_modified(self):
2882
2747
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2883
2748
        # 'tree-reference'
2884
2749
        wt = self.make_branch_and_tree('tree',
2885
 
                                       format='development-subtree')
 
2750
            format='development-subtree')
2886
2751
        wt.lock_write()
2887
2752
        self.addCleanup(wt.unlock)
2888
2753
        sub_tree = self.make_branch_and_tree('tree/sub',
2889
 
                                             format='development-subtree')
2890
 
        wt.set_root_id(b'a-root-id')
2891
 
        sub_tree.set_root_id(b'sub-tree-root')
2892
 
        self.build_tree_contents([('tree/sub/file', b'text1')])
 
2754
            format='development-subtree')
 
2755
        wt.set_root_id('a-root-id')
 
2756
        sub_tree.set_root_id('sub-tree-root')
 
2757
        self.build_tree_contents([('tree/sub/file', 'text1')])
2893
2758
        sub_tree.add('file')
2894
 
        sub_tree.commit('foo', rev_id=b'sub-A-id')
 
2759
        sub_tree.commit('foo', rev_id='sub-A-id')
2895
2760
        wt.add_reference(sub_tree)
2896
 
        wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
 
2761
        wt.commit('set text to 1', rev_id='A-id', recursive=None)
2897
2762
        # Now create a criss-cross merge in the parent, without modifying the
2898
2763
        # subtree
2899
 
        wt.commit('B', rev_id=b'B-id', recursive=None)
2900
 
        wt.set_last_revision(b'A-id')
2901
 
        wt.branch.set_last_revision_info(1, b'A-id')
2902
 
        wt.commit('C', rev_id=b'C-id', recursive=None)
2903
 
        wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2904
 
        self.build_tree_contents([('tree/sub/file', b'text2')])
2905
 
        sub_tree.commit('modify contents', rev_id=b'sub-B-id')
 
2764
        wt.commit('B', rev_id='B-id', recursive=None)
 
2765
        wt.set_last_revision('A-id')
 
2766
        wt.branch.set_last_revision_info(1, 'A-id')
 
2767
        wt.commit('C', rev_id='C-id', recursive=None)
 
2768
        wt.merge_from_branch(wt.branch, to_revision='B-id')
 
2769
        self.build_tree_contents([('tree/sub/file', 'text2')])
 
2770
        sub_tree.commit('modify contents', rev_id='sub-B-id')
2906
2771
        wt.rename_one('sub', 'alt_sub')
2907
 
        wt.commit('E', rev_id=b'E-id', recursive=None)
2908
 
        wt.set_last_revision(b'B-id')
 
2772
        wt.commit('E', rev_id='E-id', recursive=None)
 
2773
        wt.set_last_revision('B-id')
2909
2774
        wt.revert()
2910
 
        wt.set_parent_ids([b'B-id', b'C-id'])
2911
 
        wt.branch.set_last_revision_info(2, b'B-id')
2912
 
        wt.commit('D', rev_id=b'D-id', recursive=None)
 
2775
        wt.set_parent_ids(['B-id', 'C-id'])
 
2776
        wt.branch.set_last_revision_info(2, 'B-id')
 
2777
        wt.commit('D', rev_id='D-id', recursive=None)
2913
2778
 
2914
 
        merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
 
2779
        merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2915
2780
        merger.merge_type = _mod_merge.Merge3Merger
2916
2781
        merge_obj = merger.make_merger()
2917
2782
        entries = list(merge_obj._entries_lca())
2918
 
        root_id = b'a-root-id'
2919
 
        self.assertEqual([(b'sub-tree-root', False,
2920
 
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
 
2783
        root_id = 'a-root-id'
 
2784
        self.assertEqual([('sub-tree-root', False,
2921
2785
                           ((root_id, [root_id, root_id]), root_id, root_id),
2922
2786
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2923
 
                           ((False, [False, False]), False, False),
2924
 
                           False),
2925
 
                          ], entries)
 
2787
                           ((False, [False, False]), False, False)),
 
2788
                         ], entries)
2926
2789
 
2927
2790
 
2928
2791
class TestLCAMultiWay(tests.TestCase):
2930
2793
    def assertLCAMultiWay(self, expected, base, lcas, other, this,
2931
2794
                          allow_overriding_lca=True):
2932
2795
        self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2933
 
            (base, lcas), other, this,
2934
 
            allow_overriding_lca=allow_overriding_lca))
 
2796
                                (base, lcas), other, this,
 
2797
                                allow_overriding_lca=allow_overriding_lca))
2935
2798
 
2936
2799
    def test_other_equal_equal_lcas(self):
2937
2800
        """Test when OTHER=LCA and all LCAs are identical."""
2938
2801
        self.assertLCAMultiWay('this',
2939
 
                               'bval', ['bval', 'bval'], 'bval', 'bval')
2940
 
        self.assertLCAMultiWay('this',
2941
 
                               'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2942
 
        self.assertLCAMultiWay('this',
2943
 
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2944
 
        self.assertLCAMultiWay('this',
2945
 
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2946
 
        self.assertLCAMultiWay('this',
2947
 
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
 
2802
            'bval', ['bval', 'bval'], 'bval', 'bval')
 
2803
        self.assertLCAMultiWay('this',
 
2804
            'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
 
2805
        self.assertLCAMultiWay('this',
 
2806
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
 
2807
        self.assertLCAMultiWay('this',
 
2808
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
 
2809
        self.assertLCAMultiWay('this',
 
2810
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2948
2811
 
2949
2812
    def test_other_equal_this(self):
2950
2813
        """Test when other and this are identical."""
2951
2814
        self.assertLCAMultiWay('this',
2952
 
                               'bval', ['bval', 'bval'], 'oval', 'oval')
2953
 
        self.assertLCAMultiWay('this',
2954
 
                               'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2955
 
        self.assertLCAMultiWay('this',
2956
 
                               'bval', ['cval', 'dval'], 'oval', 'oval')
2957
 
        self.assertLCAMultiWay('this',
2958
 
                               'bval', [None, 'lcaval'], 'oval', 'oval')
2959
 
        self.assertLCAMultiWay('this',
2960
 
                               None, [None, 'lcaval'], 'oval', 'oval')
2961
 
        self.assertLCAMultiWay('this',
2962
 
                               None, ['lcaval', 'lcaval'], 'oval', 'oval')
2963
 
        self.assertLCAMultiWay('this',
2964
 
                               None, ['cval', 'dval'], 'oval', 'oval')
2965
 
        self.assertLCAMultiWay('this',
2966
 
                               None, ['cval', 'dval'], None, None)
2967
 
        self.assertLCAMultiWay('this',
2968
 
                               None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
 
2815
            'bval', ['bval', 'bval'], 'oval', 'oval')
 
2816
        self.assertLCAMultiWay('this',
 
2817
            'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
 
2818
        self.assertLCAMultiWay('this',
 
2819
            'bval', ['cval', 'dval'], 'oval', 'oval')
 
2820
        self.assertLCAMultiWay('this',
 
2821
            'bval', [None, 'lcaval'], 'oval', 'oval')
 
2822
        self.assertLCAMultiWay('this',
 
2823
            None, [None, 'lcaval'], 'oval', 'oval')
 
2824
        self.assertLCAMultiWay('this',
 
2825
            None, ['lcaval', 'lcaval'], 'oval', 'oval')
 
2826
        self.assertLCAMultiWay('this',
 
2827
            None, ['cval', 'dval'], 'oval', 'oval')
 
2828
        self.assertLCAMultiWay('this',
 
2829
            None, ['cval', 'dval'], None, None)
 
2830
        self.assertLCAMultiWay('this',
 
2831
            None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2969
2832
 
2970
2833
    def test_no_lcas(self):
2971
2834
        self.assertLCAMultiWay('this',
2972
 
                               'bval', [], 'bval', 'tval')
 
2835
            'bval', [], 'bval', 'tval')
2973
2836
        self.assertLCAMultiWay('other',
2974
 
                               'bval', [], 'oval', 'bval')
 
2837
            'bval', [], 'oval', 'bval')
2975
2838
        self.assertLCAMultiWay('conflict',
2976
 
                               'bval', [], 'oval', 'tval')
 
2839
            'bval', [], 'oval', 'tval')
2977
2840
        self.assertLCAMultiWay('this',
2978
 
                               'bval', [], 'oval', 'oval')
 
2841
            'bval', [], 'oval', 'oval')
2979
2842
 
2980
2843
    def test_lca_supersedes_other_lca(self):
2981
2844
        """If one lca == base, the other lca takes precedence"""
2982
2845
        self.assertLCAMultiWay('this',
2983
 
                               'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
 
2846
            'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2984
2847
        self.assertLCAMultiWay('this',
2985
 
                               'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
 
2848
            'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2986
2849
        # This is actually considered a 'revert' because the 'lcaval' in LCAS
2987
2850
        # supersedes the BASE val (in the other LCA) but then OTHER reverts it
2988
2851
        # back to bval.
2989
2852
        self.assertLCAMultiWay('other',
2990
 
                               'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
 
2853
            'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2991
2854
        self.assertLCAMultiWay('conflict',
2992
 
                               'bval', ['bval', 'lcaval'], 'bval', 'tval')
 
2855
            'bval', ['bval', 'lcaval'], 'bval', 'tval')
2993
2856
 
2994
2857
    def test_other_and_this_pick_different_lca(self):
2995
2858
        # OTHER and THIS resolve the lca conflict in different ways
2996
2859
        self.assertLCAMultiWay('conflict',
2997
 
                               'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2998
 
        self.assertLCAMultiWay('conflict',
2999
 
                               'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
3000
 
        self.assertLCAMultiWay('conflict',
3001
 
                               'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
 
2860
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
 
2861
        self.assertLCAMultiWay('conflict',
 
2862
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
 
2863
        self.assertLCAMultiWay('conflict',
 
2864
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
3002
2865
 
3003
2866
    def test_other_in_lca(self):
3004
2867
        # OTHER takes a value of one of the LCAs, THIS takes a new value, which
3005
2868
        # theoretically supersedes both LCA values and 'wins'
3006
 
        self.assertLCAMultiWay(
3007
 
            'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
3008
 
        self.assertLCAMultiWay(
3009
 
            'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3010
 
            'newval')
3011
 
        self.assertLCAMultiWay('conflict',
3012
 
                               'bval', ['lca1val',
3013
 
                                        'lca2val'], 'lca1val', 'newval',
3014
 
                               allow_overriding_lca=False)
3015
 
        self.assertLCAMultiWay('conflict',
3016
 
                               'bval', ['lca1val', 'lca2val',
3017
 
                                        'lca3val'], 'lca1val', 'newval',
3018
 
                               allow_overriding_lca=False)
 
2869
        self.assertLCAMultiWay('this',
 
2870
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
 
2871
        self.assertLCAMultiWay('this',
 
2872
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
 
2873
        self.assertLCAMultiWay('conflict',
 
2874
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
 
2875
            allow_overriding_lca=False)
 
2876
        self.assertLCAMultiWay('conflict',
 
2877
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
 
2878
            allow_overriding_lca=False)
3019
2879
        # THIS reverted back to BASE, but that is an explicit supersede of all
3020
2880
        # LCAs
3021
 
        self.assertLCAMultiWay(
3022
 
            'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3023
 
            'bval')
3024
 
        self.assertLCAMultiWay(
3025
 
            'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3026
 
        self.assertLCAMultiWay('conflict',
3027
 
                               'bval', ['lca1val', 'lca2val',
3028
 
                                        'lca3val'], 'lca1val', 'bval',
3029
 
                               allow_overriding_lca=False)
3030
 
        self.assertLCAMultiWay('conflict',
3031
 
                               'bval', ['lca1val', 'lca2val',
3032
 
                                        'bval'], 'lca1val', 'bval',
3033
 
                               allow_overriding_lca=False)
 
2881
        self.assertLCAMultiWay('this',
 
2882
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
 
2883
        self.assertLCAMultiWay('this',
 
2884
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
 
2885
        self.assertLCAMultiWay('conflict',
 
2886
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
 
2887
            allow_overriding_lca=False)
 
2888
        self.assertLCAMultiWay('conflict',
 
2889
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
 
2890
            allow_overriding_lca=False)
3034
2891
 
3035
2892
    def test_this_in_lca(self):
3036
2893
        # THIS takes a value of one of the LCAs, OTHER takes a new value, which
3037
2894
        # theoretically supersedes both LCA values and 'wins'
3038
 
        self.assertLCAMultiWay(
3039
 
            'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3040
 
        self.assertLCAMultiWay(
3041
 
            'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3042
 
        self.assertLCAMultiWay('conflict',
3043
 
                               'bval', ['lca1val',
3044
 
                                        'lca2val'], 'oval', 'lca1val',
3045
 
                               allow_overriding_lca=False)
3046
 
        self.assertLCAMultiWay('conflict',
3047
 
                               'bval', ['lca1val',
3048
 
                                        'lca2val'], 'oval', 'lca2val',
3049
 
                               allow_overriding_lca=False)
 
2895
        self.assertLCAMultiWay('other',
 
2896
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
 
2897
        self.assertLCAMultiWay('other',
 
2898
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
 
2899
        self.assertLCAMultiWay('conflict',
 
2900
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
 
2901
            allow_overriding_lca=False)
 
2902
        self.assertLCAMultiWay('conflict',
 
2903
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
 
2904
            allow_overriding_lca=False)
3050
2905
        # OTHER reverted back to BASE, but that is an explicit supersede of all
3051
2906
        # LCAs
3052
 
        self.assertLCAMultiWay(
3053
 
            'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
3054
 
            'lca3val')
3055
 
        self.assertLCAMultiWay(
3056
 
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
3057
 
            'bval', 'lca3val', allow_overriding_lca=False)
 
2907
        self.assertLCAMultiWay('other',
 
2908
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
 
2909
        self.assertLCAMultiWay('conflict',
 
2910
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
 
2911
            allow_overriding_lca=False)
3058
2912
 
3059
2913
    def test_all_differ(self):
3060
 
        self.assertLCAMultiWay(
3061
 
            'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3062
 
        self.assertLCAMultiWay(
3063
 
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
3064
 
            'tval')
3065
 
        self.assertLCAMultiWay(
3066
 
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
3067
 
            'tval')
 
2914
        self.assertLCAMultiWay('conflict',
 
2915
            'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
 
2916
        self.assertLCAMultiWay('conflict',
 
2917
            'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
 
2918
        self.assertLCAMultiWay('conflict',
 
2919
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
3068
2920
 
3069
2921
 
3070
2922
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3108
2960
        factory = self.get_merger_factory()
3109
2961
        self._install_hook(factory)
3110
2962
        builder = self.make_builder()
3111
 
        builder.add_file(b'bar-id', builder.tree_root,
3112
 
                         file_name, b'text1', True)
3113
 
        builder.change_contents(b'bar-id', other=b'text4', this=b'text3')
 
2963
        builder.add_file('bar-id', builder.tree_root, file_name, 'text1', True)
 
2964
        builder.change_contents('bar-id', other='text4', this='text3')
3114
2965
        return builder
3115
2966
 
3116
2967
    def make_kind_change(self):
3117
2968
        factory = self.get_merger_factory()
3118
2969
        self._install_hook(factory)
3119
2970
        builder = self.make_builder()
3120
 
        builder.add_file(b'bar-id', builder.tree_root, 'bar', b'text1', True,
 
2971
        builder.add_file('bar-id', builder.tree_root, 'bar', 'text1', True,
3121
2972
                         this=False)
3122
 
        builder.add_dir(b'bar-dir', builder.tree_root, 'bar-id',
 
2973
        builder.add_dir('bar-dir', builder.tree_root, 'bar-id',
3123
2974
                        base=False, other=False)
3124
2975
        return builder
3125
2976
 
3126
2977
    def test_uses_this_branch(self):
3127
2978
        builder = self.make_text_conflict()
3128
 
        with builder.make_preview_transform() as tt:
3129
 
            pass
 
2979
        tt = builder.make_preview_transform()
 
2980
        self.addCleanup(tt.finalize)
3130
2981
 
3131
2982
    def test_affected_files_cached(self):
3132
2983
        """Ensures that the config variable is cached"""
3138
2989
 
3139
2990
    def test_hook_called_for_text_conflicts(self):
3140
2991
        builder = self.make_text_conflict()
3141
 
        builder.merge()
 
2992
        conflicts = builder.merge()
3142
2993
        # The hook should call the merge_text() method
3143
2994
        self.assertEqual(['merge_text'], self.calls)
3144
2995
 
3145
2996
    def test_hook_not_called_for_kind_change(self):
3146
2997
        builder = self.make_kind_change()
3147
 
        builder.merge()
 
2998
        conflicts = builder.merge()
3148
2999
        # The hook should not call the merge_text() method
3149
3000
        self.assertEqual([], self.calls)
3150
3001
 
3151
3002
    def test_hook_not_called_for_other_files(self):
3152
3003
        builder = self.make_text_conflict('foobar')
3153
 
        builder.merge()
 
3004
        conflicts = builder.merge()
3154
3005
        # The hook should not call the merge_text() method
3155
3006
        self.assertEqual([], self.calls)
3156
3007
 
3159
3010
 
3160
3011
    def setup_simple_branch(self, relpath, shape=None, root_id=None):
3161
3012
        """One commit, containing tree specified by optional shape.
3162
 
 
 
3013
        
3163
3014
        Default is empty tree (just root entry).
3164
3015
        """
3165
3016
        if root_id is None:
3166
 
            root_id = b'%s-root-id' % (relpath.encode('ascii'),)
 
3017
            root_id = '%s-root-id' % (relpath,)
3167
3018
        wt = self.make_branch_and_tree(relpath)
3168
3019
        wt.set_root_id(root_id)
3169
3020
        if shape is not None:
3170
3021
            adjusted_shape = [relpath + '/' + elem for elem in shape]
3171
3022
            self.build_tree(adjusted_shape)
3172
 
            ids = [
3173
 
                (b'%s-%s-id' % (relpath.encode('utf-8'),
3174
 
                                basename(elem.rstrip('/')).encode('ascii')))
3175
 
                for elem in shape]
 
3023
            ids = ['%s-%s-id' % (relpath, basename(elem.rstrip('/')))
 
3024
                   for elem in shape]
3176
3025
            wt.add(shape, ids=ids)
3177
 
        rev_id = b'r1-%s' % (relpath.encode('utf-8'),)
 
3026
        rev_id = 'r1-%s' % (relpath,)
3178
3027
        wt.commit("Initial commit of %s" % (relpath,), rev_id=rev_id)
3179
3028
        self.assertEqual(root_id, wt.path2id(''))
3180
3029
        return wt
3195
3044
 
3196
3045
    def do_merge_into(self, location, merge_as):
3197
3046
        """Helper for using MergeIntoMerger.
3198
 
 
 
3047
        
3199
3048
        :param location: location of directory to merge from, either the
3200
3049
            location of a branch or of a path inside a branch.
3201
3050
        :param merge_as: the path in a tree to add the new directory as.
3202
3051
        :returns: the conflicts from 'do_merge'.
3203
3052
        """
3204
 
        with contextlib.ExitStack() as stack:
3205
 
            # Open and lock the various tree and branch objects
3206
 
            wt, subdir_relpath = WorkingTree.open_containing(merge_as)
3207
 
            stack.enter_context(wt.lock_write())
3208
 
            branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
3209
 
                location)
3210
 
            stack.enter_context(branch_to_merge.lock_read())
3211
 
            other_tree = branch_to_merge.basis_tree()
3212
 
            stack.enter_context(other_tree.lock_read())
3213
 
            # Perform the merge
3214
 
            merger = _mod_merge.MergeIntoMerger(
3215
 
                this_tree=wt, other_tree=other_tree, other_branch=branch_to_merge,
3216
 
                target_subdir=subdir_relpath, source_subpath=subdir_to_merge)
3217
 
            merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3218
 
            conflicts = merger.do_merge()
3219
 
            merger.set_pending()
3220
 
            return conflicts
 
3053
        operation = cleanup.OperationWithCleanups(self._merge_into)
 
3054
        return operation.run(location, merge_as)
 
3055
 
 
3056
    def _merge_into(self, op, location, merge_as):
 
3057
        # Open and lock the various tree and branch objects
 
3058
        wt, subdir_relpath = WorkingTree.open_containing(merge_as)
 
3059
        op.add_cleanup(wt.lock_write().unlock)
 
3060
        branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
 
3061
            location)
 
3062
        op.add_cleanup(branch_to_merge.lock_read().unlock)
 
3063
        other_tree = branch_to_merge.basis_tree()
 
3064
        op.add_cleanup(other_tree.lock_read().unlock)
 
3065
        # Perform the merge
 
3066
        merger = _mod_merge.MergeIntoMerger(this_tree=wt, other_tree=other_tree,
 
3067
            other_branch=branch_to_merge, target_subdir=subdir_relpath,
 
3068
            source_subpath=subdir_to_merge)
 
3069
        merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
 
3070
        conflicts = merger.do_merge()
 
3071
        merger.set_pending()
 
3072
        return conflicts
3221
3073
 
3222
3074
    def assertTreeEntriesEqual(self, expected_entries, tree):
3223
3075
        """Assert that 'tree' contains the expected inventory entries.
3237
3089
        project_wt.lock_read()
3238
3090
        self.addCleanup(project_wt.unlock)
3239
3091
        # The r1-lib1 revision should be merged into this one
3240
 
        self.assertEqual([b'r1-project', b'r1-lib1'],
3241
 
                         project_wt.get_parent_ids())
 
3092
        self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3242
3093
        self.assertTreeEntriesEqual(
3243
 
            [('', b'project-root-id'),
3244
 
             ('README', b'project-README-id'),
3245
 
             ('dir', b'project-dir-id'),
3246
 
             ('lib1', b'lib1-root-id'),
3247
 
             ('dir/file.c', b'project-file.c-id'),
3248
 
             ('lib1/Makefile', b'lib1-Makefile-id'),
3249
 
             ('lib1/README', b'lib1-README-id'),
3250
 
             ('lib1/foo.c', b'lib1-foo.c-id'),
3251
 
             ], project_wt)
 
3094
            [('', 'project-root-id'),
 
3095
             ('README', 'project-README-id'),
 
3096
             ('dir', 'project-dir-id'),
 
3097
             ('lib1', 'lib1-root-id'),
 
3098
             ('dir/file.c', 'project-file.c-id'),
 
3099
             ('lib1/Makefile', 'lib1-Makefile-id'),
 
3100
             ('lib1/README', 'lib1-README-id'),
 
3101
             ('lib1/foo.c', 'lib1-foo.c-id'),
 
3102
            ], project_wt)
3252
3103
 
3253
3104
    def test_subdir(self):
3254
3105
        """Merge a branch into a subdirectory of an existing directory."""
3257
3108
        project_wt.lock_read()
3258
3109
        self.addCleanup(project_wt.unlock)
3259
3110
        # The r1-lib1 revision should be merged into this one
3260
 
        self.assertEqual([b'r1-project', b'r1-lib1'],
3261
 
                         project_wt.get_parent_ids())
 
3111
        self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3262
3112
        self.assertTreeEntriesEqual(
3263
 
            [('', b'project-root-id'),
3264
 
             ('README', b'project-README-id'),
3265
 
             ('dir', b'project-dir-id'),
3266
 
             ('dir/file.c', b'project-file.c-id'),
3267
 
             ('dir/lib1', b'lib1-root-id'),
3268
 
             ('dir/lib1/Makefile', b'lib1-Makefile-id'),
3269
 
             ('dir/lib1/README', b'lib1-README-id'),
3270
 
             ('dir/lib1/foo.c', b'lib1-foo.c-id'),
3271
 
             ], project_wt)
 
3113
            [('', 'project-root-id'),
 
3114
             ('README', 'project-README-id'),
 
3115
             ('dir', 'project-dir-id'),
 
3116
             ('dir/file.c', 'project-file.c-id'),
 
3117
             ('dir/lib1', 'lib1-root-id'),
 
3118
             ('dir/lib1/Makefile', 'lib1-Makefile-id'),
 
3119
             ('dir/lib1/README', 'lib1-README-id'),
 
3120
             ('dir/lib1/foo.c', 'lib1-foo.c-id'),
 
3121
            ], project_wt)
3272
3122
 
3273
3123
    def test_newdir_with_repeat_roots(self):
3274
3124
        """If the file-id of the dir to be merged already exists a new ID will
3280
3130
        project_wt.lock_read()
3281
3131
        self.addCleanup(project_wt.unlock)
3282
3132
        # The r1-lib1 revision should be merged into this one
3283
 
        self.assertEqual([b'r1-project', b'r1-lib1'],
3284
 
                         project_wt.get_parent_ids())
 
3133
        self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3285
3134
        new_lib1_id = project_wt.path2id('lib1')
3286
3135
        self.assertNotEqual(None, new_lib1_id)
3287
3136
        self.assertTreeEntriesEqual(
3288
3137
            [('', root_id),
3289
 
             ('README', b'project-README-id'),
3290
 
             ('dir', b'project-dir-id'),
 
3138
             ('README', 'project-README-id'),
 
3139
             ('dir', 'project-dir-id'),
3291
3140
             ('lib1', new_lib1_id),
3292
 
             ('dir/file.c', b'project-file.c-id'),
3293
 
             ('lib1/Makefile', b'lib1-Makefile-id'),
3294
 
             ('lib1/README', b'lib1-README-id'),
3295
 
             ('lib1/foo.c', b'lib1-foo.c-id'),
3296
 
             ], project_wt)
 
3141
             ('dir/file.c', 'project-file.c-id'),
 
3142
             ('lib1/Makefile', 'lib1-Makefile-id'),
 
3143
             ('lib1/README', 'lib1-README-id'),
 
3144
             ('lib1/foo.c', 'lib1-foo.c-id'),
 
3145
            ], project_wt)
3297
3146
 
3298
3147
    def test_name_conflict(self):
3299
3148
        """When the target directory name already exists a conflict is
3300
3149
        generated and the original directory is renamed to foo.moved.
3301
3150
        """
3302
3151
        dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3303
 
        self.setup_simple_branch('src', ['README'])
 
3152
        src_wt = self.setup_simple_branch('src', ['README'])
3304
3153
        conflicts = self.do_merge_into('src', 'dest/dir')
3305
3154
        self.assertEqual(1, conflicts)
3306
3155
        dest_wt.lock_read()
3307
3156
        self.addCleanup(dest_wt.unlock)
3308
3157
        # The r1-lib1 revision should be merged into this one
3309
 
        self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
 
3158
        self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3310
3159
        self.assertTreeEntriesEqual(
3311
 
            [('', b'dest-root-id'),
3312
 
             ('dir', b'src-root-id'),
3313
 
             ('dir.moved', b'dest-dir-id'),
3314
 
             ('dir/README', b'src-README-id'),
3315
 
             ('dir.moved/file.txt', b'dest-file.txt-id'),
3316
 
             ], dest_wt)
 
3160
            [('', 'dest-root-id'),
 
3161
             ('dir', 'src-root-id'),
 
3162
             ('dir.moved', 'dest-dir-id'),
 
3163
             ('dir/README', 'src-README-id'),
 
3164
             ('dir.moved/file.txt', 'dest-file.txt-id'),
 
3165
            ], dest_wt)
3317
3166
 
3318
3167
    def test_file_id_conflict(self):
3319
3168
        """A conflict is generated if the merge-into adds a file (or other
3320
3169
        inventory entry) with a file-id that already exists in the target tree.
3321
3170
        """
3322
 
        self.setup_simple_branch('dest', ['file.txt'])
 
3171
        dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3323
3172
        # Make a second tree with a file-id that will clash with file.txt in
3324
3173
        # dest.
3325
3174
        src_wt = self.make_branch_and_tree('src')
3326
3175
        self.build_tree(['src/README'])
3327
 
        src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3328
 
        src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
 
3176
        src_wt.add(['README'], ids=['dest-file.txt-id'])
 
3177
        src_wt.commit("Rev 1 of src.", rev_id='r1-src')
3329
3178
        conflicts = self.do_merge_into('src', 'dest/dir')
3330
3179
        # This is an edge case that shouldn't happen to users very often.  So
3331
3180
        # we don't care really about the exact presentation of the conflict,
3337
3186
        subtree.
3338
3187
        """
3339
3188
        dest_wt = self.setup_simple_branch('dest')
3340
 
        self.setup_simple_branch('src', ['hello.txt', 'dir/', 'dir/foo.c'])
3341
 
        self.do_merge_into('src/dir', 'dest/dir')
 
3189
        src_wt = self.setup_simple_branch(
 
3190
            'src', ['hello.txt', 'dir/', 'dir/foo.c'])
 
3191
        conflicts = self.do_merge_into('src/dir', 'dest/dir')
3342
3192
        dest_wt.lock_read()
3343
3193
        self.addCleanup(dest_wt.unlock)
3344
3194
        # The r1-lib1 revision should NOT be merged into this one (this is a
3345
3195
        # partial merge).
3346
 
        self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
 
3196
        self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3347
3197
        self.assertTreeEntriesEqual(
3348
 
            [('', b'dest-root-id'),
3349
 
             ('dir', b'src-dir-id'),
3350
 
             ('dir/foo.c', b'src-foo.c-id'),
3351
 
             ], dest_wt)
 
3198
            [('', 'dest-root-id'),
 
3199
             ('dir', 'src-dir-id'),
 
3200
             ('dir/foo.c', 'src-foo.c-id'),
 
3201
            ], dest_wt)
3352
3202
 
3353
3203
    def test_only_file(self):
3354
3204
        """An edge case: merge just one file, not a whole dir."""
3355
3205
        dest_wt = self.setup_simple_branch('dest')
3356
 
        self.setup_simple_branch('two-file', ['file1.txt', 'file2.txt'])
3357
 
        self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
 
3206
        two_file_wt = self.setup_simple_branch(
 
3207
            'two-file', ['file1.txt', 'file2.txt'])
 
3208
        conflicts = self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3358
3209
        dest_wt.lock_read()
3359
3210
        self.addCleanup(dest_wt.unlock)
3360
3211
        # The r1-lib1 revision should NOT be merged into this one
3361
 
        self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
 
3212
        self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3362
3213
        self.assertTreeEntriesEqual(
3363
 
            [('', b'dest-root-id'), ('file1.txt', b'two-file-file1.txt-id')],
 
3214
            [('', 'dest-root-id'), ('file1.txt', 'two-file-file1.txt-id')],
3364
3215
            dest_wt)
3365
3216
 
3366
3217
    def test_no_such_source_path(self):
3368
3219
        does not exist.
3369
3220
        """
3370
3221
        dest_wt = self.setup_simple_branch('dest')
3371
 
        self.setup_simple_branch('src', ['dir/'])
 
3222
        two_file_wt = self.setup_simple_branch('src', ['dir/'])
3372
3223
        self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3373
 
                          'src/no-such-dir', 'dest/foo')
 
3224
            'src/no-such-dir', 'dest/foo')
3374
3225
        dest_wt.lock_read()
3375
3226
        self.addCleanup(dest_wt.unlock)
3376
3227
        # The dest tree is unmodified.
3377
 
        self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3378
 
        self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
 
3228
        self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
 
3229
        self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3379
3230
 
3380
3231
    def test_no_such_target_path(self):
3381
3232
        """PathNotInTree is also raised if the specified path in the target
3382
3233
        tree does not exist.
3383
3234
        """
3384
3235
        dest_wt = self.setup_simple_branch('dest')
3385
 
        self.setup_simple_branch('src', ['file.txt'])
 
3236
        two_file_wt = self.setup_simple_branch('src', ['file.txt'])
3386
3237
        self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3387
 
                          'src', 'dest/no-such-dir/foo')
 
3238
            'src', 'dest/no-such-dir/foo')
3388
3239
        dest_wt.lock_read()
3389
3240
        self.addCleanup(dest_wt.unlock)
3390
3241
        # The dest tree is unmodified.
3391
 
        self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3392
 
        self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
 
3242
        self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
 
3243
        self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3393
3244
 
3394
3245
 
3395
3246
class TestMergeHooks(TestCaseWithTransport):
3397
3248
    def setUp(self):
3398
3249
        super(TestMergeHooks, self).setUp()
3399
3250
        self.tree_a = self.make_branch_and_tree('tree_a')
3400
 
        self.build_tree_contents([('tree_a/file', b'content_1')])
3401
 
        self.tree_a.add('file', b'file-id')
 
3251
        self.build_tree_contents([('tree_a/file', 'content_1')])
 
3252
        self.tree_a.add('file', 'file-id')
3402
3253
        self.tree_a.commit('added file')
3403
3254
 
3404
 
        self.tree_b = self.tree_a.controldir.sprout(
3405
 
            'tree_b').open_workingtree()
3406
 
        self.build_tree_contents([('tree_b/file', b'content_2')])
 
3255
        self.tree_b = self.tree_a.controldir.sprout('tree_b').open_workingtree()
 
3256
        self.build_tree_contents([('tree_b/file', 'content_2')])
3407
3257
        self.tree_b.commit('modify file')
3408
3258
 
3409
3259
    def test_pre_merge_hook_inject_different_tree(self):
3410
3260
        tree_c = self.tree_b.controldir.sprout('tree_c').open_workingtree()
3411
 
        self.build_tree_contents([('tree_c/file', b'content_3')])
 
3261
        self.build_tree_contents([('tree_c/file', 'content_3')])
3412
3262
        tree_c.commit("more content")
3413
3263
        calls = []
3414
 
 
3415
3264
        def factory(merger):
3416
3265
            self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3417
3266
            merger.other_tree = tree_c
3420
3269
                                                   factory, 'test factory')
3421
3270
        self.tree_a.merge_from_branch(self.tree_b.branch)
3422
3271
 
3423
 
        self.assertFileEqual(b"content_3", 'tree_a/file')
 
3272
        self.assertFileEqual("content_3", 'tree_a/file')
3424
3273
        self.assertLength(1, calls)
3425
3274
 
3426
3275
    def test_post_merge_hook_called(self):
3427
3276
        calls = []
3428
 
 
3429
3277
        def factory(merger):
3430
3278
            self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3431
3279
            calls.append(merger)
3434
3282
 
3435
3283
        self.tree_a.merge_from_branch(self.tree_b.branch)
3436
3284
 
3437
 
        self.assertFileEqual(b"content_2", 'tree_a/file')
 
3285
        self.assertFileEqual("content_2", 'tree_a/file')
3438
3286
        self.assertLength(1, calls)