/breezy/unstable

To get this branch, use:
bzr branch https://code.breezy-vcs.org/breezy/unstable

« back to all changes in this revision

Viewing changes to breezy/tests/per_repository/test_commit_builder.py

Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
        branch.repository.commit_write_group()
48
48
        branch.repository.unlock()
49
49
 
50
 
    def record_root(self, builder, tree):
51
 
        if builder.record_root_entry is True:
52
 
            tree.lock_read()
53
 
            try:
54
 
                ie = tree.root_inventory.root
55
 
            finally:
56
 
                tree.unlock()
57
 
            parent_tree = tree.branch.repository.revision_tree(
58
 
                              _mod_revision.NULL_REVISION)
59
 
            parent_invs = []
60
 
            builder.record_entry_contents(ie, parent_invs, '', tree,
61
 
                tree.path_content_summary(''))
62
 
 
63
 
    def test_finish_inventory_with_record_root(self):
64
 
        tree = self.make_branch_and_tree(".")
65
 
        tree.lock_write()
66
 
        try:
67
 
            builder = tree.branch.get_commit_builder([])
68
 
            if not builder.supports_record_entry_contents:
69
 
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
70
 
                    "record_entry_contents")
71
 
            repo = tree.branch.repository
72
 
            self.record_root(builder, tree)
73
 
            builder.finish_inventory()
74
 
            repo.commit_write_group()
75
 
        finally:
76
 
            tree.unlock()
77
 
 
78
50
    def test_finish_inventory_record_iter_changes(self):
79
51
        tree = self.make_branch_and_tree(".")
80
52
        tree.lock_write()
92
64
        finally:
93
65
            tree.unlock()
94
66
 
95
 
    def test_abort_record_entry_contents(self):
96
 
        tree = self.make_branch_and_tree(".")
97
 
        tree.lock_write()
98
 
        try:
99
 
            builder = tree.branch.get_commit_builder([])
100
 
            if not builder.supports_record_entry_contents:
101
 
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
102
 
                    "record_entry_contents")
103
 
            self.record_root(builder, tree)
104
 
            builder.finish_inventory()
105
 
            builder.abort()
106
 
        finally:
107
 
            tree.unlock()
108
 
 
109
67
    def test_abort_record_iter_changes(self):
110
68
        tree = self.make_branch_and_tree(".")
111
69
        tree.lock_write()
165
123
        actually_updated_branch = (tree.branch.last_revision() == rev_id)
166
124
        self.assertEqual(actually_updated_branch, will_update_branch)
167
125
 
168
 
    def test_commit_with_revision_id_record_entry_contents(self):
169
 
        tree = self.make_branch_and_tree(".")
170
 
        tree.lock_write()
171
 
        try:
172
 
            # use a unicode revision id to test more corner cases.
173
 
            # The repository layer is meant to handle this.
174
 
            revision_id = u'\xc8abc'.encode('utf8')
175
 
            try:
176
 
                try:
177
 
                    builder = tree.branch.get_commit_builder([],
178
 
                        revision_id=revision_id)
179
 
                except errors.NonAsciiRevisionId:
180
 
                    revision_id = 'abc'
181
 
                    builder = tree.branch.get_commit_builder([],
182
 
                        revision_id=revision_id)
183
 
            except errors.CannotSetRevisionId:
184
 
                # This format doesn't support supplied revision ids
185
 
                return
186
 
            if not builder.supports_record_entry_contents:
187
 
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
188
 
                    "record_entry_contents")
189
 
            self.assertFalse(builder.random_revid)
190
 
            self.record_root(builder, tree)
191
 
            builder.finish_inventory()
192
 
            self.assertEqual(revision_id, builder.commit('foo bar'))
193
 
        finally:
194
 
            tree.unlock()
195
 
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
196
 
        # the revision id must be set on the inventory when saving it. This
197
 
        # does not precisely test that - a repository that wants to can add it
198
 
        # on deserialisation, but thats all the current contract guarantees
199
 
        # anyway.
200
 
        self.assertEqual(revision_id,
201
 
            tree.branch.repository.get_inventory(revision_id).revision_id)
202
 
 
203
126
    def test_commit_with_revision_id_record_iter_changes(self):
204
127
        tree = self.make_branch_and_tree(".")
205
128
        tree.lock_write()
256
179
        finally:
257
180
            tree.unlock()
258
181
 
259
 
    def test_commit_without_root_or_record_iter_changes_errors(self):
260
 
        tree = self.make_branch_and_tree(".")
261
 
        tree.lock_write()
262
 
        try:
263
 
            self.build_tree(['foo'])
264
 
            tree.add('foo', 'foo-id')
265
 
            builder = tree.branch.get_commit_builder([])
266
 
            if not builder.supports_record_entry_contents:
267
 
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
268
 
                    "record_entry_contents")
269
 
            entry = tree.root_inventory['foo-id']
270
 
            self.assertRaises(errors.RootMissing,
271
 
                builder.record_entry_contents, entry, [], 'foo', tree,
272
 
                    tree.path_content_summary('foo'))
273
 
            builder.abort()
274
 
        finally:
275
 
            tree.unlock()
276
 
 
277
 
    def test_commit_unchanged_root_record_entry_contents(self):
278
 
        tree = self.make_branch_and_tree(".")
279
 
        old_revision_id = tree.commit('')
280
 
        tree.lock_write()
