47
47
branch.repository.commit_write_group()
48
48
branch.repository.unlock()
50
def record_root(self, builder, tree):
51
if builder.record_root_entry is True:
54
ie = tree.root_inventory.root
57
parent_tree = tree.branch.repository.revision_tree(
58
_mod_revision.NULL_REVISION)
60
builder.record_entry_contents(ie, parent_invs, '', tree,
61
tree.path_content_summary(''))
63
def test_finish_inventory_with_record_root(self):
64
tree = self.make_branch_and_tree(".")
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()
78
50
def test_finish_inventory_record_iter_changes(self):
79
51
tree = self.make_branch_and_tree(".")
165
123
actually_updated_branch = (tree.branch.last_revision() == rev_id)
166
124
self.assertEqual(actually_updated_branch, will_update_branch)
168
def test_commit_with_revision_id_record_entry_contents(self):
169
tree = self.make_branch_and_tree(".")
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')
177
builder = tree.branch.get_commit_builder([],
178
revision_id=revision_id)
179
except errors.NonAsciiRevisionId:
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
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'))
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
200
self.assertEqual(revision_id,
201
tree.branch.repository.get_inventory(revision_id).revision_id)
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()
259
def test_commit_without_root_or_record_iter_changes_errors(self):
260
tree = self.make_branch_and_tree(".")
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'))
277
def test_commit_unchanged_root_record_entry_contents(self):
278
tree = self.make_branch_and_tree(".")
279
old_revision_id = tree.commit('')
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])
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,
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
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
303
self.assertEqual(('', '', ie.file_id, ie), delta)
304
# The delta should be tracked
305
self.assertEqual(delta, builder.get_basis_delta()[-1])
307
self.assertEqual(None, delta)
308
# Directories do not get hashed.
309
self.assertEqual(None, fs_hash)
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('')
342
def test_commit_record_entry_contents(self):
343
tree = self.make_branch_and_tree(".")
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')
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)
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")
369
self.build_tree(['bar'])
370
tree.add(['bar'], ['bar-id'])
371
basis = tree.branch.repository.revision_tree(old_revision_id)
373
self.addCleanup(basis.unlock)
374
builder = tree.branch.get_commit_builder([old_revision_id])
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')
400
tree.branch.repository.abort_write_group()
405
def test_get_basis_delta_without_notification(self):
406
tree = self.make_branch_and_tree(".")
407
old_revision_id = tree.commit('')
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()
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"])
432
basis = tree.branch.repository.revision_tree(rev_id)
433
builder = tree.branch.get_commit_builder([rev_id])
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')
451
tree.branch.repository.abort_write_group()
455
rev_tree = builder.revision_tree()
457
self.addCleanup(rev_tree.unlock)
458
self.assertFalse(rev_tree.path2id('foo'))
460
206
def test_record_delete_record_iter_changes(self):
461
207
tree = self.make_branch_and_tree(".")
462
208
self.build_tree(["foo"])
487
232
self.addCleanup(rev_tree.unlock)
488
233
self.assertFalse(rev_tree.path2id('foo'))
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")
497
builder = tree.branch.get_commit_builder([rev_id])
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")
506
tree.branch.repository.abort_write_group()
510
def test_revision_tree_record_entry_contents(self):
511
tree = self.make_branch_and_tree(".")
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')
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())
529
235
def test_revision_tree_record_iter_changes(self):
530
236
tree = self.make_branch_and_tree(".")
531
237
tree.lock_write()
846
466
expected_graph[(file_id, rev2)] = ((file_id, rev1),)
847
467
self.assertFileGraph(expected_graph, tree, (file_id, rev2))
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.
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.
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)
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)
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)
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():
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)
906
self.assertTrue(version_recorded)
908
self.assertFalse(version_recorded)
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])
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])
924
expected_delta = None
925
self.assertEqual(expected_delta, delta)
926
builder.finish_inventory()
931
rev2 = builder.commit('')
936
tree.set_parent_ids([rev2])
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.
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)
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'])
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)
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')
1055
os.symlink('newtarget', 'link')
1056
self._add_commit_change_check_changed(tree, 'link', change_link)
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:
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')
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)
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)
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)
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)
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)
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)
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)
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)
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)
1350
786
def test_last_modified_link_dir(self):
1351
self._check_kind_change(self.make_link, self.make_dir)
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)
1357
790
def test_last_modified_file_dir(self):
1358
self._check_kind_change(self.make_file, self.make_dir)
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)
1364
794
def test_last_modified_file_link(self):
1365
self._check_kind_change(self.make_file, self.make_link)
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)