/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: Jelmer Vernooij
  • Date: 2017-06-20 00:26:58 UTC
  • mto: This revision was merged to the branch mainline in revision 6713.
  • Revision ID: jelmer@jelmer.uk-20170620002658-44leut8v74pcduez
Drop support for committing using record_entr_contents.

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:
655
649
                     old_revno, old_revid, new_revno, self.rev_id,
656
650
                     tree_delta, future_tree)
657
651
 
658
 
    def _gather_parents(self):
659
 
        """Record the parents of a merge for merge detection."""
660
 
        # TODO: Make sure that this list doesn't contain duplicate
661
 
        # entries and the order is preserved when doing this.
662
 
        if self.use_record_iter_changes:
663
 
            return
664
 
        self.basis_inv = self.basis_tree.root_inventory
665
 
        self.parent_invs = [self.basis_inv]
666
 
        for revision in self.parents[1:]:
667
 
            if self.branch.repository.has_revision(revision):
668
 
                mutter('commit parent revision {%s}', revision)
669
 
                inventory = self.branch.repository.get_inventory(revision)
670
 
                self.parent_invs.append(inventory)
671
 
            else:
672
 
                mutter('commit parent ghost revision {%s}', revision)
673
 
 
674
652
    def _update_builder_with_changes(self):
675
653
        """Update the commit builder with the data about what has changed.
676
654
        """
677
 
        exclude = self.exclude
678
655
        specific_files = self.specific_files
679
656
        mutter("Selecting files for commit with filter %r", specific_files)
680
657
 
681
658
        self._check_strict()
682
 
        if self.use_record_iter_changes:
683
 
            iter_changes = self.work_tree.iter_changes(self.basis_tree,
684
 
                specific_files=specific_files)
685
 
            if self.exclude:
686
 
                iter_changes = filter_excluded(iter_changes, self.exclude)
687
 
            iter_changes = self._filter_iter_changes(iter_changes)
688
 
            for file_id, path, fs_hash in self.builder.record_iter_changes(
689
 
                self.work_tree, self.basis_revid, iter_changes):
690
 
                self.work_tree._observed_sha1(file_id, path, fs_hash)
691
 
        else:
692
 
            # Build the new inventory
693
 
            self._populate_from_inventory()
694
 
            self._record_unselected()
695
 
            self._report_and_accumulate_deletes()
 
659
        iter_changes = self.work_tree.iter_changes(self.basis_tree,
 
660
            specific_files=specific_files)
 
661
        if self.exclude:
 
662
            iter_changes = filter_excluded(iter_changes, self.exclude)
 
663
        iter_changes = self._filter_iter_changes(iter_changes)
 
664
        for file_id, path, fs_hash in self.builder.record_iter_changes(
 
665
            self.work_tree, self.basis_revid, iter_changes):
 
666
            self.work_tree._observed_sha1(file_id, path, fs_hash)
696
667
 
697
668
    def _filter_iter_changes(self, iter_changes):