281
 
        parent_tree = tree.basis_tree()
282
 
        parent_tree.lock_read()
283
 
        self.addCleanup(parent_tree.unlock)
284
 
        builder = tree.branch.get_commit_builder([old_revision_id])
285
 
        try:
286
 
            if not builder.supports_record_entry_contents:
287
 
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
288
 
                    "record_entry_contents")
289
 
            builder.will_record_deletes()
290
 
            ie = inventory.make_entry('directory', '', None,
291
 
                    tree.get_root_id())
292
 
            delta, version_recorded, fs_hash = builder.record_entry_contents(
293
 
                ie, [parent_tree.root_inventory], '', tree,
294
 
                tree.path_content_summary(''))
295
 
            # Regardless of repository root behaviour we should consider this a
296
 
            # pointless commit.
297
 
            self.assertFalse(builder.any_changes())
298
 
            self.assertFalse(version_recorded)
299
 
            # if the repository format recorded a new root revision, that
300
 
            # should be in the delta
301
 
            got_new_revision = ie.revision != old_revision_id
302
 
            if got_new_revision:
303
 
                self.assertEqual(('', '', ie.file_id, ie), delta)
304
 
                # The delta should be tracked
305
 
                self.assertEqual(delta, builder.get_basis_delta()[-1])
306
 
            else:
307
 
                self.assertEqual(None, delta)
308
 
            # Directories do not get hashed.
309
 
            self.assertEqual(None, fs_hash)
310
 
            builder.abort()
311
 
        except:
312
 
            builder.abort()
313
 
            tree.unlock()
314
 
            raise
315
 
        else:
316
 
            tree.unlock()
317
 
 
318
182
    def test_commit_unchanged_root_record_iter_changes(self):
319
183
        tree = self.make_branch_and_tree(".")
320
184
        old_revision_id = tree.commit('')
339
203
        finally:
340
204
            tree.unlock()
341
205
 
342
 
    def test_commit_record_entry_contents(self):
343
 
        tree = self.make_branch_and_tree(".")
344
 
        tree.lock_write()
345
 
        try:
346
 
            builder = tree.branch.get_commit_builder([])
347
 
            if not builder.supports_record_entry_contents:
348
 
                raise tests.TestNotApplicable("CommitBuilder doesn't "
349
 
                    "support record_entry_contents")
350
 
            self.record_root(builder, tree)
351
 
            builder.finish_inventory()
352
 
            rev_id = builder.commit('foo bar')
353
 
        finally:
354
 
            tree.unlock()
355
 
        self.assertNotEqual(None, rev_id)
356
 
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
357
 
        # the revision id must be set on the inventory when saving it. This does not
358
 
        # precisely test that - a repository that wants to can add it on deserialisation,
359
 
        # but thats all the current contract guarantees anyway.
360
 
        self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
361
 
 
362
 
    def test_get_basis_delta(self):
363
 
        tree = self.make_branch_and_tree(".")
364
 
        self.build_tree(["foo"])
365
 
        tree.add(["foo"], ["foo-id"])
366
 
        old_revision_id = tree.commit("added foo")
367
 
        tree.lock_write()
368
 
        try:
369
 
            self.build_tree(['bar'])
370
 
            tree.add(['bar'], ['bar-id'])
371
 
            basis = tree.branch.repository.revision_tree(old_revision_id)
372
 
            basis.lock_read()
373
 
            self.addCleanup(basis.unlock)
374
 
            builder = tree.branch.get_commit_builder([old_revision_id])
375
 
            total_delta = []
376
 
            try:
377
 
                if not builder.supports_record_entry_contents:
378
 
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
379
 
                        "support record_entry_contents")
380
 
                parent_invs = [basis.root_inventory]
381
 
                builder.will_record_deletes()
382
 
                if builder.record_root_entry:
383
 
                    ie = basis.root_inventory.root.copy()
384
 
                    delta, _, _ = builder.record_entry_contents(ie, parent_invs,
385
 
                        '', tree, tree.path_content_summary(''))
386
 
                    if delta is not None:
387
 
                        total_delta.append(delta)
388
 
                delta = builder.record_delete("foo", "foo-id")
389
 
                total_delta.append(delta)
390
 
                new_bar = inventory.make_entry('file', 'bar',
391
 
                    parent_id=tree.get_root_id(), file_id='bar-id')
392
 
                delta, _, _ = builder.record_entry_contents(new_bar, parent_invs,
393
 
                    'bar', tree, tree.path_content_summary('bar'))
394
 
                total_delta.append(delta)
395
 
                # All actions should have been recorded in the basis_delta
396
 
                self.assertEqual(total_delta, builder.get_basis_delta())
397
 
                builder.finish_inventory()
398
 
                builder.commit('delete foo, add bar')
399
 
            except:
400
 
                tree.branch.repository.abort_write_group()
401
 
                raise
402
 
        finally:
403
 
            tree.unlock()
404
 
 
405
 
    def test_get_basis_delta_without_notification(self):
406
 
        tree = self.make_branch_and_tree(".")
407
 
        old_revision_id = tree.commit('')
408
 
        tree.lock_write()
409
 
        try:
410
 
            parent_tree = tree.basis_tree()
411
 
            parent_tree.lock_read()
412
 
            self.addCleanup(parent_tree.unlock)
413
 
            builder = tree.branch.get_commit_builder([old_revision_id])
