~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Alexander Belchenko
  • Date: 2007-10-04 05:50:44 UTC
  • mfrom: (2881 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2884.
  • Revision ID: bialix@ukr.net-20071004055044-pb88kgkfayawro8n
merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
68
68
                           ConflictsInTree,
69
69
                           StrictCommitFailed
70
70
                           )
71
 
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any, 
 
71
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any,
72
72
                            is_inside_or_parent_of_any,
 
73
                            minimum_path_selection,
73
74
                            quotefn, sha_file, split_lines)
74
75
from bzrlib.testament import Testament
75
76
from bzrlib.trace import mutter, note, warning, is_quiet
241
242
                               " parameter is required for commit().")
242
243
 
243
244
        self.bound_branch = None
 
245
        self.any_entries_changed = False
 
246
        self.any_entries_deleted = False
244
247
        self.local = local
245
248
        self.master_branch = None
246
249
        self.master_locked = False
 
250
        self.recursive = recursive
247
251
        self.rev_id = None
248
 
        self.specific_files = specific_files
 
252
        if specific_files is not None:
 
253
            self.specific_files = sorted(
 
254
                minimum_path_selection(specific_files))
 
255
        else:
 
256
            self.specific_files = None
 
257
        self.specific_file_ids = None
249
258
        self.allow_pointless = allow_pointless
250
 
        self.recursive = recursive
251
259
        self.revprops = revprops
252
260
        self.message_callback = message_callback
253
261
        self.timestamp = timestamp
280
288
                self.config = self.branch.get_config()
281
289
 
282
290
            # If provided, ensure the specified files are versioned
283
 
            if specific_files is not None:
284
 
                # Note: We don't actually need the IDs here. This routine
285
 
                # is being called because it raises PathNotVerisonedError
286
 
                # as a side effect of finding the IDs.
 
291
            if self.specific_files is not None:
 
292
                # Note: This routine is being called because it raises
 
293
                # PathNotVersionedError as a side effect of finding the IDs. We
 
294
                # later use the ids we found as input to the working tree
 
295
                # inventory iterator, so we only consider those ids rather than
 
296
                # examining the whole tree again.
287
297
                # XXX: Dont we have filter_unversioned to do this more
288
298
                # cheaply?
289
 
                tree.find_ids_across_trees(specific_files,
290
 
                                           [self.basis_tree, self.work_tree])
 
299
                self.specific_file_ids = tree.find_ids_across_trees(
 
300
                    specific_files, [self.basis_tree, self.work_tree])
291
301
 
292
302
            # Setup the progress bar. As the number of files that need to be
293
303
            # committed in unknown, progress is reported as stages.
383
393
            return NullCommitReporter()
384
394
        return ReportCommitToLog()
385
395
 
386
 
    def _any_real_changes(self):
387
 
        """Are there real changes between new_inventory and basis?
388
 
 
389
 
        For trees without rich roots, inv.root.revision changes every commit.
390
 
        But if that is the only change, we want to treat it as though there
391
 
        are *no* changes.
392
 
        """
393
 
        new_entries = self.builder.new_inventory.iter_entries()
394
 
        basis_entries = self.basis_inv.iter_entries()
395
 
        new_path, new_root_ie = new_entries.next()
396
 
        basis_path, basis_root_ie = basis_entries.next()
397
 
 
398
 
        # This is a copy of InventoryEntry.__eq__ only leaving out .revision
399
 
        def ie_equal_no_revision(this, other):
400
 
            return ((this.file_id == other.file_id)
401
 
                    and (this.name == other.name)
402
 
                    and (this.symlink_target == other.symlink_target)
403
 
                    and (this.text_sha1 == other.text_sha1)
404
 
                    and (this.text_size == other.text_size)
405
 
                    and (this.text_id == other.text_id)
406
 
                    and (this.parent_id == other.parent_id)
407
 
                    and (this.kind == other.kind)
408
 
                    and (this.executable == other.executable)
409
 
                    and (this.reference_revision == other.reference_revision)
410
 
                    )
411
 
        if not ie_equal_no_revision(new_root_ie, basis_root_ie):
412
 
            return True
413
 
 
414
 
        for new_ie, basis_ie in zip(new_entries, basis_entries):
415
 
            if new_ie != basis_ie:
416
 
                return True
417
 
 
418
 
        # No actual changes present
419
 
        return False
