/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/commit.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2017-06-20 22:44:25 UTC
  • mfrom: (6700.1.7 iter-changes-tree-reference)
  • Revision ID: breezy.the.bot@gmail.com-20170620224425-woizq3viic2oyqu3
Stop calling CommitBuilder.record_entry_contents.


Merged from https://code.launchpad.net/~jelmer/brz/iter-changes-all-the-way/+merge/325965

Show diffs side-by-side

added added

removed removed

Lines of Context:
344
344
        self.work_tree.lock_write()
345
345
        operation.add_cleanup(self.work_tree.unlock)
346
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
347
        self.pb = ui.ui_factory.nested_progress_bar()
351
348
        operation.add_cleanup(self.pb.finished)
352
349
        self.basis_revid = self.work_tree.last_revision()
371
368
        if self.config_stack is None:
372
369
            self.config_stack = self.work_tree.get_config_stack()
373
370
 
374
 
        self._set_specific_file_ids()
375
 
 
376
371
        # Setup the progress bar. As the number of files that need to be
377
372
        # committed in unknown, progress is reported as stages.
378
373
        # We keep track of entries separately though and include that
390
385
        self.pb.show_count = True
391
386
        self.pb.show_bar = True
392
387
 
393
 
        self._gather_parents()
394
388
        # After a merge, a selected file commit is not supported.
395
389
        # See 'bzr help merge' for an explanation as to why.
396
390
        if len(self.parents) > 1 and self.specific_files is not None:
654
648
                     old_revno, old_revid, new_revno, self.rev_id,
655
649
                     tree_delta, future_tree)
656
650
 
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
651
    def _update_builder_with_changes(self):
674
652
        """Update the commit builder with the data about what has changed.
675
653
        """
676
 
        exclude = self.exclude
677
654
        specific_files = self.specific_files
678
655
        mutter("Selecting files for commit with filter %r", specific_files)
679
656
 
680
657
        self._check_strict()
681
 
        if self.use_record_iter_changes:
682
 
            iter_changes = self.work_tree.iter_changes(self.basis_tree,
683
 
                specific_files=specific_files)
684
 
            if self.exclude:
685
 
                iter_changes = filter_excluded(iter_changes, self.exclude)
686
 
            iter_changes = self._filter_iter_changes(iter_changes)
687
 
            for file_id, path, fs_hash in self.builder.record_iter_changes(
688
 
                self.work_tree, self.basis_revid, iter_changes):
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()
 
658
        iter_changes = self.work_tree.iter_changes(self.basis_tree,
 
659
            specific_files=specific_files)
 
660
        if self.exclude:
 
661
            iter_changes = filter_excluded(iter_changes, self.exclude)
 
662
        iter_changes = self._filter_iter_changes(iter_changes)
 
663
        for file_id, path, fs_hash in self.builder.record_iter_changes(
 
664
            self.work_tree, self.basis_revid, iter_changes):
 
665
            self.work_tree._observed_sha1(file_id, path, fs_hash)
695
666
 
696
667
    def _filter_iter_changes(self, iter_changes):
697
668
        """Process iter_changes.
745
716
        # Unversion IDs that were found to be deleted
746
717
        self.deleted_ids = deleted_ids
747
718
 
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
719
    def _check_strict(self):
798
720
        # XXX: when we use iter_changes this would likely be faster if
799
721
        # iter_changes would check for us (even in the presence of
803
725
            for unknown in self.work_tree.unknowns():
804
726
                raise StrictCommitFailed()
805
727
 
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
728
    def _commit_nested_tree(self, file_id, path):
908
729
        "Commit a nested tree."
909
730
        sub_tree = self.work_tree.get_nested_tree(file_id, path)
929
750
        except errors.PointlessCommit:
930
751
            return self.work_tree.get_reference_revision(file_id)
931
752
 
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
753
    def _set_progress_stage(self, name, counter=False):
976
754
        """Set the progress stage and emit an update to the progress bar."""
977
755
        self.pb_stage_name = name
994
772
        else:
995
773
            text = gettext("%s - Stage") % (self.pb_stage_name, )
996
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