414
 
            # It is an error to expect builder.get_basis_delta() to be correct,
415
 
            # if you have not also called will_record_deletes() to indicate you
416
 
            # will be calling record_delete() when appropriate
417
 
            self.assertRaises(AssertionError, builder.get_basis_delta)
418
 
            tree.branch.repository.abort_write_group()
419
 
        finally:
420
 
            tree.unlock()
421
 
 
422
 
    def test_record_delete(self):
423
 
        tree = self.make_branch_and_tree(".")
424
 
        self.build_tree(["foo"])
425
 
        tree.add(["foo"], ["foo-id"])
426
 
        rev_id = tree.commit("added foo")
427
 
        # Remove the inventory details for foo-id, because
428
 
        # record_entry_contents ends up copying root verbatim.
429
 
        tree.unversion(["foo-id"])
430
 
        tree.lock_write()
431
 
        try:
432
 
            basis = tree.branch.repository.revision_tree(rev_id)
433
 
            builder = tree.branch.get_commit_builder([rev_id])
434
 
            try:
435
 
                if not builder.supports_record_entry_contents:
436
 
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
437
 
                        "support record_entry_contents")
438
 
                builder.will_record_deletes()
439
 
                if builder.record_root_entry is True:
440
 
                    parent_invs = [basis.root_inventory]
441
 
                    del basis.root_inventory.root.children['foo']
442
 
                    builder.record_entry_contents(basis.root_inventory.root,
443
 
                        parent_invs, '', tree, tree.path_content_summary(''))
444
 
                # the delta should be returned, and recorded in _basis_delta
445
 
                delta = builder.record_delete("foo", "foo-id")
446
 
                self.assertEqual(("foo", None, "foo-id", None), delta)
447
 
                self.assertEqual(delta, builder.get_basis_delta()[-1])
448
 
                builder.finish_inventory()
449
 
                rev_id2 = builder.commit('delete foo')
450
 
            except:
451
 
                tree.branch.repository.abort_write_group()
452
 
                raise
453
 
        finally:
454
 
            tree.unlock()
455
 
        rev_tree = builder.revision_tree()
456
 
        rev_tree.lock_read()
457
 
        self.addCleanup(rev_tree.unlock)
458
 
        self.assertFalse(rev_tree.path2id('foo'))
459
 
 
460
206
    def test_record_delete_record_iter_changes(self):
461
207
        tree = self.make_branch_and_tree(".")
462
208
        self.build_tree(["foo"])
466
212
        try:
467
213
            builder = tree.branch.get_commit_builder([rev_id])
468
214
            try:
469
 
                builder.will_record_deletes()
470
215
                delete_change = ('foo-id', ('foo', None), True, (True, False),
471
216
                    (tree.path2id(''), None), ('foo', None), ('file', None),
472
217
                    (False, None))
487
232
        self.addCleanup(rev_tree.unlock)
488
233
        self.assertFalse(rev_tree.path2id('foo'))
489
234
 
490
 
    def test_record_delete_without_notification(self):
491
 
        tree = self.make_branch_and_tree(".")
492
 
        self.build_tree(["foo"])
493
 
        tree.add(["foo"], ["foo-id"])
494
 
        rev_id = tree.commit("added foo")
495
 
        tree.lock_write()
496
 
        try:
497
 
            builder = tree.branch.get_commit_builder([rev_id])
498
 
            try:
499
 
                if not builder.supports_record_entry_contents:
500
 
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
501
 
                        "support record_entry_contents")
502
 
                self.record_root(builder, tree)
503
 
                self.assertRaises(AssertionError,
504
 
                    builder.record_delete, "foo", "foo-id")
505
 
            finally:
506
 
                tree.branch.repository.abort_write_group()
507
 
        finally:
508
 
            tree.unlock()
509
 
 
510
 
    def test_revision_tree_record_entry_contents(self):
511
 
        tree = self.make_branch_and_tree(".")
512
 
        tree.lock_write()
513
 
        try:
514
 
            builder = tree.branch.get_commit_builder([])
515
 
            if not builder.supports_record_entry_contents:
516
 
                raise tests.TestNotApplicable("CommitBuilder doesn't "
517
 
                    "support record_entry_contents")
518
 
            self.record_root(builder, tree)
519
 
            builder.finish_inventory()
520
 
            rev_id = builder.commit('foo bar')
521
 
        finally:
522
 
            tree.unlock()
523
 
        rev_tree = builder.revision_tree()
524
 
        # Just a couple simple tests to ensure that it actually follows
525
 
        # the RevisionTree api.
526
 
        self.assertEqual(rev_id, rev_tree.get_revision_id())
527
 
        self.assertEqual([], rev_tree.get_parent_ids())
528
 
 
529
235
    def test_revision_tree_record_iter_changes(self):
530
236
        tree = self.make_branch_and_tree(".")
531
237
        tree.lock_write()
595
301
 
596
302
    def _commit_check_unchanged(self, tree, name, file_id, mini_commit=None):
597
303
        rev1 = tree.commit('')
598
 
        if mini_commit is None:
599
 
            mini_commit = self.mini_commit
600
304
        rev2 = mini_commit(tree, name, name, False, False)
601
305
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
602
306
        self.assertEqual(rev1, tree1.get_file_revision(file_id))
609
313
        # committing without changing a dir does not change the last modified.
610
314
        tree = self.make_branch_and_tree('.')