420
 
 
421
396
    def _check_pointless(self):
422
397
        if self.allow_pointless:
423
398
            return
434
409
            return
435
410
        # If length == 1, then we only have the root entry. Which means
436
411
        # that there is no real difference (only the root could be different)
437
 
        if (len(self.builder.new_inventory) != 1 and self._any_real_changes()):
 
412
        if len(self.builder.new_inventory) != 1 and (self.any_entries_changed
 
413
            or self.any_entries_deleted):
438
414
            return
439
415
        raise PointlessCommit()
440
416
 
671
647
        # recorded in their previous state. For more details, see
672
648
        # https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
673
649
        if specific_files:
674
 
            for path, new_ie in self.basis_inv.iter_entries():
675
 
                if new_ie.file_id in self.builder.new_inventory:
 
650
            for path, old_ie in self.basis_inv.iter_entries():
 
651
                if old_ie.file_id in self.builder.new_inventory:
 
652
                    # already added - skip.
676
653
                    continue
677
654
                if is_inside_any(specific_files, path):
 
655
                    # was inside the selected path, if not present it has been
 
656
                    # deleted so skip.
678
657
                    continue
679
 
                ie = new_ie.copy()
680
 
                ie.revision = None
681
 
                self.builder.record_entry_contents(ie, self.parent_invs, path,
682
 
                                                   self.basis_tree)
 
658
                if old_ie.kind == 'directory':
 
659
                    self._next_progress_entry()
 
660
                # not in final inv yet, was not in the selected files, so is an
 
661
                # entry to be preserved unaltered.
 
662
                ie = old_ie.copy()
 
663
                # Note: specific file commits after a merge are currently
 
664
                # prohibited. This test is for sanity/safety in case it's
 
665
                # required after that changes.
 
666
                if len(self.parents) > 1:
 
667
                    ie.revision = None
 
668
                delta, version_recorded = self.builder.record_entry_contents(
 
669
                    ie, self.parent_invs, path, self.basis_tree, None)
 
670
                if version_recorded:
 
671
                    self.any_entries_changed = True
683
672
 
 
673
        # note that deletes have occurred
 
674
        if set(self.basis_inv._byid.keys()) - set(self.builder.new_inventory._byid.keys()):
 
675
            self.any_entries_deleted = True
684
676
        # Report what was deleted.
685
 
        if self.reporter.is_verbose():
 
677
        if self.any_entries_deleted and self.reporter.is_verbose():
686
678
            for path, ie in self.basis_inv.iter_entries():
687
679
                if ie.file_id not in self.builder.new_inventory:
688
680
                    self.reporter.deleted(path)
699
691
        deleted_paths = set()
700
692
        work_inv = self.work_tree.inventory
701
693
        assert work_inv.root is not None
702
 
        entries = work_inv.iter_entries_by_dir()
 
694
        # XXX: Note that entries may have the wrong kind.
 
695
        entries = work_inv.iter_entries_by_dir(
 
696
            specific_file_ids=self.specific_file_ids, yield_parents=True)
703
697
        if not self.builder.record_root_entry:
704
698
            entries.next()
705
699
        for path, existing_ie in entries:
709
703
            kind = existing_ie.kind
710
704
            if kind == 'directory':
711
705
                self._next_progress_entry()
712
 
 
713
706
            # Skip files that have been deleted from the working tree.
714
707
            # The deleted files/directories are also recorded so they
715
708
            # can be explicitly unversioned later. Note that when a
717
710
            # deleted files matching that filter.
718
711
            if is_inside_any(deleted_paths, path):
719
712
                continue
 
713
            content_summary = self.work_tree.path_content_summary(path)
720
714
            if not specific_files or is_inside_any(specific_files, path):
721
 
                if not self.work_tree.has_filename(path):
 
715
                if content_summary[0] == 'missing':
722
716
                    deleted_paths.add(path)
723
717
                    self.reporter.missing(path)
724
718
                    deleted_ids.append(file_id)
725
719
                    continue
726
 
            try:
727
 
                kind = self.work_tree.kind(file_id)
728
 
                # TODO: specific_files filtering before nested tree processing
729
 
                if kind == 'tree-reference' and self.recursive == 'down':
730
 
                    self._commit_nested_tree(file_id, path)
731
 
            except errors.NoSuchFile:
732
 
                pass
 