698
669
        """Process iter_changes.
746
717
        # Unversion IDs that were found to be deleted
747
718
        self.deleted_ids = deleted_ids
748
719
 
749
 
    def _record_unselected(self):
750
 
        # If specific files are selected, then all un-selected files must be
751
 
        # recorded in their previous state. For more details, see
752
 
        # https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
753
 
        if self.specific_files or self.exclude:
754
 
            specific_files = self.specific_files or []
755
 
            for path, old_ie in self.basis_inv.iter_entries():
756
 
                if self.builder.new_inventory.has_id(old_ie.file_id):
757
 
                    # already added - skip.
758
 
                    continue
759
 
                if (is_inside_any(specific_files, path)
760
 
                    and not is_inside_any(self.exclude, path)):
761
 
                    # was inside the selected path, and not excluded - if not
762
 
                    # present it has been deleted so skip.
763
 
                    continue
764
 
                # From here down it was either not selected, or was excluded:
765
 
                # We preserve the entry unaltered.
766
 
                ie = old_ie.copy()
767
 
                # Note: specific file commits after a merge are currently
768
 
                # prohibited. This test is for sanity/safety in case it's
769
 
                # required after that changes.
770
 
                if len(self.parents) > 1:
771
 
                    ie.revision = None
772
 
                self.builder.record_entry_contents(ie, self.parent_invs, path,
773
 
                    self.basis_tree, None)
774
 
 
775
 
    def _report_and_accumulate_deletes(self):
776
 
        if (isinstance(self.basis_inv, Inventory)
777
 
            and isinstance(self.builder.new_inventory, Inventory)):
778
 
            # the older Inventory classes provide a _byid dict, and building a
779
 
            # set from the keys of this dict is substantially faster than even
780
 
            # getting a set of ids from the inventory
781
 
            #
782
 
            # <lifeless> set(dict) is roughly the same speed as
783
 
            # set(iter(dict)) and both are significantly slower than
784
 
            # set(dict.keys())
785
 
            deleted_ids = set(self.basis_inv._byid.keys()) - \
786
 
               set(self.builder.new_inventory._byid.keys())
787
 
        else:
788
 
            deleted_ids = set(self.basis_inv) - set(self.builder.new_inventory)
789
 
        if deleted_ids:
790
 
            self.any_entries_deleted = True
791
 
            deleted = sorted([(self.basis_tree.id2path(file_id), file_id)
792
 
                for file_id in deleted_ids])
793
 
            # XXX: this is not quite directory-order sorting
794
 
            for path, file_id in deleted:
795
 
                self.builder.record_delete(path, file_id)
796
 
                self.reporter.deleted(path)
797
 
 
798
720
    def _check_strict(self):
799
721
        # XXX: when we use iter_changes this would likely be faster if
800
722
        # iter_changes would check for us (even in the presence of
804
726
            for unknown in self.work_tree.unknowns():
805
727
                raise StrictCommitFailed()
806
728
 
807
 
    def _populate_from_inventory(self):
808
 
        """Populate the CommitBuilder by walking the working tree inventory."""
809
 
        # Build the revision inventory.
810
 
        #
811
 
        # This starts by creating a new empty inventory. Depending on
812
 
        # which files are selected for commit, and what is present in the
813
 
        # current tree, the new inventory is populated. inventory entries
814
 
        # which are candidates for modification have their revision set to
815
 
        # None; inventory entries that are carried over untouched have their
816
 
        # revision set to their prior value.
817
 
        #
818
 
        # ESEPARATIONOFCONCERNS: this function is diffing and using the diff
819
 
        # results to create a new inventory at the same time, which results
820
 
        # in bugs like #46635.  Any reason not to use/enhance Tree.changes_from?
821
 
        # ADHB 11-07-2006
822
 
 
823
 
        specific_files = self.specific_files
824
 
        exclude = self.exclude
825
 
        report_changes = self.reporter.is_verbose()
826
 
        deleted_ids = []
827
 
        # A tree of paths that have been deleted. E.g. if foo/bar has been
828
 
        # deleted, then we have {'foo':{'bar':{}}}
829
 
        deleted_paths = {}
830
 
        # XXX: Note that entries may have the wrong kind because the entry does
831
 
        # not reflect the status on disk.
832
 
        # NB: entries will include entries within the excluded ids/paths
833
 
        # because iter_entries_by_dir has no 'exclude' facility today.
834
 
        entries = self.work_tree.iter_entries_by_dir(
835
 
            specific_file_ids=self.specific_file_ids, yield_parents=True)
836
 
        for path, existing_ie in entries:
837
 
            file_id = existing_ie.file_id
838
 
            name = existing_ie.name
839
 
            parent_id = existing_ie.parent_id
840
 
            kind = existing_ie.kind
841
 
            # Skip files that have been deleted from the working tree.
842
 
            # The deleted path ids are also recorded so they can be explicitly
843
 
            # unversioned later.
844
 
            if deleted_paths:
845
 
                path_segments = splitpath(path)
846
 
                deleted_dict = deleted_paths
847
 
                for segment in path_segments:
848
 
                    deleted_dict = deleted_dict.get(segment, None)
849
 
                    if not deleted_dict:
850
 
                        # We either took a path not present in the dict
851
 
                        # (deleted_dict was None), or we've reached an empty
852
 
                        # child dir in the dict, so are now a sub-path.
853
 
                        break
854
 
                else:
855
 
                    deleted_dict = None
856
 
                if deleted_dict is not None:
857
 
                    # the path has a deleted parent, do not add it.
858
 
                    continue
859
 
            if exclude and is_inside_any(exclude, path):
860
 
                # Skip excluded paths. Excluded paths are processed by
861
 
                # _update_builder_with_changes.
862
 
                continue
863
 
            content_summary = self.work_tree.path_content_summary(path)
864
 
            kind = content_summary[0]
865
 
            # Note that when a filter of specific files is given, we must only
866
 
            # skip/record deleted files matching that filter.
867
 
            if not specific_files or is_inside_any(specific_files, path):
868
 
                if kind == 'missing':
869
 
                    if not deleted_paths:
870
 
                        # path won't have been split yet.
871
 
                        path_segments = splitpath(path)
872
 
                    deleted_dict = deleted_paths
873
 
                    for segment in path_segments:
874
 
                        deleted_dict = deleted_dict.setdefault(segment, {})
875
 
                    self.reporter.missing(path)
876
 
                    self._next_progress_entry()
877
 
                    deleted_ids.append(file_id)
878
 
                    continue
879
 
            # TODO: have the builder do the nested commit just-in-time IF and
880
 
            # only if needed.
881
 
            if kind == 'tree-reference':
882
 
                # enforce repository nested tree policy.
883
 
                if (not self.work_tree.supports_tree_reference() or
884
 
                    # repository does not support it either.
885
 
                    not self.branch.repository._format.supports_tree_reference):
886
 
                    kind = 'directory'
887
 
                    content_summary = (kind, None, None, None)
888
 
                elif self.recursive == 'down':
889
 
                    nested_revision_id = self._commit_nested_tree(
890
 
                        file_id, path)
891
 
                    content_summary = (kind, None, None, nested_revision_id)
892
 
                else:
893
 
                    nested_revision_id = self.work_tree.get_reference_revision(file_id)
894
 
                    content_summary = (kind, None, None, nested_revision_id)
895
 
 
896
 
            # Record an entry for this item
897
 
            # Note: I don't particularly want to have the existing_ie
898
 
            # parameter but the test suite currently (28-Jun-07) breaks
899
 
            # without it thanks to a unicode normalisation issue. :-(
900
 
            definitely_changed = kind != existing_ie.kind
901
 
            self._record_entry(path, file_id, specific_files, kind, name,
902
 
                parent_id, definitely_changed, existing_ie, report_changes,
903
 
                content_summary)
904
 
 
905
 
        # Unversion IDs that were found to be deleted
906
 
        self.deleted_ids = deleted_ids
907
 
 
908
729
    def _commit_nested_tree(self, file_id, path):
909
730
        "Commit a nested tree."
910
731
        sub_tree = self.work_tree.get_nested_tree(file_id, path)
930
751
        except errors.PointlessCommit:
931
752
            return self.work_tree.get_reference_revision(file_id)
932
753
 
933
 
    def _record_entry(self, path, file_id, specific_files, kind, name,
934
 
        parent_id, definitely_changed, existing_ie, report_changes,
935
 
        content_summary):
936
 
        "Record the new inventory entry for a path if any."
937
 
        # mutter('check %s {%s}', path, file_id)
938
 
        # mutter('%s selected for commit', path)
939
 
        if definitely_changed or existing_ie is None:
940
 
            ie = make_entry(kind, name, parent_id, file_id)
941
 
        else:
942
 
            ie = existing_ie.copy()
943
 
            ie.revision = None
944
 
        # For carried over entries we don't care about the fs hash - the repo
945
 
        # isn't generating a sha, so we're not saving computation time.
946
 
        _, _, fs_hash = self.builder.record_entry_contents(
947
 
            ie, self.parent_invs, path, self.work_tree, content_summary)
948
 
        if report_changes:
949
 
            self._report_change(ie, path)
950
 
        if fs_hash:
951
 
            self.work_tree._observed_sha1(ie.file_id, path, fs_hash)
952
 
        return ie
953
 
 
954
 
    def _report_change(self, ie, path):
955
 
        """Report a change to the user.