611
315
        self.build_tree(['dir/'])
612
 
        self._add_commit_check_unchanged(tree, 'dir')
613
 
 
614
 
    def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
615
 
        # committing without changing a dir does not change the last modified.
616
 
        tree = self.make_branch_and_tree('.')
617
 
        self.build_tree(['dir/'])
618
316
        self._add_commit_check_unchanged(tree, 'dir',
619
317
            mini_commit=self.mini_commit_record_iter_changes)
620
318
 
640
338
        # committing without changing a file does not change the last modified.
641
339
        tree = self.make_branch_and_tree('.')
642
340
        self.build_tree(['file'])
643
 
        self._add_commit_check_unchanged(tree, 'file')
644
 
 
645
 
    def test_last_modified_revision_after_commit_file_unchanged_ric(self):
646
 
        # committing without changing a file does not change the last modified.
647
 
        tree = self.make_branch_and_tree('.')
648
 
        self.build_tree(['file'])
649
341
        self._add_commit_check_unchanged(tree, 'file',
650
342
            mini_commit=self.mini_commit_record_iter_changes)
651
343
 
654
346
        self.requireFeature(features.SymlinkFeature)
655
347
        tree = self.make_branch_and_tree('.')
656
348
        os.symlink('target', 'link')
657
 
        self._add_commit_check_unchanged(tree, 'link')
658
 
 
659
 
    def test_last_modified_revision_after_commit_link_unchanged_ric(self):
660
 
        # committing without changing a link does not change the last modified.
661
 
        self.requireFeature(features.SymlinkFeature)
662
 
        tree = self.make_branch_and_tree('.')
663
 
        os.symlink('target', 'link')
664
349
        self._add_commit_check_unchanged(tree, 'link',
665
350
            mini_commit=self.mini_commit_record_iter_changes)
666
351
 
672
357
        try:
673
358
            tree.add_reference(subtree)
674
359
            self._commit_check_unchanged(tree, 'reference',
675
 
                subtree.get_root_id())
676
 
        except errors.UnsupportedOperation:
677
 
            return
678
 
 
679
 
    def test_last_modified_revision_after_commit_reference_unchanged_ric(self):
680
 
        # committing without changing a subtree does not change the last
681
 
        # modified.
682
 
        tree = self.make_branch_and_tree('.')
683
 
        subtree = self.make_reference('reference')
684
 
        try:
685
 
            tree.add_reference(subtree)
686
 
            self._commit_check_unchanged(tree, 'reference',
687
360
                subtree.get_root_id(),
688
361
                mini_commit=self.mini_commit_record_iter_changes)
689
362
        except errors.UnsupportedOperation:
707
380
        # renaming a dir changes the last modified.
708
381
        tree = self.make_branch_and_tree('.')
709
382
        self.build_tree(['dir/'])
710
 
        self._add_commit_renamed_check_changed(tree, 'dir')
711
 
 
712
 
    def test_last_modified_revision_after_rename_dir_changes_ric(self):
713
 
        # renaming a dir changes the last modified.
714
 
        tree = self.make_branch_and_tree('.')
715
 
        self.build_tree(['dir/'])
716
383
        self._add_commit_renamed_check_changed(tree, 'dir',
717
384
            mini_commit=self.mini_commit_record_iter_changes)
718
385
 
721
388
        tree = self.make_branch_and_tree('.')
722
389
        self.build_tree(['file'])
723
390
        self._add_commit_renamed_check_changed(tree, 'file',
724
 
            expect_fs_hash=True)
725
 
 
726
 
    def test_last_modified_revision_after_rename_file_changes_ric(self):
727
 
        # renaming a file changes the last modified.
728
 
        tree = self.make_branch_and_tree('.')
729
 
        self.build_tree(['file'])
730
 
        self._add_commit_renamed_check_changed(tree, 'file',
731
391
            expect_fs_hash=True,
732
392
            mini_commit=self.mini_commit_record_iter_changes)
733
393
 
736
396
        self.requireFeature(features.SymlinkFeature)
737
397
        tree = self.make_branch_and_tree('.')
738
398
        os.symlink('target', 'link')
739
 
        self._add_commit_renamed_check_changed(tree, 'link')
740
 
 
741
 
    def test_last_modified_revision_after_rename_link_changes_ric(self):
742
 
        # renaming a link changes the last modified.
743
 
        self.requireFeature(features.SymlinkFeature)
744
 
        tree = self.make_branch_and_tree('.')
745
 
        os.symlink('target', 'link')
746
399
        self._add_commit_renamed_check_changed(tree, 'link',
747
400
            mini_commit=self.mini_commit_record_iter_changes)
748
401
 
753
406
        try:
754
407
            tree.add_reference(subtree)
755
408
            self._commit_renamed_check_changed(tree, 'reference',
756
 
                subtree.get_root_id())
757
 
        except errors.UnsupportedOperation:
758
 
            return
759
 
 
760
 
    def test_last_modified_revision_after_rename_ref_changes_ric(self):
761
 
        # renaming a reference changes the last modified.
762
 
        tree = self.make_branch_and_tree('.')
763
 
        subtree = self.make_reference('reference')
764
 
        try:
765
 
            tree.add_reference(subtree)
766
 
            self._commit_renamed_check_changed(tree, 'reference',
767
409
                subtree.get_root_id(),
768
410
                mini_commit=self.mini_commit_record_iter_changes)