720
            # TODO: have the builder do the nested commit just-in-time IF and
 
721
            # only if needed.
 
722
            if content_summary[0] == 'tree-reference':
 
723
                # enforce repository nested tree policy.
 
724
                if (not self.work_tree.supports_tree_reference() or
 
725
                    # repository does not support it either.
 
726
                    not self.branch.repository._format.supports_tree_reference):
 
727
                    content_summary = ('directory',) + content_summary[1:]
 
728
            kind = content_summary[0]
 
729
            # TODO: specific_files filtering before nested tree processing
 
730
            if kind == 'tree-reference':
 
731
                if self.recursive == 'down':
 
732
                    nested_revision_id = self._commit_nested_tree(
 
733
                        file_id, path)
 
734
                    content_summary = content_summary[:3] + (
 
735
                        nested_revision_id,)
 
736
                else:
 
737
                    content_summary = content_summary[:3] + (
 
738
                        self.work_tree.get_reference_revision(file_id),)
733
739
 
734
740
            # Record an entry for this item
735
741
            # Note: I don't particularly want to have the existing_ie
736
742
            # parameter but the test suite currently (28-Jun-07) breaks
737
743
            # without it thanks to a unicode normalisation issue. :-(
738
 
            definitely_changed = kind != existing_ie.kind 
 
744
            definitely_changed = kind != existing_ie.kind
739
745
            self._record_entry(path, file_id, specific_files, kind, name,
740
 
                parent_id, definitely_changed, existing_ie, report_changes)
 
746
                parent_id, definitely_changed, existing_ie, report_changes,
 
747
                content_summary)
741
748
 
742
749
        # Unversion IDs that were found to be deleted
743
750
        self.work_tree.unversion(deleted_ids)
756
763
            sub_tree.branch.repository = \
757
764
                self.work_tree.branch.repository
758
765
        try:
759
 
            sub_tree.commit(message=None, revprops=self.revprops,
 
766
            return sub_tree.commit(message=None, revprops=self.revprops,
760
767
                recursive=self.recursive,
761
768
                message_callback=self.message_callback,
762
769
                timestamp=self.timestamp, timezone=self.timezone,
765
772
                strict=self.strict, verbose=self.verbose,
766
773
                local=self.local, reporter=self.reporter)
767
774
        except errors.PointlessCommit:
768
 
            pass
 
775
            return self.work_tree.get_reference_revision(file_id)
769
776
 
770
777
    def _record_entry(self, path, file_id, specific_files, kind, name,
771
 
            parent_id, definitely_changed, existing_ie=None,
772
 
            report_changes=True):
 
778
        parent_id, definitely_changed, existing_ie, report_changes,
 
779
        content_summary):
773
780
        "Record the new inventory entry for a path if any."
774
781
        # mutter('check %s {%s}', path, file_id)
775
 
        if (not specific_files or 
776
 
            is_inside_or_parent_of_any(specific_files, path)):
777
 
                # mutter('%s selected for commit', path)
778
 
                if definitely_changed or existing_ie is None:
779
 
                    ie = inventory.make_entry(kind, name, parent_id, file_id)
780
 
                else:
781
 
                    ie = existing_ie.copy()
782
 
                    ie.revision = None
 
782
        # mutter('%s selected for commit', path)
 
783
        if definitely_changed or existing_ie is None:
 
784
            ie = inventory.make_entry(kind, name, parent_id, file_id)
783
785
        else:
784
 
            # mutter('%s not selected for commit', path)
785
 
            if self.basis_inv.has_id(file_id):
786
 
                ie = self.basis_inv[file_id].copy()
787
 
            else:
788
 
                # this entry is new and not being committed
789
 
                ie = None
790
 
        if ie is not None:
791
 
            self.builder.record_entry_contents(ie, self.parent_invs, 
792
 
                path, self.work_tree)
793
 
            if report_changes:
794
 
                self._report_change(ie, path)
 
786
            ie = existing_ie.copy()
 
787
            ie.revision = None
 
788
        delta, version_recorded = self.builder.record_entry_contents(ie,
 
789
            self.parent_invs, path, self.work_tree, content_summary)
 
790
        if version_recorded:
 
791
            self.any_entries_changed = True
 
792
        if report_changes:
 
793
            self._report_change(ie, path)
795
794
        return ie
796
795
 
797
796
    def _report_change(self, ie, path):