956
 
 
957
 
        The change that has occurred is described relative to the basis
958
 
        inventory.
959
 
        """
960
 
        if (self.basis_inv.has_id(ie.file_id)):
961
 
            basis_ie = self.basis_inv[ie.file_id]
962
 
        else:
963
 
            basis_ie = None
964
 
        change = ie.describe_change(basis_ie, ie)
965
 
        if change in (InventoryEntry.RENAMED,
966
 
            InventoryEntry.MODIFIED_AND_RENAMED):
967
 
            old_path = self.basis_inv.id2path(ie.file_id)
968
 
            self.reporter.renamed(change, old_path, path)
969
 
            self._next_progress_entry()
970
 
        else:
971
 
            if change == gettext('unchanged'):
972
 
                return
973
 
            self.reporter.snapshot_change(change, path)
974
 
            self._next_progress_entry()
975
 
 
976
754
    def _set_progress_stage(self, name, counter=False):
977
755
        """Set the progress stage and emit an update to the progress bar."""
978
756
        self.pb_stage_name = name
995
773
        else:
996
774
            text = gettext("%s - Stage") % (self.pb_stage_name, )
997
775
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
998
 
 
999
 
    def _set_specific_file_ids(self):
1000
 
        """populate self.specific_file_ids if we will use it."""
1001
 
        if not self.use_record_iter_changes:
1002
 
            # If provided, ensure the specified files are versioned
1003
 
            if self.specific_files is not None:
1004
 
                # Note: This routine is being called because it raises
1005
 
                # PathNotVersionedError as a side effect of finding the IDs. We
1006
 
                # later use the ids we found as input to the working tree
1007
 
                # inventory iterator, so we only consider those ids rather than
1008
 
                # examining the whole tree again.
1009
 
                # XXX: Dont we have filter_unversioned to do this more
1010
 
                # cheaply?
1011
 
                self.specific_file_ids = tree.find_ids_across_trees(
1012
 
                    self.specific_files, [self.basis_tree, self.work_tree])
1013
 
            else:
1014
 
                self.specific_file_ids = None