769
411
        except errors.UnsupportedOperation:
782
424
        # reparenting a dir changes the last modified.
783
425
        tree = self.make_branch_and_tree('.')
784
426
        self.build_tree(['dir/'])
785
 
        self._add_commit_reparent_check_changed(tree, 'dir')
786
 
 
787
 
    def test_last_modified_revision_after_reparent_dir_changes_ric(self):
788
 
        # reparenting a dir changes the last modified.
789
 
        tree = self.make_branch_and_tree('.')
790
 
        self.build_tree(['dir/'])
791
427
        self._add_commit_reparent_check_changed(tree, 'dir',
792
428
            mini_commit=self.mini_commit_record_iter_changes)
793
429
 
796
432
        tree = self.make_branch_and_tree('.')
797
433
        self.build_tree(['file'])
798
434
        self._add_commit_reparent_check_changed(tree, 'file',
799
 
            expect_fs_hash=True)
800
 
 
801
 
    def test_last_modified_revision_after_reparent_file_changes_ric(self):
802
 
        # reparenting a file changes the last modified.
803
 
        tree = self.make_branch_and_tree('.')
804
 
        self.build_tree(['file'])
805
 
        self._add_commit_reparent_check_changed(tree, 'file',
806
435
            expect_fs_hash=True,
807
436
            mini_commit=self.mini_commit_record_iter_changes)
808
437
 
811
440
        self.requireFeature(features.SymlinkFeature)
812
441
        tree = self.make_branch_and_tree('.')
813
442
        os.symlink('target', 'link')
814
 
        self._add_commit_reparent_check_changed(tree, 'link')
815
 
 
816
 
    def test_last_modified_revision_after_reparent_link_changes_ric(self):
817
 
        # reparenting a link changes the last modified.
818
 
        self.requireFeature(features.SymlinkFeature)
819
 
        tree = self.make_branch_and_tree('.')
820
 
        os.symlink('target', 'link')
821
443
        self._add_commit_reparent_check_changed(tree, 'link',
822
444
            mini_commit=self.mini_commit_record_iter_changes)
823
445
 
834
456
        expect_fs_hash=False, mini_commit=None):
835
457
        rev1 = tree.commit('')
836
458
        changer()
837
 
        if mini_commit is None:
838
 
            mini_commit = self.mini_commit
839
459
        rev2 = mini_commit(tree, name, tree.id2path(file_id),
840
460
            expect_fs_hash=expect_fs_hash)
841
461
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
846
466
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
847
467
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
848
468
 
849
 
    def mini_commit(self, tree, name, new_name, records_version=True,
850
 
        delta_against_basis=True, expect_fs_hash=False):
851
 
        """Perform a miniature commit looking for record entry results.
852
 
 
853
 
        :param tree: The tree to commit.
854
 
        :param name: The path in the basis tree of the tree being committed.
855
 
        :param new_name: The path in the tree being committed.
856
 
        :param records_version: True if the commit of new_name is expected to
857
 
            record a new version.
858
 
        :param delta_against_basis: True of the commit of new_name is expected
859
 
            to have a delta against the basis.
860
 
        :param expect_fs_hash: True or false to indicate whether we expect a
861
 
            file hash to be returned from the record_entry_contents call.
862
 
        """
863
 
        tree.lock_write()
864
 
        try:
865
 
            # mini manual commit here so we can check the return of
866
 
            # record_entry_contents.
867
 
            parent_ids = tree.get_parent_ids()
868
 
            builder = tree.branch.get_commit_builder(parent_ids)
869
 
            try:
870
 
                if not builder.supports_record_entry_contents:
871
 
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
872
 
                        "support record_entry_contents")
873
 
                builder.will_record_deletes()
874
 
                parent_tree = tree.basis_tree()
875
 
                parent_tree.lock_read()
876
 
                self.addCleanup(parent_tree.unlock)
877
 
                parent_invs = [parent_tree.root_inventory]
878
 
                for parent_id in parent_ids[1:]:
879
 
                    parent_invs.append(tree.branch.repository.revision_tree(
880
 
                        parent_id).root_inventory)
881
 
                # root
882
 
                builder.record_entry_contents(
883
 
                    inventory.make_entry('directory', '', None,
884
 
                        tree.get_root_id()), parent_invs, '', tree,
885
 
                        tree.path_content_summary(''))
886
 
                def commit_id(file_id):
887
 
                    old_ie = tree.root_inventory[file_id]
888
 
                    path = tree.id2path(file_id)
889
 
                    ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
890
 
                        old_ie.parent_id, file_id)
891
 
                    content_summary = tree.path_content_summary(path)
892
 
                    if content_summary[0] == 'tree-reference':
893
 
                        content_summary = content_summary[:3] + (
894
 
                            tree.get_reference_revision(file_id),)
895
 
                    return builder.record_entry_contents(ie, parent_invs, path,
896
 
                        tree, content_summary)
897
 
 
898
 
                file_id = tree.path2id(new_name)
899
 
                parent_id = tree.root_inventory[file_id].parent_id
900
 
                if parent_id != tree.get_root_id():
901
 
                    commit_id(parent_id)
902
 
                # because a change of some sort is meant to have occurred,
903
 
                # recording the entry must return True.
904
 
                delta, version_recorded, fs_hash = commit_id(file_id)
905
 
                if records_version:
906
 
                    self.assertTrue(version_recorded)
