Merge lp:~jelmer/brz/remove-record-entry-contents into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/remove-record-entry-contents
Merge into: lp:brz
Prerequisite: lp:~jelmer/brz/iter-changes-all-the-way
Diff against target: 1633 lines (+37/-1096)
9 files modified
breezy/bzr/vf_repository.py (+10/-281)
breezy/commit.py (+8/-247)
breezy/plugins/fastimport/revision_store.py (+1/-1)
breezy/repository.py (+0/-2)
breezy/tests/per_repository/test_commit_builder.py (+6/-555)
breezy/tests/per_workingtree/test_commit.py (+1/-0)
breezy/tests/test_foreign.py (+6/-10)
breezy/tests/test_merge.py (+2/-0)
doc/en/release-notes/brz-3.0.txt (+3/-0)
To merge this branch: bzr merge lp:~jelmer/brz/remove-record-entry-contents
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+325968@code.launchpad.net

Commit message

Remove remaining implementations of CommitBuilder.record_{entry_contents,delete}.

Description of the change

Remove remaining implementations of CommitBuilder.record_{entry_contents,delete}.

... and all their tests.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Changes here all look good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'breezy/bzr/vf_repository.py'
--- breezy/bzr/vf_repository.py 2017-06-20 00:39:00 +0000
+++ breezy/bzr/vf_repository.py 2017-06-20 22:39:47 +0000
@@ -112,9 +112,6 @@
112 """Commit builder implementation for versioned files based repositories.112 """Commit builder implementation for versioned files based repositories.
113 """113 """
114114
115 # this commit builder supports the record_entry_contents interface
116 supports_record_entry_contents = True
117
118 # the default CommitBuilder does not manage trees whose root is versioned.115 # the default CommitBuilder does not manage trees whose root is versioned.
119 _versioned_root = False116 _versioned_root = False
120117
@@ -129,7 +126,7 @@
129 except IndexError:126 except IndexError:
130 basis_id = _mod_revision.NULL_REVISION127 basis_id = _mod_revision.NULL_REVISION
131 self.basis_delta_revision = basis_id128 self.basis_delta_revision = basis_id
132 self.new_inventory = Inventory(None)129 self._new_inventory = None
133 self._basis_delta = []130 self._basis_delta = []
134 self.__heads = graph.HeadsCache(repository.get_graph()).heads131 self.__heads = graph.HeadsCache(repository.get_graph()).heads
135 # memo'd check for no-op commits.132 # memo'd check for no-op commits.
@@ -220,11 +217,11 @@
220 require deserializing the inventory, while we already have a copy in217 require deserializing the inventory, while we already have a copy in
221 memory.218 memory.
222 """219 """
223 if self.new_inventory is None:220 if self._new_inventory is None:
224 self.new_inventory = self.repository.get_inventory(221 self._new_inventory = self.repository.get_inventory(
225 self._new_revision_id)222 self._new_revision_id)
226 return inventorytree.InventoryRevisionTree(self.repository,223 return inventorytree.InventoryRevisionTree(self.repository,
227 self.new_inventory, self._new_revision_id)224 self._new_inventory, self._new_revision_id)
228225
229 def finish_inventory(self):226 def finish_inventory(self):
230 """Tell the builder that the inventory is finished.227 """Tell the builder that the inventory is finished.
@@ -232,42 +229,14 @@
232 :return: The inventory id in the repository, which can be used with229 :return: The inventory id in the repository, which can be used with
233 repository.get_inventory.230 repository.get_inventory.
234 """231 """
235 if self.new_inventory is None:232 # an inventory delta was accumulated without creating a new
236 # an inventory delta was accumulated without creating a new233 # inventory.
237 # inventory.234 basis_id = self.basis_delta_revision
238 basis_id = self.basis_delta_revision235 self.inv_sha1, self._new_inventory = self.repository.add_inventory_by_delta(
239 # We ignore the 'inventory' returned by add_inventory_by_delta236 basis_id, self._basis_delta, self._new_revision_id,
240 # because self.new_inventory is used to hint to the rest of the237 self.parents)
241 # system what code path was taken
242 self.inv_sha1, _ = self.repository.add_inventory_by_delta(
243 basis_id, self._basis_delta, self._new_revision_id,
244 self.parents)
245 else:
246 if self.new_inventory.root is None:
247 raise AssertionError('Root entry should be supplied to'
248 ' record_entry_contents, as of bzr 0.10.')
249 self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
250 self.new_inventory.revision_id = self._new_revision_id
251 self.inv_sha1 = self.repository.add_inventory(
252 self._new_revision_id,
253 self.new_inventory,
254 self.parents
255 )
256 return self._new_revision_id238 return self._new_revision_id
257239
258 def _check_root(self, ie, parent_invs, tree):
259 """Helper for record_entry_contents.
260
261 :param ie: An entry being added.
262 :param parent_invs: The inventories of the parent revisions of the
263 commit.
264 :param tree: The tree that is being committed.
265 """
266 # In this revision format, root entries have no knit or weave When
267 # serializing out to disk and back in root.revision is always
268 # _new_revision_id
269 ie.revision = self._new_revision_id
270
271 def _require_root_change(self, tree):240 def _require_root_change(self, tree):
272 """Enforce an appropriate root object change.241 """Enforce an appropriate root object change.
273242
@@ -311,241 +280,11 @@
311 def get_basis_delta(self):280 def get_basis_delta(self):
312 """Return the complete inventory delta versus the basis inventory.281 """Return the complete inventory delta versus the basis inventory.
313282
314 This has been built up with the calls to record_delete and
315 record_entry_contents.
316
317 :return: An inventory delta, suitable for use with apply_delta, or283 :return: An inventory delta, suitable for use with apply_delta, or
318 Repository.add_inventory_by_delta, etc.284 Repository.add_inventory_by_delta, etc.
319 """285 """
320 return self._basis_delta286 return self._basis_delta
321287
322 def record_delete(self, path, file_id):
323 """Record that a delete occured against a basis tree.
324
325 This is an optional API - when used it adds items to the basis_delta
326 being accumulated by the commit builder.
327
328 :param path: The path of the thing deleted.
329 :param file_id: The file id that was deleted.
330 """
331 delta = (path, None, file_id, None)
332 self._basis_delta.append(delta)
333 self._any_changes = True
334 return delta
335
336 def record_entry_contents(self, ie, parent_invs, path, tree,
337 content_summary):
338 """Record the content of ie from tree into the commit if needed.
339
340 Side effect: sets ie.revision when unchanged
341
342 :param ie: An inventory entry present in the commit.
343 :param parent_invs: The inventories of the parent revisions of the
344 commit.
345 :param path: The path the entry is at in the tree.
346 :param tree: The tree which contains this entry and should be used to
347 obtain content.
348 :param content_summary: Summary data from the tree about the paths
349 content - stat, length, exec, sha/link target. This is only
350 accessed when the entry has a revision of None - that is when it is
351 a candidate to commit.
352 :return: A tuple (change_delta, version_recorded, fs_hash).
353 change_delta is an inventory_delta change for this entry against
354 the basis tree of the commit, or None if no change occured against
355 the basis tree.
356 version_recorded is True if a new version of the entry has been
357 recorded. For instance, committing a merge where a file was only
358 changed on the other side will return (delta, False).
359 fs_hash is either None, or the hash details for the path (currently
360 a tuple of the contents sha1 and the statvalue returned by
361 tree.get_file_with_stat()).
362 """
363 if self.new_inventory.root is None:
364 if ie.parent_id is not None:
365 raise errors.RootMissing()
366 self._check_root(ie, parent_invs, tree)
367 if ie.revision is None:
368 kind = content_summary[0]
369 else:
370 # ie is carried over from a prior commit
371 kind = ie.kind
372 # XXX: repository specific check for nested tree support goes here - if
373 # the repo doesn't want nested trees we skip it ?
374 if (kind == 'tree-reference' and
375 not self.repository._format.supports_tree_reference):
376 # mismatch between commit builder logic and repository:
377 # this needs the entry creation pushed down into the builder.
378 raise NotImplementedError('Missing repository subtree support.')
379 self.new_inventory.add(ie)
380
381 # TODO: slow, take it out of the inner loop.
382 try:
383 basis_inv = parent_invs[0]
384 except IndexError:
385 basis_inv = Inventory(root_id=None)
386
387 # ie.revision is always None if the InventoryEntry is considered
388 # for committing. We may record the previous parents revision if the
389 # content is actually unchanged against a sole head.
390 if ie.revision is not None:
391 if not self._versioned_root and path == '':
392 # repositories that do not version the root set the root's
393 # revision to the new commit even when no change occurs (more
394 # specifically, they do not record a revision on the root; and
395 # the rev id is assigned to the root during deserialisation -
396 # this masks when a change may have occurred against the basis.
397 # To match this we always issue a delta, because the revision
398 # of the root will always be changing.
399 if basis_inv.has_id(ie.file_id):
400 delta = (basis_inv.id2path(ie.file_id), path,
401 ie.file_id, ie)
402 else:
403 # add
404 delta = (None, path, ie.file_id, ie)
405 self._basis_delta.append(delta)
406 return delta, False, None
407 else:
408 # we don't need to commit this, because the caller already
409 # determined that an existing revision of this file is
410 # appropriate. If it's not being considered for committing then
411 # it and all its parents to the root must be unaltered so
412 # no-change against the basis.
413 if ie.revision == self._new_revision_id:
414 raise AssertionError("Impossible situation, a skipped "
415 "inventory entry (%r) claims to be modified in this "
416 "commit (%r).", (ie, self._new_revision_id))
417 return None, False, None
418 # XXX: Friction: parent_candidates should return a list not a dict
419 # so that we don't have to walk the inventories again.
420 parent_candidate_entries = ie.parent_candidates(parent_invs)
421 head_set = self._heads(ie.file_id, parent_candidate_entries)
422 heads = []
423 for inv in parent_invs:
424 if inv.has_id(ie.file_id):
425 old_rev = inv[ie.file_id].revision
426 if old_rev in head_set:
427 heads.append(inv[ie.file_id].revision)
428 head_set.remove(inv[ie.file_id].revision)
429
430 store = False
431 # now we check to see if we need to write a new record to the
432 # file-graph.
433 # We write a new entry unless there is one head to the ancestors, and
434 # the kind-derived content is unchanged.
435
436 # Cheapest check first: no ancestors, or more the one head in the
437 # ancestors, we write a new node.
438 if len(heads) != 1:
439 store = True
440 if not store:
441 # There is a single head, look it up for comparison
442 parent_entry = parent_candidate_entries[heads[0]]
443 # if the non-content specific data has changed, we'll be writing a
444 # node:
445 if (parent_entry.parent_id != ie.parent_id or
446 parent_entry.name != ie.name):
447 store = True
448 # now we need to do content specific checks:
449 if not store:
450 # if the kind changed the content obviously has
451 if kind != parent_entry.kind:
452 store = True
453 # Stat cache fingerprint feedback for the caller - None as we usually
454 # don't generate one.
455 fingerprint = None
456 if kind == 'file':
457 if content_summary[2] is None:
458 raise ValueError("Files must not have executable = None")
459 if not store:
460 # We can't trust a check of the file length because of content
461 # filtering...
462 if (# if the exec bit has changed we have to store:
463 parent_entry.executable != content_summary[2]):
464 store = True
465 elif parent_entry.text_sha1 == content_summary[3]:
466 # all meta and content is unchanged (using a hash cache
467 # hit to check the sha)
468 ie.revision = parent_entry.revision
469 ie.text_size = parent_entry.text_size
470 ie.text_sha1 = parent_entry.text_sha1
471 ie.executable = parent_entry.executable
472 return self._get_delta(ie, basis_inv, path), False, None
473 else:
474 # Either there is only a hash change(no hash cache entry,
475 # or same size content change), or there is no change on
476 # this file at all.
477 # Provide the parent's hash to the store layer, so that the
478 # content is unchanged we will not store a new node.
479 nostore_sha = parent_entry.text_sha1
480 if store:
481 # We want to record a new node regardless of the presence or
482 # absence of a content change in the file.
483 nostore_sha = None
484 ie.executable = content_summary[2]
485 file_obj, stat_value = tree.get_file_with_stat(ie.file_id, path)
486 try:
487 text = file_obj.read()
488 finally:
489 file_obj.close()
490 try:
491 ie.text_sha1, ie.text_size = self._add_text_to_weave(
492 ie.file_id, text, heads, nostore_sha)
493 # Let the caller know we generated a stat fingerprint.
494 fingerprint = (ie.text_sha1, stat_value)
495 except errors.ExistingContent:
496 # Turns out that the file content was unchanged, and we were
497 # only going to store a new node if it was changed. Carry over
498 # the entry.
499 ie.revision = parent_entry.revision
500 ie.text_size = parent_entry.text_size
501 ie.text_sha1 = parent_entry.text_sha1
502 ie.executable = parent_entry.executable
503 return self._get_delta(ie, basis_inv, path), False, None
504 elif kind == 'directory':
505 if not store:
506 # all data is meta here, nothing specific to directory, so
507 # carry over:
508 ie.revision = parent_entry.revision
509 return self._get_delta(ie, basis_inv, path), False, None
510 self._add_text_to_weave(ie.file_id, '', heads, None)
511 elif kind == 'symlink':
512 current_link_target = content_summary[3]
513 if not store:
514 # symlink target is not generic metadata, check if it has
515 # changed.
516 if current_link_target != parent_entry.symlink_target:
517 store = True
518 if not store:
519 # unchanged, carry over.
520 ie.revision = parent_entry.revision
521 ie.symlink_target = parent_entry.symlink_target
522 return self._get_delta(ie, basis_inv, path), False, None
523 ie.symlink_target = current_link_target
524 self._add_text_to_weave(ie.file_id, '', heads, None)
525 elif kind == 'tree-reference':
526 if not store:
527 if content_summary[3] != parent_entry.reference_revision:
528 store = True
529 if not store:
530 # unchanged, carry over.
531 ie.reference_revision = parent_entry.reference_revision
532 ie.revision = parent_entry.revision
533 return self._get_delta(ie, basis_inv, path), False, None
534 ie.reference_revision = content_summary[3]
535 if ie.reference_revision is None:
536 raise AssertionError("invalid content_summary for nested tree: %r"
537 % (content_summary,))
538 self._add_text_to_weave(ie.file_id, '', heads, None)
539 else:
540 raise NotImplementedError('unknown kind')
541 ie.revision = self._new_revision_id
542 # The initial commit adds a root directory, but this in itself is not
543 # a worthwhile commit.
544 if (self.basis_delta_revision != _mod_revision.NULL_REVISION or
545 path != ""):
546 self._any_changes = True
547 return self._get_delta(ie, basis_inv, path), True, fingerprint
548
549 def record_iter_changes(self, tree, basis_revision_id, iter_changes,288 def record_iter_changes(self, tree, basis_revision_id, iter_changes,
550 _entry_factory=entry_factory):289 _entry_factory=entry_factory):
551 """Record a new tree via iter_changes.290 """Record a new tree via iter_changes.
@@ -805,7 +544,6 @@
805 inv_delta.append((change[1][0], new_path, change[0], entry))544 inv_delta.append((change[1][0], new_path, change[0], entry))
806 if new_path == '':545 if new_path == '':
807 seen_root = True546 seen_root = True
808 self.new_inventory = None
809 # The initial commit adds a root directory, but this in itself is not547 # The initial commit adds a root directory, but this in itself is not
810 # a worthwhile commit.548 # a worthwhile commit.
811 if ((len(inv_delta) > 0 and basis_revision_id != _mod_revision.NULL_REVISION) or549 if ((len(inv_delta) > 0 and basis_revision_id != _mod_revision.NULL_REVISION) or
@@ -832,15 +570,6 @@
832 # the root entry gets versioned properly by this builder.570 # the root entry gets versioned properly by this builder.
833 _versioned_root = True571 _versioned_root = True
834572
835 def _check_root(self, ie, parent_invs, tree):
836 """Helper for record_entry_contents.
837
838 :param ie: An entry being added.
839 :param parent_invs: The inventories of the parent revisions of the
840 commit.
841 :param tree: The tree that is being committed.
842 """
843
844 def _require_root_change(self, tree):573 def _require_root_change(self, tree):
845 """Enforce an appropriate root object change.574 """Enforce an appropriate root object change.
846575
847576
=== modified file 'breezy/commit.py'
--- breezy/commit.py 2017-06-20 22:29:58 +0000
+++ breezy/commit.py 2017-06-20 22:39:47 +0000
@@ -344,9 +344,6 @@
344 self.work_tree.lock_write()344 self.work_tree.lock_write()
345 operation.add_cleanup(self.work_tree.unlock)345 operation.add_cleanup(self.work_tree.unlock)
346 self.parents = self.work_tree.get_parent_ids()346 self.parents = self.work_tree.get_parent_ids()
347 # We can use record_iter_changes IFF no tree references are involved.
348 self.use_record_iter_changes = (
349 not self.branch.repository._format.supports_tree_reference)
350 self.pb = ui.ui_factory.nested_progress_bar()347 self.pb = ui.ui_factory.nested_progress_bar()
351 operation.add_cleanup(self.pb.finished)348 operation.add_cleanup(self.pb.finished)
352 self.basis_revid = self.work_tree.last_revision()349 self.basis_revid = self.work_tree.last_revision()
@@ -371,8 +368,6 @@
371 if self.config_stack is None:368 if self.config_stack is None:
372 self.config_stack = self.work_tree.get_config_stack()369 self.config_stack = self.work_tree.get_config_stack()
373370
374 self._set_specific_file_ids()
375
376 # Setup the progress bar. As the number of files that need to be371 # Setup the progress bar. As the number of files that need to be
377 # committed in unknown, progress is reported as stages.372 # committed in unknown, progress is reported as stages.
378 # We keep track of entries separately though and include that373 # We keep track of entries separately though and include that
@@ -390,7 +385,6 @@
390 self.pb.show_count = True385 self.pb.show_count = True
391 self.pb.show_bar = True386 self.pb.show_bar = True
392387
393 self._gather_parents()
394 # After a merge, a selected file commit is not supported.388 # After a merge, a selected file commit is not supported.
395 # See 'bzr help merge' for an explanation as to why.389 # See 'bzr help merge' for an explanation as to why.
396 if len(self.parents) > 1 and self.specific_files is not None:390 if len(self.parents) > 1 and self.specific_files is not None:
@@ -654,44 +648,21 @@
654 old_revno, old_revid, new_revno, self.rev_id,648 old_revno, old_revid, new_revno, self.rev_id,
655 tree_delta, future_tree)649 tree_delta, future_tree)
656650
657 def _gather_parents(self):
658 """Record the parents of a merge for merge detection."""
659 # TODO: Make sure that this list doesn't contain duplicate
660 # entries and the order is preserved when doing this.
661 if self.use_record_iter_changes:
662 return
663 self.basis_inv = self.basis_tree.root_inventory
664 self.parent_invs = [self.basis_inv]
665 for revision in self.parents[1:]:
666 if self.branch.repository.has_revision(revision):
667 mutter('commit parent revision {%s}', revision)
668 inventory = self.branch.repository.get_inventory(revision)
669 self.parent_invs.append(inventory)
670 else:
671 mutter('commit parent ghost revision {%s}', revision)
672
673 def _update_builder_with_changes(self):651 def _update_builder_with_changes(self):
674 """Update the commit builder with the data about what has changed.652 """Update the commit builder with the data about what has changed.
675 """653 """
676 exclude = self.exclude
677 specific_files = self.specific_files654 specific_files = self.specific_files
678 mutter("Selecting files for commit with filter %r", specific_files)655 mutter("Selecting files for commit with filter %r", specific_files)
679656
680 self._check_strict()657 self._check_strict()
681 if self.use_record_iter_changes:658 iter_changes = self.work_tree.iter_changes(self.basis_tree,
682 iter_changes = self.work_tree.iter_changes(self.basis_tree,659 specific_files=specific_files)
683 specific_files=specific_files)660 if self.exclude:
684 if self.exclude:661 iter_changes = filter_excluded(iter_changes, self.exclude)
685 iter_changes = filter_excluded(iter_changes, self.exclude)662 iter_changes = self._filter_iter_changes(iter_changes)
686 iter_changes = self._filter_iter_changes(iter_changes)663 for file_id, path, fs_hash in self.builder.record_iter_changes(
687 for file_id, path, fs_hash in self.builder.record_iter_changes(664 self.work_tree, self.basis_revid, iter_changes):
688 self.work_tree, self.basis_revid, iter_changes):665 self.work_tree._observed_sha1(file_id, path, fs_hash)
689 self.work_tree._observed_sha1(file_id, path, fs_hash)
690 else:
691 # Build the new inventory
692 self._populate_from_inventory()
693 self._record_unselected()
694 self._report_and_accumulate_deletes()
695666
696 def _filter_iter_changes(self, iter_changes):667 def _filter_iter_changes(self, iter_changes):
697 """Process iter_changes.668 """Process iter_changes.
@@ -745,55 +716,6 @@
745 # Unversion IDs that were found to be deleted716 # Unversion IDs that were found to be deleted
746 self.deleted_ids = deleted_ids717 self.deleted_ids = deleted_ids
747718
748 def _record_unselected(self):
749 # If specific files are selected, then all un-selected files must be
750 # recorded in their previous state. For more details, see
751 # https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
752 if self.specific_files or self.exclude:
753 specific_files = self.specific_files or []
754 for path, old_ie in self.basis_inv.iter_entries():
755 if self.builder.new_inventory.has_id(old_ie.file_id):
756 # already added - skip.
757 continue
758 if (is_inside_any(specific_files, path)
759 and not is_inside_any(self.exclude, path)):
760 # was inside the selected path, and not excluded - if not
761 # present it has been deleted so skip.
762 continue
763 # From here down it was either not selected, or was excluded:
764 # We preserve the entry unaltered.
765 ie = old_ie.copy()
766 # Note: specific file commits after a merge are currently
767 # prohibited. This test is for sanity/safety in case it's
768 # required after that changes.
769 if len(self.parents) > 1:
770 ie.revision = None
771 self.builder.record_entry_contents(ie, self.parent_invs, path,
772 self.basis_tree, None)
773
774 def _report_and_accumulate_deletes(self):
775 if (isinstance(self.basis_inv, Inventory)
776 and isinstance(self.builder.new_inventory, Inventory)):
777 # the older Inventory classes provide a _byid dict, and building a
778 # set from the keys of this dict is substantially faster than even
779 # getting a set of ids from the inventory
780 #
781 # <lifeless> set(dict) is roughly the same speed as
782 # set(iter(dict)) and both are significantly slower than
783 # set(dict.keys())
784 deleted_ids = set(self.basis_inv._byid.keys()) - \
785 set(self.builder.new_inventory._byid.keys())
786 else:
787 deleted_ids = set(self.basis_inv) - set(self.builder.new_inventory)
788 if deleted_ids:
789 self.any_entries_deleted = True
790 deleted = sorted([(self.basis_tree.id2path(file_id), file_id)
791 for file_id in deleted_ids])
792 # XXX: this is not quite directory-order sorting
793 for path, file_id in deleted:
794 self.builder.record_delete(path, file_id)
795 self.reporter.deleted(path)
796
797 def _check_strict(self):719 def _check_strict(self):
798 # XXX: when we use iter_changes this would likely be faster if720 # XXX: when we use iter_changes this would likely be faster if
799 # iter_changes would check for us (even in the presence of721 # iter_changes would check for us (even in the presence of
@@ -803,107 +725,6 @@
803 for unknown in self.work_tree.unknowns():725 for unknown in self.work_tree.unknowns():
804 raise StrictCommitFailed()726 raise StrictCommitFailed()
805727
806 def _populate_from_inventory(self):
807 """Populate the CommitBuilder by walking the working tree inventory."""
808 # Build the revision inventory.
809 #
810 # This starts by creating a new empty inventory. Depending on
811 # which files are selected for commit, and what is present in the
812 # current tree, the new inventory is populated. inventory entries
813 # which are candidates for modification have their revision set to
814 # None; inventory entries that are carried over untouched have their
815 # revision set to their prior value.
816 #
817 # ESEPARATIONOFCONCERNS: this function is diffing and using the diff
818 # results to create a new inventory at the same time, which results
819 # in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
820 # ADHB 11-07-2006
821
822 specific_files = self.specific_files
823 exclude = self.exclude
824 report_changes = self.reporter.is_verbose()
825 deleted_ids = []
826 # A tree of paths that have been deleted. E.g. if foo/bar has been
827 # deleted, then we have {'foo':{'bar':{}}}
828 deleted_paths = {}
829 # XXX: Note that entries may have the wrong kind because the entry does
830 # not reflect the status on disk.
831 # NB: entries will include entries within the excluded ids/paths
832 # because iter_entries_by_dir has no 'exclude' facility today.
833 entries = self.work_tree.iter_entries_by_dir(
834 specific_file_ids=self.specific_file_ids, yield_parents=True)
835 for path, existing_ie in entries:
836 file_id = existing_ie.file_id
837 name = existing_ie.name
838 parent_id = existing_ie.parent_id
839 kind = existing_ie.kind
840 # Skip files that have been deleted from the working tree.
841 # The deleted path ids are also recorded so they can be explicitly
842 # unversioned later.
843 if deleted_paths:
844 path_segments = splitpath(path)
845 deleted_dict = deleted_paths
846 for segment in path_segments:
847 deleted_dict = deleted_dict.get(segment, None)
848 if not deleted_dict:
849 # We either took a path not present in the dict
850 # (deleted_dict was None), or we've reached an empty
851 # child dir in the dict, so are now a sub-path.
852 break
853 else:
854 deleted_dict = None
855 if deleted_dict is not None:
856 # the path has a deleted parent, do not add it.
857 continue
858 if exclude and is_inside_any(exclude, path):
859 # Skip excluded paths. Excluded paths are processed by
860 # _update_builder_with_changes.
861 continue
862 content_summary = self.work_tree.path_content_summary(path)
863 kind = content_summary[0]
864 # Note that when a filter of specific files is given, we must only
865 # skip/record deleted files matching that filter.
866 if not specific_files or is_inside_any(specific_files, path):
867 if kind == 'missing':
868 if not deleted_paths:
869 # path won't have been split yet.
870 path_segments = splitpath(path)
871 deleted_dict = deleted_paths
872 for segment in path_segments:
873 deleted_dict = deleted_dict.setdefault(segment, {})
874 self.reporter.missing(path)
875 self._next_progress_entry()
876 deleted_ids.append(file_id)
877 continue
878 # TODO: have the builder do the nested commit just-in-time IF and
879 # only if needed.
880 if kind == 'tree-reference':
881 # enforce repository nested tree policy.
882 if (not self.work_tree.supports_tree_reference() or
883 # repository does not support it either.
884 not self.branch.repository._format.supports_tree_reference):
885 kind = 'directory'
886 content_summary = (kind, None, None, None)
887 elif self.recursive == 'down':
888 nested_revision_id = self._commit_nested_tree(
889 file_id, path)
890 content_summary = (kind, None, None, nested_revision_id)
891 else:
892 nested_revision_id = self.work_tree.get_reference_revision(file_id)
893 content_summary = (kind, None, None, nested_revision_id)
894
895 # Record an entry for this item
896 # Note: I don't particularly want to have the existing_ie
897 # parameter but the test suite currently (28-Jun-07) breaks
898 # without it thanks to a unicode normalisation issue. :-(
899 definitely_changed = kind != existing_ie.kind
900 self._record_entry(path, file_id, specific_files, kind, name,
901 parent_id, definitely_changed, existing_ie, report_changes,
902 content_summary)
903
904 # Unversion IDs that were found to be deleted
905 self.deleted_ids = deleted_ids
906
907 def _commit_nested_tree(self, file_id, path):728 def _commit_nested_tree(self, file_id, path):
908 "Commit a nested tree."729 "Commit a nested tree."
909 sub_tree = self.work_tree.get_nested_tree(file_id, path)730 sub_tree = self.work_tree.get_nested_tree(file_id, path)
@@ -929,49 +750,6 @@
929 except errors.PointlessCommit:750 except errors.PointlessCommit:
930 return self.work_tree.get_reference_revision(file_id)751 return self.work_tree.get_reference_revision(file_id)
931752
932 def _record_entry(self, path, file_id, specific_files, kind, name,
933 parent_id, definitely_changed, existing_ie, report_changes,
934 content_summary):
935 "Record the new inventory entry for a path if any."
936 # mutter('check %s {%s}', path, file_id)
937 # mutter('%s selected for commit', path)
938 if definitely_changed or existing_ie is None:
939 ie = make_entry(kind, name, parent_id, file_id)
940 else:
941 ie = existing_ie.copy()
942 ie.revision = None
943 # For carried over entries we don't care about the fs hash - the repo
944 # isn't generating a sha, so we're not saving computation time.
945 _, _, fs_hash = self.builder.record_entry_contents(
946 ie, self.parent_invs, path, self.work_tree, content_summary)
947 if report_changes:
948 self._report_change(ie, path)
949 if fs_hash:
950 self.work_tree._observed_sha1(ie.file_id, path, fs_hash)
951 return ie
952
953 def _report_change(self, ie, path):
954 """Report a change to the user.
955
956 The change that has occurred is described relative to the basis
957 inventory.
958 """
959 if (self.basis_inv.has_id(ie.file_id)):
960 basis_ie = self.basis_inv[ie.file_id]
961 else:
962 basis_ie = None
963 change = ie.describe_change(basis_ie, ie)
964 if change in (InventoryEntry.RENAMED,
965 InventoryEntry.MODIFIED_AND_RENAMED):
966 old_path = self.basis_inv.id2path(ie.file_id)
967 self.reporter.renamed(change, old_path, path)
968 self._next_progress_entry()
969 else:
970 if change == gettext('unchanged'):
971 return
972 self.reporter.snapshot_change(change, path)
973 self._next_progress_entry()
974
975 def _set_progress_stage(self, name, counter=False):753 def _set_progress_stage(self, name, counter=False):
976 """Set the progress stage and emit an update to the progress bar."""754 """Set the progress stage and emit an update to the progress bar."""
977 self.pb_stage_name = name755 self.pb_stage_name = name
@@ -994,20 +772,3 @@
994 else:772 else:
995 text = gettext("%s - Stage") % (self.pb_stage_name, )773 text = gettext("%s - Stage") % (self.pb_stage_name, )
996 self.pb.update(text, self.pb_stage_count, self.pb_stage_total)774 self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
997
998 def _set_specific_file_ids(self):
999 """populate self.specific_file_ids if we will use it."""
1000 if not self.use_record_iter_changes:
1001 # If provided, ensure the specified files are versioned
1002 if self.specific_files is not None:
1003 # Note: This routine is being called because it raises
1004 # PathNotVersionedError as a side effect of finding the IDs. We
1005 # later use the ids we found as input to the working tree
1006 # inventory iterator, so we only consider those ids rather than
1007 # examining the whole tree again.
1008 # XXX: Dont we have filter_unversioned to do this more
1009 # cheaply?
1010 self.specific_file_ids = tree.find_ids_across_trees(
1011 self.specific_files, [self.basis_tree, self.work_tree])
1012 else:
1013 self.specific_file_ids = None
1014775
=== modified file 'breezy/plugins/fastimport/revision_store.py'
--- breezy/plugins/fastimport/revision_store.py 2017-06-13 00:24:51 +0000
+++ breezy/plugins/fastimport/revision_store.py 2017-06-20 22:39:47 +0000
@@ -417,7 +417,7 @@
417 rev.inv_sha1 = builder.inv_sha1417 rev.inv_sha1 = builder.inv_sha1
418 config = builder._config_stack418 config = builder._config_stack
419 builder.repository.add_revision(builder._new_revision_id, rev,419 builder.repository.add_revision(builder._new_revision_id, rev,
420 builder.new_inventory)420 builder.revision_tree().root_inventory)
421 if self._graph is not None:421 if self._graph is not None:
422 # TODO: Use StaticTuple and .intern() for these things422 # TODO: Use StaticTuple and .intern() for these things
423 self._graph.add_node(builder._new_revision_id, rev.parent_ids)423 self._graph.add_node(builder._new_revision_id, rev.parent_ids)
424424
=== modified file 'breezy/repository.py'
--- breezy/repository.py 2017-06-20 00:39:00 +0000
+++ breezy/repository.py 2017-06-20 22:39:47 +0000
@@ -74,8 +74,6 @@
7474
75 # all clients should supply tree roots.75 # all clients should supply tree roots.
76 record_root_entry = True76 record_root_entry = True
77 # whether this commit builder supports the record_entry_contents interface
78 supports_record_entry_contents = False
79 # whether this commit builder will automatically update the branch that is77 # whether this commit builder will automatically update the branch that is
80 # being committed to78 # being committed to
81 updates_branch = False79 updates_branch = False
8280
=== modified file 'breezy/tests/per_repository/test_commit_builder.py'
--- breezy/tests/per_repository/test_commit_builder.py 2017-06-20 00:51:07 +0000
+++ breezy/tests/per_repository/test_commit_builder.py 2017-06-20 22:39:47 +0000
@@ -47,34 +47,6 @@
47 branch.repository.commit_write_group()47 branch.repository.commit_write_group()
48 branch.repository.unlock()48 branch.repository.unlock()
4949
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 def test_finish_inventory_record_iter_changes(self):50 def test_finish_inventory_record_iter_changes(self):
79 tree = self.make_branch_and_tree(".")51 tree = self.make_branch_and_tree(".")
80 tree.lock_write()52 tree.lock_write()
@@ -92,20 +64,6 @@
92 finally:64 finally:
93 tree.unlock()65 tree.unlock()
9466
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 def test_abort_record_iter_changes(self):67 def test_abort_record_iter_changes(self):
110 tree = self.make_branch_and_tree(".")68 tree = self.make_branch_and_tree(".")
111 tree.lock_write()69 tree.lock_write()
@@ -165,41 +123,6 @@
165 actually_updated_branch = (tree.branch.last_revision() == rev_id)123 actually_updated_branch = (tree.branch.last_revision() == rev_id)
166 self.assertEqual(actually_updated_branch, will_update_branch)124 self.assertEqual(actually_updated_branch, will_update_branch)
167125
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 def test_commit_with_revision_id_record_iter_changes(self):126 def test_commit_with_revision_id_record_iter_changes(self):
204 tree = self.make_branch_and_tree(".")127 tree = self.make_branch_and_tree(".")
205 tree.lock_write()128 tree.lock_write()
@@ -256,64 +179,6 @@
256 finally:179 finally:
257 tree.unlock()180 tree.unlock()
258181
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 ie = inventory.make_entry('directory', '', None,
290 tree.get_root_id())
291 delta, version_recorded, fs_hash = builder.record_entry_contents(
292 ie, [parent_tree.root_inventory], '', tree,
293 tree.path_content_summary(''))
294 # Regardless of repository root behaviour we should consider this a
295 # pointless commit.
296 self.assertFalse(builder.any_changes())
297 self.assertFalse(version_recorded)
298 # if the repository format recorded a new root revision, that
299 # should be in the delta
300 got_new_revision = ie.revision != old_revision_id
301 if got_new_revision:
302 self.assertEqual(('', '', ie.file_id, ie), delta)
303 # The delta should be tracked
304 self.assertEqual(delta, builder.get_basis_delta()[-1])
305 else:
306 self.assertEqual(None, delta)
307 # Directories do not get hashed.
308 self.assertEqual(None, fs_hash)
309 builder.abort()
310 except:
311 builder.abort()
312 tree.unlock()
313 raise
314 else:
315 tree.unlock()
316
317 def test_commit_unchanged_root_record_iter_changes(self):182 def test_commit_unchanged_root_record_iter_changes(self):
318 tree = self.make_branch_and_tree(".")183 tree = self.make_branch_and_tree(".")
319 old_revision_id = tree.commit('')184 old_revision_id = tree.commit('')
@@ -338,105 +203,6 @@
338 finally:203 finally:
339 tree.unlock()204 tree.unlock()
340205
341 def test_commit_record_entry_contents(self):
342 tree = self.make_branch_and_tree(".")
343 tree.lock_write()
344 try:
345 builder = tree.branch.get_commit_builder([])
346 if not builder.supports_record_entry_contents:
347 raise tests.TestNotApplicable("CommitBuilder doesn't "
348 "support record_entry_contents")
349 self.record_root(builder, tree)
350 builder.finish_inventory()
351 rev_id = builder.commit('foo bar')
352 finally:
353 tree.unlock()
354 self.assertNotEqual(None, rev_id)
355 self.assertTrue(tree.branch.repository.has_revision(rev_id))
356 # the revision id must be set on the inventory when saving it. This does not
357 # precisely test that - a repository that wants to can add it on deserialisation,
358 # but thats all the current contract guarantees anyway.
359 self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
360
361 def test_get_basis_delta(self):
362 tree = self.make_branch_and_tree(".")
363 self.build_tree(["foo"])
364 tree.add(["foo"], ["foo-id"])
365 old_revision_id = tree.commit("added foo")
366 tree.lock_write()
367 try:
368 self.build_tree(['bar'])
369 tree.add(['bar'], ['bar-id'])
370 basis = tree.branch.repository.revision_tree(old_revision_id)
371 basis.lock_read()
372 self.addCleanup(basis.unlock)
373 builder = tree.branch.get_commit_builder([old_revision_id])
374 total_delta = []
375 try:
376 if not builder.supports_record_entry_contents:
377 raise tests.TestNotApplicable("CommitBuilder doesn't "
378 "support record_entry_contents")
379 parent_invs = [basis.root_inventory]
380 if builder.record_root_entry:
381 ie = basis.root_inventory.root.copy()
382 delta, _, _ = builder.record_entry_contents(ie, parent_invs,
383 '', tree, tree.path_content_summary(''))
384 if delta is not None:
385 total_delta.append(delta)
386 delta = builder.record_delete("foo", "foo-id")
387 total_delta.append(delta)
388 new_bar = inventory.make_entry('file', 'bar',
389 parent_id=tree.get_root_id(), file_id='bar-id')
390 delta, _, _ = builder.record_entry_contents(new_bar, parent_invs,
391 'bar', tree, tree.path_content_summary('bar'))
392 total_delta.append(delta)
393 # All actions should have been recorded in the basis_delta
394 self.assertEqual(total_delta, builder.get_basis_delta())
395 builder.finish_inventory()
396 builder.commit('delete foo, add bar')
397 except:
398 tree.branch.repository.abort_write_group()
399 raise
400 finally:
401 tree.unlock()
402
403 def test_record_delete(self):
404 tree = self.make_branch_and_tree(".")
405 self.build_tree(["foo"])
406 tree.add(["foo"], ["foo-id"])
407 rev_id = tree.commit("added foo")
408 # Remove the inventory details for foo-id, because
409 # record_entry_contents ends up copying root verbatim.
410 tree.unversion(["foo-id"])
411 tree.lock_write()
412 try:
413 basis = tree.branch.repository.revision_tree(rev_id)
414 builder = tree.branch.get_commit_builder([rev_id])
415 try:
416 if not builder.supports_record_entry_contents:
417 raise tests.TestNotApplicable("CommitBuilder doesn't "
418 "support record_entry_contents")
419 if builder.record_root_entry is True:
420 parent_invs = [basis.root_inventory]
421 del basis.root_inventory.root.children['foo']
422 builder.record_entry_contents(basis.root_inventory.root,
423 parent_invs, '', tree, tree.path_content_summary(''))
424 # the delta should be returned, and recorded in _basis_delta
425 delta = builder.record_delete("foo", "foo-id")
426 self.assertEqual(("foo", None, "foo-id", None), delta)
427 self.assertEqual(delta, builder.get_basis_delta()[-1])
428 builder.finish_inventory()
429 rev_id2 = builder.commit('delete foo')
430 except:
431 tree.branch.repository.abort_write_group()
432 raise
433 finally:
434 tree.unlock()
435 rev_tree = builder.revision_tree()
436 rev_tree.lock_read()
437 self.addCleanup(rev_tree.unlock)
438 self.assertFalse(rev_tree.path2id('foo'))
439
440 def test_record_delete_record_iter_changes(self):206 def test_record_delete_record_iter_changes(self):
441 tree = self.make_branch_and_tree(".")207 tree = self.make_branch_and_tree(".")
442 self.build_tree(["foo"])208 self.build_tree(["foo"])
@@ -466,44 +232,6 @@
466 self.addCleanup(rev_tree.unlock)232 self.addCleanup(rev_tree.unlock)
467 self.assertFalse(rev_tree.path2id('foo'))233 self.assertFalse(rev_tree.path2id('foo'))
468234
469 def test_record_delete_without_notification(self):
470 tree = self.make_branch_and_tree(".")
471 self.build_tree(["foo"])
472 tree.add(["foo"], ["foo-id"])
473 rev_id = tree.commit("added foo")
474 tree.lock_write()
475 try:
476 builder = tree.branch.get_commit_builder([rev_id])
477 try:
478 if not builder.supports_record_entry_contents:
479 raise tests.TestNotApplicable("CommitBuilder doesn't "
480 "support record_entry_contents")
481 self.record_root(builder, tree)
482 builder.record_delete("foo", "foo-id")
483 finally:
484 tree.branch.repository.abort_write_group()
485 finally:
486 tree.unlock()
487
488 def test_revision_tree_record_entry_contents(self):
489 tree = self.make_branch_and_tree(".")
490 tree.lock_write()
491 try:
492 builder = tree.branch.get_commit_builder([])
493 if not builder.supports_record_entry_contents:
494 raise tests.TestNotApplicable("CommitBuilder doesn't "
495 "support record_entry_contents")
496 self.record_root(builder, tree)
497 builder.finish_inventory()
498 rev_id = builder.commit('foo bar')
499 finally:
500 tree.unlock()
501 rev_tree = builder.revision_tree()
502 # Just a couple simple tests to ensure that it actually follows
503 # the RevisionTree api.
504 self.assertEqual(rev_id, rev_tree.get_revision_id())
505 self.assertEqual([], rev_tree.get_parent_ids())
506
507 def test_revision_tree_record_iter_changes(self):235 def test_revision_tree_record_iter_changes(self):
508 tree = self.make_branch_and_tree(".")236 tree = self.make_branch_and_tree(".")
509 tree.lock_write()237 tree.lock_write()
@@ -573,8 +301,6 @@
573301
574 def _commit_check_unchanged(self, tree, name, file_id, mini_commit=None):302 def _commit_check_unchanged(self, tree, name, file_id, mini_commit=None):
575 rev1 = tree.commit('')303 rev1 = tree.commit('')
576 if mini_commit is None:
577 mini_commit = self.mini_commit
578 rev2 = mini_commit(tree, name, name, False, False)304 rev2 = mini_commit(tree, name, name, False, False)
579 tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])305 tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
580 self.assertEqual(rev1, tree1.get_file_revision(file_id))306 self.assertEqual(rev1, tree1.get_file_revision(file_id))
@@ -587,12 +313,6 @@
587 # committing without changing a dir does not change the last modified.313 # committing without changing a dir does not change the last modified.
588 tree = self.make_branch_and_tree('.')314 tree = self.make_branch_and_tree('.')
589 self.build_tree(['dir/'])315 self.build_tree(['dir/'])
590 self._add_commit_check_unchanged(tree, 'dir')
591
592 def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
593 # committing without changing a dir does not change the last modified.
594 tree = self.make_branch_and_tree('.')
595 self.build_tree(['dir/'])
596 self._add_commit_check_unchanged(tree, 'dir',316 self._add_commit_check_unchanged(tree, 'dir',
597 mini_commit=self.mini_commit_record_iter_changes)317 mini_commit=self.mini_commit_record_iter_changes)
598318
@@ -618,12 +338,6 @@
618 # committing without changing a file does not change the last modified.338 # committing without changing a file does not change the last modified.
619 tree = self.make_branch_and_tree('.')339 tree = self.make_branch_and_tree('.')
620 self.build_tree(['file'])340 self.build_tree(['file'])
621 self._add_commit_check_unchanged(tree, 'file')
622
623 def test_last_modified_revision_after_commit_file_unchanged_ric(self):
624 # committing without changing a file does not change the last modified.
625 tree = self.make_branch_and_tree('.')
626 self.build_tree(['file'])
627 self._add_commit_check_unchanged(tree, 'file',341 self._add_commit_check_unchanged(tree, 'file',
628 mini_commit=self.mini_commit_record_iter_changes)342 mini_commit=self.mini_commit_record_iter_changes)
629343
@@ -632,13 +346,6 @@
632 self.requireFeature(features.SymlinkFeature)346 self.requireFeature(features.SymlinkFeature)
633 tree = self.make_branch_and_tree('.')347 tree = self.make_branch_and_tree('.')
634 os.symlink('target', 'link')348 os.symlink('target', 'link')
635 self._add_commit_check_unchanged(tree, 'link')
636
637 def test_last_modified_revision_after_commit_link_unchanged_ric(self):
638 # committing without changing a link does not change the last modified.
639 self.requireFeature(features.SymlinkFeature)
640 tree = self.make_branch_and_tree('.')
641 os.symlink('target', 'link')
642 self._add_commit_check_unchanged(tree, 'link',349 self._add_commit_check_unchanged(tree, 'link',
643 mini_commit=self.mini_commit_record_iter_changes)350 mini_commit=self.mini_commit_record_iter_changes)
644351
@@ -650,18 +357,6 @@
650 try:357 try:
651 tree.add_reference(subtree)358 tree.add_reference(subtree)
652 self._commit_check_unchanged(tree, 'reference',359 self._commit_check_unchanged(tree, 'reference',
653 subtree.get_root_id())
654 except errors.UnsupportedOperation:
655 return
656
657 def test_last_modified_revision_after_commit_reference_unchanged_ric(self):
658 # committing without changing a subtree does not change the last
659 # modified.
660 tree = self.make_branch_and_tree('.')
661 subtree = self.make_reference('reference')
662 try:
663 tree.add_reference(subtree)
664 self._commit_check_unchanged(tree, 'reference',
665 subtree.get_root_id(),360 subtree.get_root_id(),
666 mini_commit=self.mini_commit_record_iter_changes)361 mini_commit=self.mini_commit_record_iter_changes)
667 except errors.UnsupportedOperation:362 except errors.UnsupportedOperation:
@@ -685,12 +380,6 @@
685 # renaming a dir changes the last modified.380 # renaming a dir changes the last modified.
686 tree = self.make_branch_and_tree('.')381 tree = self.make_branch_and_tree('.')
687 self.build_tree(['dir/'])382 self.build_tree(['dir/'])
688 self._add_commit_renamed_check_changed(tree, 'dir')
689
690 def test_last_modified_revision_after_rename_dir_changes_ric(self):
691 # renaming a dir changes the last modified.
692 tree = self.make_branch_and_tree('.')
693 self.build_tree(['dir/'])
694 self._add_commit_renamed_check_changed(tree, 'dir',383 self._add_commit_renamed_check_changed(tree, 'dir',
695 mini_commit=self.mini_commit_record_iter_changes)384 mini_commit=self.mini_commit_record_iter_changes)
696385
@@ -699,13 +388,6 @@
699 tree = self.make_branch_and_tree('.')388 tree = self.make_branch_and_tree('.')
700 self.build_tree(['file'])389 self.build_tree(['file'])
701 self._add_commit_renamed_check_changed(tree, 'file',390 self._add_commit_renamed_check_changed(tree, 'file',
702 expect_fs_hash=True)
703
704 def test_last_modified_revision_after_rename_file_changes_ric(self):
705 # renaming a file changes the last modified.
706 tree = self.make_branch_and_tree('.')
707 self.build_tree(['file'])
708 self._add_commit_renamed_check_changed(tree, 'file',
709 expect_fs_hash=True,391 expect_fs_hash=True,
710 mini_commit=self.mini_commit_record_iter_changes)392 mini_commit=self.mini_commit_record_iter_changes)
711393
@@ -714,13 +396,6 @@
714 self.requireFeature(features.SymlinkFeature)396 self.requireFeature(features.SymlinkFeature)
715 tree = self.make_branch_and_tree('.')397 tree = self.make_branch_and_tree('.')
716 os.symlink('target', 'link')398 os.symlink('target', 'link')
717 self._add_commit_renamed_check_changed(tree, 'link')
718
719 def test_last_modified_revision_after_rename_link_changes_ric(self):
720 # renaming a link changes the last modified.
721 self.requireFeature(features.SymlinkFeature)
722 tree = self.make_branch_and_tree('.')
723 os.symlink('target', 'link')
724 self._add_commit_renamed_check_changed(tree, 'link',399 self._add_commit_renamed_check_changed(tree, 'link',
725 mini_commit=self.mini_commit_record_iter_changes)400 mini_commit=self.mini_commit_record_iter_changes)
726401
@@ -731,17 +406,6 @@
731 try:406 try:
732 tree.add_reference(subtree)407 tree.add_reference(subtree)
733 self._commit_renamed_check_changed(tree, 'reference',408 self._commit_renamed_check_changed(tree, 'reference',
734 subtree.get_root_id())
735 except errors.UnsupportedOperation:
736 return
737
738 def test_last_modified_revision_after_rename_ref_changes_ric(self):
739 # renaming a reference changes the last modified.
740 tree = self.make_branch_and_tree('.')
741 subtree = self.make_reference('reference')
742 try:
743 tree.add_reference(subtree)
744 self._commit_renamed_check_changed(tree, 'reference',
745 subtree.get_root_id(),409 subtree.get_root_id(),
746 mini_commit=self.mini_commit_record_iter_changes)410 mini_commit=self.mini_commit_record_iter_changes)
747 except errors.UnsupportedOperation:411 except errors.UnsupportedOperation:
@@ -760,12 +424,6 @@
760 # reparenting a dir changes the last modified.424 # reparenting a dir changes the last modified.
761 tree = self.make_branch_and_tree('.')425 tree = self.make_branch_and_tree('.')
762 self.build_tree(['dir/'])426 self.build_tree(['dir/'])
763 self._add_commit_reparent_check_changed(tree, 'dir')
764
765 def test_last_modified_revision_after_reparent_dir_changes_ric(self):
766 # reparenting a dir changes the last modified.
767 tree = self.make_branch_and_tree('.')
768 self.build_tree(['dir/'])
769 self._add_commit_reparent_check_changed(tree, 'dir',427 self._add_commit_reparent_check_changed(tree, 'dir',
770 mini_commit=self.mini_commit_record_iter_changes)428 mini_commit=self.mini_commit_record_iter_changes)
771429
@@ -774,13 +432,6 @@
774 tree = self.make_branch_and_tree('.')432 tree = self.make_branch_and_tree('.')
775 self.build_tree(['file'])433 self.build_tree(['file'])
776 self._add_commit_reparent_check_changed(tree, 'file',434 self._add_commit_reparent_check_changed(tree, 'file',
777 expect_fs_hash=True)
778
779 def test_last_modified_revision_after_reparent_file_changes_ric(self):
780 # reparenting a file changes the last modified.
781 tree = self.make_branch_and_tree('.')
782 self.build_tree(['file'])
783 self._add_commit_reparent_check_changed(tree, 'file',
784 expect_fs_hash=True,435 expect_fs_hash=True,
785 mini_commit=self.mini_commit_record_iter_changes)436 mini_commit=self.mini_commit_record_iter_changes)
786437
@@ -789,13 +440,6 @@
789 self.requireFeature(features.SymlinkFeature)440 self.requireFeature(features.SymlinkFeature)
790 tree = self.make_branch_and_tree('.')441 tree = self.make_branch_and_tree('.')
791 os.symlink('target', 'link')442 os.symlink('target', 'link')
792 self._add_commit_reparent_check_changed(tree, 'link')
793
794 def test_last_modified_revision_after_reparent_link_changes_ric(self):
795 # reparenting a link changes the last modified.
796 self.requireFeature(features.SymlinkFeature)
797 tree = self.make_branch_and_tree('.')
798 os.symlink('target', 'link')
799 self._add_commit_reparent_check_changed(tree, 'link',443 self._add_commit_reparent_check_changed(tree, 'link',
800 mini_commit=self.mini_commit_record_iter_changes)444 mini_commit=self.mini_commit_record_iter_changes)
801445
@@ -812,8 +456,6 @@
812 expect_fs_hash=False, mini_commit=None):456 expect_fs_hash=False, mini_commit=None):
813 rev1 = tree.commit('')457 rev1 = tree.commit('')
814 changer()458 changer()
815 if mini_commit is None:
816 mini_commit = self.mini_commit
817 rev2 = mini_commit(tree, name, tree.id2path(file_id),459 rev2 = mini_commit(tree, name, tree.id2path(file_id),
818 expect_fs_hash=expect_fs_hash)460 expect_fs_hash=expect_fs_hash)
819 tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])461 tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
@@ -824,97 +466,6 @@
824 expected_graph[(file_id, rev2)] = ((file_id, rev1),)466 expected_graph[(file_id, rev2)] = ((file_id, rev1),)
825 self.assertFileGraph(expected_graph, tree, (file_id, rev2))467 self.assertFileGraph(expected_graph, tree, (file_id, rev2))
826468
827 def mini_commit(self, tree, name, new_name, records_version=True,
828 delta_against_basis=True, expect_fs_hash=False):
829 """Perform a miniature commit looking for record entry results.
830
831 :param tree: The tree to commit.
832 :param name: The path in the basis tree of the tree being committed.
833 :param new_name: The path in the tree being committed.
834 :param records_version: True if the commit of new_name is expected to
835 record a new version.
836 :param delta_against_basis: True of the commit of new_name is expected
837 to have a delta against the basis.
838 :param expect_fs_hash: True or false to indicate whether we expect a
839 file hash to be returned from the record_entry_contents call.
840 """
841 tree.lock_write()
842 try:
843 # mini manual commit here so we can check the return of
844 # record_entry_contents.
845 parent_ids = tree.get_parent_ids()
846 builder = tree.branch.get_commit_builder(parent_ids)
847 try:
848 if not builder.supports_record_entry_contents:
849 raise tests.TestNotApplicable("CommitBuilder doesn't "
850 "support record_entry_contents")
851 parent_tree = tree.basis_tree()
852 parent_tree.lock_read()
853 self.addCleanup(parent_tree.unlock)
854 parent_invs = [parent_tree.root_inventory]
855 for parent_id in parent_ids[1:]:
856 parent_invs.append(tree.branch.repository.revision_tree(
857 parent_id).root_inventory)
858 # root
859 builder.record_entry_contents(
860 inventory.make_entry('directory', '', None,
861 tree.get_root_id()), parent_invs, '', tree,
862 tree.path_content_summary(''))
863 def commit_id(file_id):
864 old_ie = tree.root_inventory[file_id]
865 path = tree.id2path(file_id)
866 ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
867 old_ie.parent_id, file_id)
868 content_summary = tree.path_content_summary(path)
869 if content_summary[0] == 'tree-reference':
870 content_summary = content_summary[:3] + (
871 tree.get_reference_revision(file_id),)
872 return builder.record_entry_contents(ie, parent_invs, path,
873 tree, content_summary)
874
875 file_id = tree.path2id(new_name)
876 parent_id = tree.root_inventory[file_id].parent_id
877 if parent_id != tree.get_root_id():
878 commit_id(parent_id)
879 # because a change of some sort is meant to have occurred,
880 # recording the entry must return True.
881 delta, version_recorded, fs_hash = commit_id(file_id)
882 if records_version:
883 self.assertTrue(version_recorded)
884 else:
885 self.assertFalse(version_recorded)
886 if expect_fs_hash:
887 tree_file_stat = tree.get_file_with_stat(file_id)
888 tree_file_stat[0].close()
889 self.assertEqual(2, len(fs_hash))
890 self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
891 self.assertEqualStat(tree_file_stat[1], fs_hash[1])
892 else:
893 self.assertEqual(None, fs_hash)
894 new_entry = builder.new_inventory[file_id]
895 if delta_against_basis:
896 expected_delta = (name, new_name, file_id, new_entry)
897 # The delta should be recorded
898 self.assertEqual(expected_delta,
899 builder.get_basis_delta()[-1])
900 else:
901 expected_delta = None
902 self.assertEqual(expected_delta, delta)
903 builder.finish_inventory()
904 except:
905 builder.abort()
906 raise
907 else:
908 rev2 = builder.commit('')
909 except:
910 tree.unlock()
911 raise
912 try:
913 tree.set_parent_ids([rev2])
914 finally:
915 tree.unlock()
916 return rev2
917
918 def mini_commit_record_iter_changes(self, tree, name, new_name,469 def mini_commit_record_iter_changes(self, tree, name, new_name,
919 records_version=True, delta_against_basis=True, expect_fs_hash=False):470 records_version=True, delta_against_basis=True, expect_fs_hash=False):
920 """Perform a miniature commit looking for record entry results.471 """Perform a miniature commit looking for record entry results.
@@ -934,7 +485,7 @@
934 tree.lock_write()485 tree.lock_write()
935 try:486 try:
936 # mini manual commit here so we can check the return of487 # mini manual commit here so we can check the return of
937 # record_entry_contents.488 # record_iter_changes
938 parent_ids = tree.get_parent_ids()489 parent_ids = tree.get_parent_ids()
939 builder = tree.branch.get_commit_builder(parent_ids)490 builder = tree.branch.get_commit_builder(parent_ids)
940 parent_tree = tree.basis_tree()491 parent_tree = tree.basis_tree()
@@ -958,14 +509,12 @@
958 self.assertEqualStat(result[2][1], tree_file_stat[1])509 self.assertEqualStat(result[2][1], tree_file_stat[1])
959 else:510 else:
960 self.assertEqual([], result)511 self.assertEqual([], result)
961 self.assertIs(None, builder.new_inventory)
962 builder.finish_inventory()512 builder.finish_inventory()
963 if tree.branch.repository._format.supports_full_versioned_files:513 if tree.branch.repository._format.supports_full_versioned_files:
964 inv_key = (builder._new_revision_id,)514 inv_key = (builder._new_revision_id,)
965 inv_sha1 = tree.branch.repository.inventories.get_sha1s(515 inv_sha1 = tree.branch.repository.inventories.get_sha1s(
966 [inv_key])[inv_key]516 [inv_key])[inv_key]
967 self.assertEqual(inv_sha1, builder.inv_sha1)517 self.assertEqual(inv_sha1, builder.inv_sha1)
968 self.assertIs(None, builder.new_inventory)
969 rev2 = builder.commit('')518 rev2 = builder.commit('')
970 delta = builder.get_basis_delta()519 delta = builder.get_basis_delta()
971 delta_dict = dict((change[2], change) for change in delta)520 delta_dict = dict((change[2], change) for change in delta)
@@ -1009,29 +558,10 @@
1009 def change_file():558 def change_file():
1010 tree.put_file_bytes_non_atomic('fileid', 'new content')559 tree.put_file_bytes_non_atomic('fileid', 'new content')
1011 self._add_commit_change_check_changed(tree, 'file', change_file,560 self._add_commit_change_check_changed(tree, 'file', change_file,
1012 expect_fs_hash=True)
1013
1014 def test_last_modified_revision_after_content_file_changes_ric(self):
1015 # altering a file changes the last modified.
1016 tree = self.make_branch_and_tree('.')
1017 self.build_tree(['file'])
1018 def change_file():
1019 tree.put_file_bytes_non_atomic('fileid', 'new content')
1020 self._add_commit_change_check_changed(tree, 'file', change_file,
1021 expect_fs_hash=True,561 expect_fs_hash=True,
1022 mini_commit=self.mini_commit_record_iter_changes)562 mini_commit=self.mini_commit_record_iter_changes)
1023563
1024 def test_last_modified_revision_after_content_link_changes(self):564 def _test_last_mod_rev_after_content_link_changes(
1025 # changing a link changes the last modified.
1026 self.requireFeature(features.SymlinkFeature)
1027 tree = self.make_branch_and_tree('.')
1028 os.symlink('target', 'link')
1029 def change_link():
1030 os.unlink('link')
1031 os.symlink('newtarget', 'link')
1032 self._add_commit_change_check_changed(tree, 'link', change_link)
1033
1034 def _test_last_mod_rev_after_content_link_changes_ric(
1035 self, link, target, newtarget, file_id=None):565 self, link, target, newtarget, file_id=None):
1036 if file_id is None:566 if file_id is None:
1037 file_id = link567 file_id = link
@@ -1047,13 +577,13 @@
1047 mini_commit=self.mini_commit_record_iter_changes,577 mini_commit=self.mini_commit_record_iter_changes,
1048 file_id=file_id)578 file_id=file_id)
1049579
1050 def test_last_modified_rev_after_content_link_changes_ric(self):580 def test_last_modified_rev_after_content_link_changes(self):
1051 self._test_last_mod_rev_after_content_link_changes_ric(581 self._test_last_mod_rev_after_content_link_changes(
1052 'link', 'target', 'newtarget')582 'link', 'target', 'newtarget')
1053583
1054 def test_last_modified_rev_after_content_unicode_link_changes_ric(self):584 def test_last_modified_rev_after_content_unicode_link_changes(self):
1055 self.requireFeature(features.UnicodeFilenameFeature)585 self.requireFeature(features.UnicodeFilenameFeature)
1056 self._test_last_mod_rev_after_content_link_changes_ric(586 self._test_last_mod_rev_after_content_link_changes(
1057 u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget',587 u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget',
1058588
1059 file_id=u'li\u1234nk'.encode('UTF-8'))589 file_id=u'li\u1234nk'.encode('UTF-8'))
@@ -1075,8 +605,6 @@
1075 rev2 = self._rename_in_tree(tree1, name)605 rev2 = self._rename_in_tree(tree1, name)
1076 rev3 = self._rename_in_tree(tree2, name)606 rev3 = self._rename_in_tree(tree2, name)
1077 tree1.merge_from_branch(tree2.branch)607 tree1.merge_from_branch(tree2.branch)
1078 if mini_commit is None:
1079 mini_commit = self.mini_commit
1080 rev4 = mini_commit(tree1, 'new_' + name, 'new_' + name,608 rev4 = mini_commit(tree1, 'new_' + name, 'new_' + name,
1081 expect_fs_hash=expect_fs_hash)609 expect_fs_hash=expect_fs_hash)
1082 tree3, = self._get_revtrees(tree1, [rev4])610 tree3, = self._get_revtrees(tree1, [rev4])
@@ -1093,12 +621,6 @@
1093 # merge a dir changes the last modified.621 # merge a dir changes the last modified.
1094 tree1 = self.make_branch_and_tree('t1')622 tree1 = self.make_branch_and_tree('t1')
1095 self.build_tree(['t1/dir/'])623 self.build_tree(['t1/dir/'])
1096 self._commit_sprout_rename_merge(tree1, 'dir')
1097
1098 def test_last_modified_revision_after_merge_dir_changes_ric(self):
1099 # merge a dir changes the last modified.
1100 tree1 = self.make_branch_and_tree('t1')
1101 self.build_tree(['t1/dir/'])
1102 self._commit_sprout_rename_merge(tree1, 'dir',624 self._commit_sprout_rename_merge(tree1, 'dir',
1103 mini_commit=self.mini_commit_record_iter_changes)625 mini_commit=self.mini_commit_record_iter_changes)
1104626
@@ -1106,12 +628,6 @@
1106 # merge a file changes the last modified.628 # merge a file changes the last modified.
1107 tree1 = self.make_branch_and_tree('t1')629 tree1 = self.make_branch_and_tree('t1')
1108 self.build_tree(['t1/file'])630 self.build_tree(['t1/file'])
1109 self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
1110
1111 def test_last_modified_revision_after_merge_file_changes_ric(self):
1112 # merge a file changes the last modified.
1113 tree1 = self.make_branch_and_tree('t1')
1114 self.build_tree(['t1/file'])
1115 self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True,631 self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True,
1116 mini_commit=self.mini_commit_record_iter_changes)632 mini_commit=self.mini_commit_record_iter_changes)
1117633
@@ -1120,13 +636,6 @@
1120 self.requireFeature(features.SymlinkFeature)636 self.requireFeature(features.SymlinkFeature)
1121 tree1 = self.make_branch_and_tree('t1')637 tree1 = self.make_branch_and_tree('t1')
1122 os.symlink('target', 't1/link')638 os.symlink('target', 't1/link')
1123 self._commit_sprout_rename_merge(tree1, 'link')
1124
1125 def test_last_modified_revision_after_merge_link_changes_ric(self):
1126 # merge a link changes the last modified.
1127 self.requireFeature(features.SymlinkFeature)
1128 tree1 = self.make_branch_and_tree('t1')
1129 os.symlink('target', 't1/link')
1130 self._commit_sprout_rename_merge(tree1, 'link',639 self._commit_sprout_rename_merge(tree1, 'link',
1131 mini_commit=self.mini_commit_record_iter_changes)640 mini_commit=self.mini_commit_record_iter_changes)
1132641
@@ -1140,8 +649,6 @@
1140 # change on the other side to merge back649 # change on the other side to merge back
1141 rev2 = self._rename_in_tree(tree2, name)650 rev2 = self._rename_in_tree(tree2, name)
1142 tree1.merge_from_branch(tree2.branch)651 tree1.merge_from_branch(tree2.branch)
1143 if mini_commit is None:
1144 mini_commit = self.mini_commit
1145 def _check_graph(in_tree, changed_in_tree):652 def _check_graph(in_tree, changed_in_tree):
1146 rev3 = mini_commit(in_tree, name, 'new_' + name, False,653 rev3 = mini_commit(in_tree, name, 'new_' + name, False,
1147 delta_against_basis=changed_in_tree)654 delta_against_basis=changed_in_tree)
@@ -1173,8 +680,6 @@
1173 tree2.add(['name'], [file_id])680 tree2.add(['name'], [file_id])
1174 rev2 = tree2.commit('')681 rev2 = tree2.commit('')
1175 tree1.merge_from_branch(tree2.branch)682 tree1.merge_from_branch(tree2.branch)
1176 if mini_commit is None:
1177 mini_commit = self.mini_commit
1178 rev3 = mini_commit(tree1, None, 'name', False)683 rev3 = mini_commit(tree1, None, 'name', False)
1179 tree3, = self._get_revtrees(tree1, [rev2])684 tree3, = self._get_revtrees(tree1, [rev2])
1180 # in rev2, name should be only changed in rev2685 # in rev2, name should be only changed in rev2
@@ -1187,12 +692,6 @@
1187 # merge a dir that changed preserves the last modified.692 # merge a dir that changed preserves the last modified.
1188 tree1 = self.make_branch_and_tree('t1')693 tree1 = self.make_branch_and_tree('t1')
1189 self.build_tree(['t1/dir/'])694 self.build_tree(['t1/dir/'])
1190 self._commit_sprout_rename_merge_converged(tree1, 'dir')
1191
1192 def test_last_modified_revision_after_converged_merge_dir_unchanged_ric(self):
1193 # merge a dir that changed preserves the last modified.
1194 tree1 = self.make_branch_and_tree('t1')
1195 self.build_tree(['t1/dir/'])
1196 self._commit_sprout_rename_merge_converged(tree1, 'dir',695 self._commit_sprout_rename_merge_converged(tree1, 'dir',
1197 mini_commit=self.mini_commit_record_iter_changes)696 mini_commit=self.mini_commit_record_iter_changes)
1198697
@@ -1200,12 +699,6 @@
1200 # merge a file that changed preserves the last modified.699 # merge a file that changed preserves the last modified.
1201 tree1 = self.make_branch_and_tree('t1')700 tree1 = self.make_branch_and_tree('t1')
1202 self.build_tree(['t1/file'])701 self.build_tree(['t1/file'])
1203 self._commit_sprout_rename_merge_converged(tree1, 'file')
1204
1205 def test_last_modified_revision_after_converged_merge_file_unchanged_ric(self):
1206 # merge a file that changed preserves the last modified.
1207 tree1 = self.make_branch_and_tree('t1')
1208 self.build_tree(['t1/file'])
1209 self._commit_sprout_rename_merge_converged(tree1, 'file',702 self._commit_sprout_rename_merge_converged(tree1, 'file',
1210 mini_commit=self.mini_commit_record_iter_changes)703 mini_commit=self.mini_commit_record_iter_changes)
1211704
@@ -1214,46 +707,24 @@
1214 self.requireFeature(features.SymlinkFeature)707 self.requireFeature(features.SymlinkFeature)
1215 tree1 = self.make_branch_and_tree('t1')708 tree1 = self.make_branch_and_tree('t1')
1216 os.symlink('target', 't1/link')709 os.symlink('target', 't1/link')
1217 self._commit_sprout_rename_merge_converged(tree1, 'link')
1218
1219 def test_last_modified_revision_after_converged_merge_link_unchanged_ric(self):
1220 # merge a link that changed preserves the last modified.
1221 self.requireFeature(features.SymlinkFeature)
1222 tree1 = self.make_branch_and_tree('t1')
1223 os.symlink('target', 't1/link')
1224 self._commit_sprout_rename_merge_converged(tree1, 'link',710 self._commit_sprout_rename_merge_converged(tree1, 'link',
1225 mini_commit=self.mini_commit_record_iter_changes)711 mini_commit=self.mini_commit_record_iter_changes)
1226712
1227 def test_last_modified_revision_after_merge_new_dir_unchanged(self):713 def test_last_modified_revision_after_merge_new_dir_unchanged(self):
1228 # merge a new dir does not change the last modified.714 # merge a new dir does not change the last modified.
1229 tree1 = self.make_branch_and_tree('t1')715 tree1 = self.make_branch_and_tree('t1')
1230 self._commit_sprout_make_merge(tree1, self.make_dir)
1231
1232 def test_last_modified_revision_after_merge_new_dir_unchanged_ric(self):
1233 # merge a new dir does not change the last modified.
1234 tree1 = self.make_branch_and_tree('t1')
1235 self._commit_sprout_make_merge(tree1, self.make_dir,716 self._commit_sprout_make_merge(tree1, self.make_dir,
1236 mini_commit=self.mini_commit_record_iter_changes)717 mini_commit=self.mini_commit_record_iter_changes)
1237718
1238 def test_last_modified_revision_after_merge_new_file_unchanged(self):719 def test_last_modified_revision_after_merge_new_file_unchanged(self):
1239 # merge a new file does not change the last modified.720 # merge a new file does not change the last modified.
1240 tree1 = self.make_branch_and_tree('t1')721 tree1 = self.make_branch_and_tree('t1')
1241 self._commit_sprout_make_merge(tree1, self.make_file)
1242
1243 def test_last_modified_revision_after_merge_new_file_unchanged_ric(self):
1244 # merge a new file does not change the last modified.
1245 tree1 = self.make_branch_and_tree('t1')
1246 self._commit_sprout_make_merge(tree1, self.make_file,722 self._commit_sprout_make_merge(tree1, self.make_file,
1247 mini_commit=self.mini_commit_record_iter_changes)723 mini_commit=self.mini_commit_record_iter_changes)
1248724
1249 def test_last_modified_revision_after_merge_new_link_unchanged(self):725 def test_last_modified_revision_after_merge_new_link_unchanged(self):
1250 # merge a new link does not change the last modified.726 # merge a new link does not change the last modified.
1251 tree1 = self.make_branch_and_tree('t1')727 tree1 = self.make_branch_and_tree('t1')
1252 self._commit_sprout_make_merge(tree1, self.make_link)
1253
1254 def test_last_modified_revision_after_merge_new_link_unchanged_ric(self):
1255 # merge a new link does not change the last modified.
1256 tree1 = self.make_branch_and_tree('t1')
1257 self._commit_sprout_make_merge(tree1, self.make_link,728 self._commit_sprout_make_merge(tree1, self.make_link,
1258 mini_commit=self.mini_commit_record_iter_changes)729 mini_commit=self.mini_commit_record_iter_changes)
1259730
@@ -1289,10 +760,6 @@
1289 expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)760 expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
1290761
1291 def test_last_modified_dir_file(self):762 def test_last_modified_dir_file(self):
1292 self._check_kind_change(self.make_dir, self.make_file,
1293 expect_fs_hash=True)
1294
1295 def test_last_modified_dir_file_ric(self):
1296 try:763 try:
1297 self._check_kind_change(self.make_dir, self.make_file,764 self._check_kind_change(self.make_dir, self.make_file,
1298 expect_fs_hash=True,765 expect_fs_hash=True,
@@ -1303,9 +770,6 @@
1303 "directory to file")770 "directory to file")
1304771
1305 def test_last_modified_dir_link(self):772 def test_last_modified_dir_link(self):
1306 self._check_kind_change(self.make_dir, self.make_link)
1307
1308 def test_last_modified_dir_link_ric(self):
1309 try:773 try:
1310 self._check_kind_change(self.make_dir, self.make_link,774 self._check_kind_change(self.make_dir, self.make_link,
1311 mini_commit=self.mini_commit_record_iter_changes)775 mini_commit=self.mini_commit_record_iter_changes)
@@ -1316,31 +780,18 @@
1316780
1317 def test_last_modified_link_file(self):781 def test_last_modified_link_file(self):
1318 self._check_kind_change(self.make_link, self.make_file,782 self._check_kind_change(self.make_link, self.make_file,
1319 expect_fs_hash=True)
1320
1321 def test_last_modified_link_file_ric(self):
1322 self._check_kind_change(self.make_link, self.make_file,
1323 expect_fs_hash=True,783 expect_fs_hash=True,
1324 mini_commit=self.mini_commit_record_iter_changes)784 mini_commit=self.mini_commit_record_iter_changes)
1325785
1326 def test_last_modified_link_dir(self):786 def test_last_modified_link_dir(self):
1327 self._check_kind_change(self.make_link, self.make_dir)
1328
1329 def test_last_modified_link_dir_ric(self):
1330 self._check_kind_change(self.make_link, self.make_dir,787 self._check_kind_change(self.make_link, self.make_dir,
1331 mini_commit=self.mini_commit_record_iter_changes)788 mini_commit=self.mini_commit_record_iter_changes)
1332789
1333 def test_last_modified_file_dir(self):790 def test_last_modified_file_dir(self):
1334 self._check_kind_change(self.make_file, self.make_dir)
1335
1336 def test_last_modified_file_dir_ric(self):
1337 self._check_kind_change(self.make_file, self.make_dir,791 self._check_kind_change(self.make_file, self.make_dir,
1338 mini_commit=self.mini_commit_record_iter_changes)792 mini_commit=self.mini_commit_record_iter_changes)
1339793
1340 def test_last_modified_file_link(self):794 def test_last_modified_file_link(self):
1341 self._check_kind_change(self.make_file, self.make_link)
1342
1343 def test_last_modified_file_link_ric(self):
1344 self._check_kind_change(self.make_file, self.make_link,795 self._check_kind_change(self.make_file, self.make_link,
1345 mini_commit=self.mini_commit_record_iter_changes)796 mini_commit=self.mini_commit_record_iter_changes)
1346797
1347798
=== modified file 'breezy/tests/per_workingtree/test_commit.py'
--- breezy/tests/per_workingtree/test_commit.py 2017-06-10 00:17:06 +0000
+++ breezy/tests/per_workingtree/test_commit.py 2017-06-20 22:39:47 +0000
@@ -420,6 +420,7 @@
420 if not tree.supports_tree_reference():420 if not tree.supports_tree_reference():
421 # inapplicable test.421 # inapplicable test.
422 return422 return
423 self.knownFailure('nested trees don\'t work well with iter_changes')
423 subtree = self.make_branch_and_tree('subtree')424 subtree = self.make_branch_and_tree('subtree')
424 tree.add(['subtree'])425 tree.add(['subtree'])
425 self.build_tree(['subtree/file'])426 self.build_tree(['subtree/file'])
426427
=== modified file 'breezy/tests/test_foreign.py'
--- breezy/tests/test_foreign.py 2017-06-10 16:40:42 +0000
+++ breezy/tests/test_foreign.py 2017-06-20 22:39:47 +0000
@@ -203,28 +203,24 @@
203 parent_revids = []203 parent_revids = []
204 else:204 else:
205 parent_revids = [parent_revid]205 parent_revids = [parent_revid]
206 builder = self.target.get_commit_builder(parent_revids, 206 builder = self.target.get_commit_builder(parent_revids,
207 self.target.get_config_stack(), rev.timestamp,207 self.target.get_config_stack(), rev.timestamp,
208 rev.timezone, rev.committer, rev.properties,208 rev.timezone, rev.committer, rev.properties,
209 new_revid)209 new_revid)
210 try:210 try:
211 parent_tree = self.target.repository.revision_tree(211 parent_tree = self.target.repository.revision_tree(
212 parent_revid)212 parent_revid)
213 for path, ie in tree.iter_entries_by_dir():213 iter_changes = tree.iter_changes(parent_tree)
214 new_ie = ie.copy()214 list(builder.record_iter_changes(
215 new_ie.revision = None215 tree, parent_revid, iter_changes))
216 builder.record_entry_contents(new_ie,
217 [parent_tree.root_inventory],
218 path, tree,
219 (ie.kind, ie.text_size, ie.executable, ie.text_sha1))
220 builder.finish_inventory()216 builder.finish_inventory()
221 except:217 except:
222 builder.abort()218 builder.abort()
223 raise219 raise
224 revidmap[revid] = builder.commit(rev.message)220 revidmap[revid] = builder.commit(rev.message)
225 self.target.set_last_revision_info(parent_revno+1, 221 self.target.set_last_revision_info(parent_revno+1,
226 revidmap[revid])222 revidmap[revid])
227 trace.mutter('lossily pushed revision %s -> %s', 223 trace.mutter('lossily pushed revision %s -> %s',
228 revid, revidmap[revid])224 revid, revidmap[revid])
229 finally:225 finally:
230 self.source.unlock()226 self.source.unlock()
231227
=== modified file 'breezy/tests/test_merge.py'
--- breezy/tests/test_merge.py 2017-06-10 16:40:42 +0000
+++ breezy/tests/test_merge.py 2017-06-20 22:39:47 +0000
@@ -226,6 +226,8 @@
226 tree_a.conflicts())226 tree_a.conflicts())
227227
228 def test_nested_merge(self):228 def test_nested_merge(self):
229 self.knownFailure(
230 'iter_changes doesn\'t work with changes in nested trees')
229 tree = self.make_branch_and_tree('tree',231 tree = self.make_branch_and_tree('tree',
230 format='development-subtree')232 format='development-subtree')
231 sub_tree = self.make_branch_and_tree('tree/sub-tree',233 sub_tree = self.make_branch_and_tree('tree/sub-tree',
232234
=== modified file 'doc/en/release-notes/brz-3.0.txt'
--- doc/en/release-notes/brz-3.0.txt 2017-06-19 14:35:58 +0000
+++ doc/en/release-notes/brz-3.0.txt 2017-06-20 22:39:47 +0000
@@ -121,6 +121,9 @@
121 * All previously deprecated functionality has been removed.121 * All previously deprecated functionality has been removed.
122 (Jelmer Vernooij)122 (Jelmer Vernooij)
123123
124 * ``CommitBuilder.record_entry_contents`` has been removed.
125 (Jelmer Vernooij, #731433, #604953)
126
124 * Renamed ``breezy.delta.report_delta`` parameter ``filter=`` to127 * Renamed ``breezy.delta.report_delta`` parameter ``filter=`` to
125 ``predicate=``. (Martin Packman)128 ``predicate=``. (Martin Packman)
126129

Subscribers

People subscribed via source and target branches