907
 
                else:
908
 
                    self.assertFalse(version_recorded)
909
 
                if expect_fs_hash:
910
 
                    tree_file_stat = tree.get_file_with_stat(file_id)
911
 
                    tree_file_stat[0].close()
912
 
                    self.assertEqual(2, len(fs_hash))
913
 
                    self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
914
 
                    self.assertEqualStat(tree_file_stat[1], fs_hash[1])
915
 
                else:
916
 
                    self.assertEqual(None, fs_hash)
917
 
                new_entry = builder.new_inventory[file_id]
918
 
                if delta_against_basis:
919
 
                    expected_delta = (name, new_name, file_id, new_entry)
920
 
                    # The delta should be recorded
921
 
                    self.assertEqual(expected_delta,
922
 
                        builder.get_basis_delta()[-1])
923
 
                else:
924
 
                    expected_delta = None
925
 
                self.assertEqual(expected_delta, delta)
926
 
                builder.finish_inventory()
927
 
            except:
928
 
                builder.abort()
929
 
                raise
930
 
            else:
931
 
                rev2 = builder.commit('')
932
 
        except:
933
 
            tree.unlock()
934
 
            raise
935
 
        try:
936
 
            tree.set_parent_ids([rev2])
937
 
        finally:
938
 
            tree.unlock()
939
 
        return rev2
940
 
 
941
469
    def mini_commit_record_iter_changes(self, tree, name, new_name,
942
470
        records_version=True, delta_against_basis=True, expect_fs_hash=False):
943
471
        """Perform a miniature commit looking for record entry results.
957
485
        tree.lock_write()
958
486
        try:
959
487
            # mini manual commit here so we can check the return of
960
 
            # record_entry_contents.
 
488
            # record_iter_changes
961
489
            parent_ids = tree.get_parent_ids()
962
490
            builder = tree.branch.get_commit_builder(parent_ids)
963
 
            builder.will_record_deletes()
964
491
            parent_tree = tree.basis_tree()
965
492
            parent_tree.lock_read()
966
493
            self.addCleanup(parent_tree.unlock)
982
509
                self.assertEqualStat(result[2][1], tree_file_stat[1])
983
510
            else:
984
511
                self.assertEqual([], result)
985
 
            self.assertIs(None, builder.new_inventory)
986
512
            builder.finish_inventory()
987
513
            if tree.branch.repository._format.supports_full_versioned_files:
988
514
                inv_key = (builder._new_revision_id,)
989
515
                inv_sha1 = tree.branch.repository.inventories.get_sha1s(
990
516
                                [inv_key])[inv_key]
991
517
                self.assertEqual(inv_sha1, builder.inv_sha1)
992
 
            self.assertIs(None, builder.new_inventory)
993
518
            rev2 = builder.commit('')
994
519
            delta = builder.get_basis_delta()
995
520
            delta_dict = dict((change[2], change) for change in delta)
1033
558
        def change_file():
1034
559
            tree.put_file_bytes_non_atomic('fileid', 'new content')
1035
560
        self._add_commit_change_check_changed(tree, 'file', change_file,
1036
 
            expect_fs_hash=True)
1037
 
 
1038
 
    def test_last_modified_revision_after_content_file_changes_ric(self):
1039
 
        # altering a file changes the last modified.
1040
 
        tree = self.make_branch_and_tree('.')
1041
 
        self.build_tree(['file'])
1042
 
        def change_file():
1043
 
            tree.put_file_bytes_non_atomic('fileid', 'new content')
1044
 
        self._add_commit_change_check_changed(tree, 'file', change_file,
1045
561
            expect_fs_hash=True,
1046
562
            mini_commit=self.mini_commit_record_iter_changes)
1047
563
 
1048
 
    def test_last_modified_revision_after_content_link_changes(self):
1049
 
        # changing a link changes the last modified.
1050
 
        self.requireFeature(features.SymlinkFeature)
1051
 
        tree = self.make_branch_and_tree('.')
1052
 
        os.symlink('target', 'link')
1053
 
        def change_link():
1054
 
            os.unlink('link')
1055
 
            os.symlink('newtarget', 'link')
1056
 
        self._add_commit_change_check_changed(tree, 'link', change_link)
1057
 
 
1058
 
    def _test_last_mod_rev_after_content_link_changes_ric(
 
564
    def _test_last_mod_rev_after_content_link_changes(
1059
565
        self, link, target, newtarget, file_id=None):
1060
566
        if file_id is None:
1061
567
            file_id = link
1071
577
            mini_commit=self.mini_commit_record_iter_changes,
1072
578
            file_id=file_id)
1073
579
 
1074
 
    def test_last_modified_rev_after_content_link_changes_ric(self):
1075
 
        self._test_last_mod_rev_after_content_link_changes_ric(
 
580
    def test_last_modified_rev_after_content_link_changes(self):
 
581
        self._test_last_mod_rev_after_content_link_changes(
1076
582
            'link', 'target', 'newtarget')
1077
583
 
1078
 
    def test_last_modified_rev_after_content_unicode_link_changes_ric(self):
 
584
    def test_last_modified_rev_after_content_unicode_link_changes(self):
1079
585
        self.requireFeature(features.UnicodeFilenameFeature)
1080
 
        self._test_last_mod_rev_after_content_link_changes_ric(
 
586
        self._test_last_mod_rev_after_content_link_changes(
1081
587
            u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget',
1082
588
 
1083
589
            file_id=u'li\u1234nk'.encode('UTF-8'))
1099
605
        rev2 = self._rename_in_tree(tree1, name)
1100
606
        rev3 = self._rename_in_tree(tree2, name)
1101
607
        tree1.merge_from_branch(tree2.branch)
1102
 
        if mini_commit is None:
1103
 
            mini_commit = self.mini_commit
1104
608
        rev4 = mini_commit(tree1, 'new_' + name, 'new_' + name,
1105
609
            expect_fs_hash=expect_fs_hash)
1106
610
        tree3, = self._get_revtrees(tree1, [rev4])
1117
621
        # merge a dir changes the last modified.
1118
622
        tree1 = self.make_branch_and_tree('t1')
1119
623
        self.build_tree(['t1/dir/'])
1120
 
        self._commit_sprout_rename_merge(tree1, 'dir')
1121
 
 
1122
 
    def test_last_modified_revision_after_merge_dir_changes_ric(self):
1123
 
        # merge a dir changes the last modified.
1124
 
        tree1 = self.make_branch_and_tree('t1')
1125
 
        self.build_tree(['t1/dir/'])
1126
624
        self._commit_sprout_rename_merge(tree1, 'dir',
1127
625
            mini_commit=self.mini_commit_record_iter_changes)
1128
626
 
1130
628
        # merge a file changes the last modified.
1131
629
        tree1 = self.make_branch_and_tree('t1')
1132
630
        self.build_tree(['t1/file'])
1133
 
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
1134
 
 
1135
 
    def test_last_modified_revision_after_merge_file_changes_ric(self):
1136
 
        # merge a file changes the last modified.
1137
 
        tree1 = self.make_branch_and_tree('t1')
1138
 
        self.build_tree(['t1/file'])
1139
631
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True,
1140
632
            mini_commit=self.mini_commit_record_iter_changes)
1141
633
 
1144
636
        self.requireFeature(features.SymlinkFeature)
1145
637
        tree1 = self.make_branch_and_tree('t1')
1146
638
        os.symlink('target', 't1/link')
1147
 
        self._commit_sprout_rename_merge(tree1, 'link')
1148
 
 
1149
 
    def test_last_modified_revision_after_merge_link_changes_ric(self):
1150
 
        # merge a link changes the last modified.
1151
 
        self.requireFeature(features.SymlinkFeature)
1152
 
        tree1 = self.make_branch_and_tree('t1')
1153
 
        os.symlink('target', 't1/link')
1154
639
        self._commit_sprout_rename_merge(tree1, 'link',
1155
640
            mini_commit=self.mini_commit_record_iter_changes)
1156
641
 
1164
649
        # change on the other side to merge back
1165
650
        rev2 = self._rename_in_tree(tree2, name)
1166
651
        tree1.merge_from_branch(tree2.branch)
1167
 
        if mini_commit is None:
1168
 
            mini_commit = self.mini_commit
1169
652
        def _check_graph(in_tree, changed_in_tree):
1170
653
            rev3 = mini_commit(in_tree, name, 'new_' + name, False,
1171
654
                delta_against_basis=changed_in_tree)
1197
680
        tree2.add(['name'], [file_id])
1198
681
        rev2 = tree2.commit('')
1199
682
        tree1.merge_from_branch(tree2.branch)
1200
 
        if mini_commit is None:
1201
 
            mini_commit = self.mini_commit
1202
683
        rev3 = mini_commit(tree1, None, 'name', False)
1203
684
        tree3, = self._get_revtrees(tree1, [rev2])
1204
685
        # in rev2, name should be only changed in rev2
1211
692
        # merge a dir that changed preserves the last modified.
1212
693
        tree1 = self.make_branch_and_tree('t1')
1213
694
        self.build_tree(['t1/dir/'])
1214
 
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
1215
 
 
1216
 
    def test_last_modified_revision_after_converged_merge_dir_unchanged_ric(self):
1217
 
        # merge a dir that changed preserves the last modified.
1218
 
        tree1 = self.make_branch_and_tree('t1')
1219
 
        self.build_tree(['t1/dir/'])
1220
695
        self._commit_sprout_rename_merge_converged(tree1, 'dir',
1221
696
            mini_commit=self.mini_commit_record_iter_changes)
1222
697
 
1224
699
        # merge a file that changed preserves the last modified.
1225
700
        tree1 = self.make_branch_and_tree('t1')
1226
701
        self.build_tree(['t1/file'])
1227
 
        self._commit_sprout_rename_merge_converged(tree1, 'file')
1228
 
 
1229
 
    def test_last_modified_revision_after_converged_merge_file_unchanged_ric(self):
1230
 
        # merge a file that changed preserves the last modified.
1231
 
        tree1 = self.make_branch_and_tree('t1')
1232
 
        self.build_tree(['t1/file'])
1233
702
        self._commit_sprout_rename_merge_converged(tree1, 'file',
1234
703
            mini_commit=self.mini_commit_record_iter_changes)
1235
704
 
1238
707
        self.requireFeature(features.SymlinkFeature)
1239
708
        tree1 = self.make_branch_and_tree('t1')
1240
709
        os.symlink('target', 't1/link')
1241
 
        self._commit_sprout_rename_merge_converged(tree1, 'link')
1242
 
 
1243
 
    def test_last_modified_revision_after_converged_merge_link_unchanged_ric(self):
1244
 
        # merge a link that changed preserves the last modified.
1245
 
        self.requireFeature(features.SymlinkFeature)
1246
 
        tree1 = self.make_branch_and_tree('t1')
1247
 
        os.symlink('target', 't1/link')
1248
710
        self._commit_sprout_rename_merge_converged(tree1, 'link',
1249
711
            mini_commit=self.mini_commit_record_iter_changes)
1250
712
 
1251
713
    def test_last_modified_revision_after_merge_new_dir_unchanged(self):
1252
714
        # merge a new dir does not change the last modified.
1253
715
        tree1 = self.make_branch_and_tree('t1')
1254
 
        self._commit_sprout_make_merge(tree1, self.make_dir)
1255
 
 
1256
 
    def test_last_modified_revision_after_merge_new_dir_unchanged_ric(self):
1257
 
        # merge a new dir does not change the last modified.
1258
 
        tree1 = self.make_branch_and_tree('t1')
1259
716
        self._commit_sprout_make_merge(tree1, self.make_dir,
1260
717
            mini_commit=self.mini_commit_record_iter_changes)
1261
718
 
1262
719
    def test_last_modified_revision_after_merge_new_file_unchanged(self):
1263
720
        # merge a new file does not change the last modified.
1264
721
        tree1 = self.make_branch_and_tree('t1')
1265
 
        self._commit_sprout_make_merge(tree1, self.make_file)
1266
 
 
1267
 
    def test_last_modified_revision_after_merge_new_file_unchanged_ric(self):
1268
 
        # merge a new file does not change the last modified.
1269
 
        tree1 = self.make_branch_and_tree('t1')
1270
722
        self._commit_sprout_make_merge(tree1, self.make_file,
1271
723
            mini_commit=self.mini_commit_record_iter_changes)
1272
724
 
1273
725
    def test_last_modified_revision_after_merge_new_link_unchanged(self):
1274
726
        # merge a new link does not change the last modified.
1275
727
        tree1 = self.make_branch_and_tree('t1')
1276
 
        self._commit_sprout_make_merge(tree1, self.make_link)
1277
 
 
1278
 
    def test_last_modified_revision_after_merge_new_link_unchanged_ric(self):
1279
 
        # merge a new link does not change the last modified.
1280
 
        tree1 = self.make_branch_and_tree('t1')
1281
728
        self._commit_sprout_make_merge(tree1, self.make_link,
1282
729
            mini_commit=self.mini_commit_record_iter_changes)
1283
730
 
1313
760
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
1314
761
 
1315
762
    def test_last_modified_dir_file(self):
1316
 
        self._check_kind_change(self.make_dir, self.make_file,
1317
 
            expect_fs_hash=True)
1318
 
 
1319
 
    def test_last_modified_dir_file_ric(self):
1320
763
        try:
1321
764
            self._check_kind_change(self.make_dir, self.make_file,
1322
765
                expect_fs_hash=True,
1327
770
                "directory to file")
1328
771
 
1329
772
    def test_last_modified_dir_link(self):
1330
 
        self._check_kind_change(self.make_dir, self.make_link)
1331
 
 
1332
 
    def test_last_modified_dir_link_ric(self):
1333
773
        try:
1334
774
            self._check_kind_change(self.make_dir, self.make_link,
1335
775
                mini_commit=self.mini_commit_record_iter_changes)
1340
780
 
1341
781
    def test_last_modified_link_file(self):
1342
782
        self._check_kind_change(self.make_link, self.make_file,
1343
 
            expect_fs_hash=True)
1344
 
 
1345
 
    def test_last_modified_link_file_ric(self):
1346
 
        self._check_kind_change(self.make_link, self.make_file,
1347
783
            expect_fs_hash=True,
1348
784
            mini_commit=self.mini_commit_record_iter_changes)
1349
785
 
1350
786
    def test_last_modified_link_dir(self):
1351
 
        self._check_kind_change(self.make_link, self.make_dir)
1352
 
 
1353
 
    def test_last_modified_link_dir_ric(self):
1354
787
        self._check_kind_change(self.make_link, self.make_dir,
1355
788
            mini_commit=self.mini_commit_record_iter_changes)
1356
789
 
1357
790
    def test_last_modified_file_dir(self):
1358
 
        self._check_kind_change(self.make_file, self.make_dir)
1359
 
 
1360
 
    def test_last_modified_file_dir_ric(self):
1361
791
        self._check_kind_change(self.make_file, self.make_dir,
1362
792
            mini_commit=self.mini_commit_record_iter_changes)
1363
793
 
1364
794
    def test_last_modified_file_link(self):
1365
 
        self._check_kind_change(self.make_file, self.make_link)
1366
 
 
1367
 
    def test_last_modified_file_link_ric(self):
1368
795
        self._check_kind_change(self.make_file, self.make_link,
1369
796
            mini_commit=self.mini_commit_record_iter_changes)
1370
797
 
1428
855
        tree.lock_write()
1429
856
        try:
1430
857
            # Make sure no username is available.
1431
 
            self.assertRaises(errors.NoWhoami, tree.branch.get_commit_builder,
 
858
            self.assertRaises(config.NoWhoami, tree.branch.get_commit_builder,
1432
859
                              [])
1433
860
            builder = tree.branch.get_commit_builder(
1434
861
                [], committer='me@example.com')