~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Marius Kruger
  • Date: 2007-06-27 18:48:10 UTC
  • mfrom: (2557 +trunk)
  • mto: (2605.1.1 rm-renamed)
  • mto: This revision was merged to the branch mainline in revision 2609.
  • Revision ID: marius.kruger@enerweb.co.za-20070627184810-4jq1y5f20xafow9w
Merge with bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
263
263
            if self.config is None:
264
264
                self.config = self.branch.get_config()
265
265
 
266
 
            self.work_inv = self.work_tree.inventory
267
 
            self.basis_inv = self.basis_tree.inventory
 
266
            # If provided, ensure the specified files are versioned
268
267
            if specific_files is not None:
269
 
                # Ensure specified files are versioned
270
 
                # (We don't actually need the ids here)
 
268
                # Note: We don't actually need the IDs here. This routine
 
269
                # is being called because it raises PathNotVerisonedError
 
270
                # as a side effect of finding the IDs.
271
271
                # XXX: Dont we have filter_unversioned to do this more
272
272
                # cheaply?
273
273
                tree.find_ids_across_trees(specific_files,
288
288
            self.pb.show_count = True
289
289
            self.pb.show_bar = False
290
290
 
 
291
            # After a merge, a selected file commit is not supported.
 
292
            # See 'bzr help merge' for an explanation as to why.
 
293
            self.basis_inv = self.basis_tree.inventory
291
294
            self._gather_parents()
292
295
            if len(self.parents) > 1 and self.specific_files:
293
296
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
294
297
            
295
 
            # Build the new inventory
 
298
            # Collect the changes
296
299
            self._emit_progress_set_stage("Collecting changes", show_entries=True)
297
300
            self.builder = self.branch.get_commit_builder(self.parents,
298
301
                self.config, timestamp, timezone, committer, revprops, rev_id)
299
 
            self._remove_deleted()
300
 
            self._populate_new_inv()
301
 
            self._report_deletes()
 
302
            self._update_builder_with_changes()
302
303
            self._check_pointless()
303
304
 
304
 
            # TODO: Now the new inventory is known, check for conflicts and
305
 
            # prompt the user for a commit message.
 
305
            # TODO: Now the new inventory is known, check for conflicts.
306
306
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
307
307
            # weave lines, because nothing should be recorded until it is known
308
308
            # that commit will succeed.
309
309
            self._emit_progress_set_stage("Saving data locally")
310
310
            self.builder.finish_inventory()
 
311
 
 
312
            # Prompt the user for a commit message if none provided
311
313
            message = message_callback(self)
312
314
            assert isinstance(message, unicode), type(message)
313
315
            self.message = message
563
565
            else:
564
566
                mutter('commit parent ghost revision {%s}', revision)
565
567
 
566
 
    def _remove_deleted(self):
567
 
        """Remove deleted files from the working inventories.
568
 
 
569
 
        This is done prior to taking the working inventory as the
570
 
        basis for the new committed inventory.
571
 
 
572
 
        This returns true if any files
573
 
        *that existed in the basis inventory* were deleted.
574
 
        Files that were added and deleted
575
 
        in the working copy don't matter.
576
 
        """
577
 
        specific = self.specific_files
578
 
        deleted_ids = []
579
 
        deleted_paths = set()
580
 
        for path, ie in self.work_inv.iter_entries():
581
 
            if is_inside_any(deleted_paths, path):
582
 
                # The tree will delete the required ids recursively.
583
 
                continue
584
 
            if specific and not is_inside_any(specific, path):
585
 
                continue
586
 
            if not self.work_tree.has_filename(path):
587
 
                deleted_paths.add(path)
588
 
                self.reporter.missing(path)
589
 
                deleted_ids.append(ie.file_id)
590
 
        self.work_tree.unversion(deleted_ids)
591
 
 
592
 
    def _populate_new_inv(self):
593
 
        """Build revision inventory.
594
 
 
595
 
        This creates a new empty inventory. Depending on
596
 
        which files are selected for commit, and what is present in the
597
 
        current tree, the new inventory is populated. inventory entries 
598
 
        which are candidates for modification have their revision set to
599
 
        None; inventory entries that are carried over untouched have their
600
 
        revision set to their prior value.
601
 
        """
 
568
    def _update_builder_with_changes(self):
 
569
        """Update the commit builder with the data about what has changed.
 
570
        """
 
571
        # Build the revision inventory.
 
572
        #
 
573
        # This starts by creating a new empty inventory. Depending on
 
574
        # which files are selected for commit, and what is present in the
 
575
        # current tree, the new inventory is populated. inventory entries 
 
576
        # which are candidates for modification have their revision set to
 
577
        # None; inventory entries that are carried over untouched have their
 
578
        # revision set to their prior value.
 
579
        #
602
580
        # ESEPARATIONOFCONCERNS: this function is diffing and using the diff
603
581
        # results to create a new inventory at the same time, which results
604
582
        # in bugs like #46635.  Any reason not to use/enhance Tree.changes_from?
605
583
        # ADHB 11-07-2006
606
 
        mutter("Selecting files for commit with filter %s", self.specific_files)
607
 
        assert self.work_inv.root is not None
608
 
        entries = self.work_inv.iter_entries()
 
584
 
 
585
        specific_files = self.specific_files
 
586
        mutter("Selecting files for commit with filter %s", specific_files)
 
587
        work_inv = self.work_tree.inventory
 
588
        assert work_inv.root is not None
 
589
        self.pb_entries_total = len(work_inv)
 
590
 
 
591
        # Check and warn about old CommitBuilders
 
592
        entries = work_inv.iter_entries()
609
593
        if not self.builder.record_root_entry:
610
594
            symbol_versioning.warn('CommitBuilders should support recording'
611
595
                ' the root entry as of bzr 0.10.', DeprecationWarning, 
612
596
                stacklevel=1)
613
597
            self.builder.new_inventory.add(self.basis_inv.root.copy())
614
598
            entries.next()
615
 
        self.pb_entries_total = len(self.work_inv)
 
599
 
 
600
        deleted_ids = []
 
601
        deleted_paths = set()
616
602
        for path, new_ie in entries:
617
603
            self._emit_progress_next_entry()
618
604
            file_id = new_ie.file_id
 
605
 
 
606
            # Skip files that have been deleted from the working tree.
 
607
            # The deleted files/directories are also recorded so they
 
608
            # can be explicitly unversioned later. Note that when a
 
609
            # filter of specific files is given, we must only skip/record
 
610
            # deleted files matching that filter.
 
611
            if is_inside_any(deleted_paths, path):
 
612
                continue
 
613
            if not specific_files or is_inside_any(specific_files, path):
 
614
                if not self.work_tree.has_filename(path):
 
615
                    deleted_paths.add(path)
 
616
                    self.reporter.missing(path)
 
617
                    deleted_ids.append(file_id)
 
618
                    continue
619
619
            try:
620
620
                kind = self.work_tree.kind(file_id)
621
621
                if kind == 'tree-reference' and self.recursive == 'down':
649
649
            except errors.NoSuchFile:
650
650
                pass
651
651
            # mutter('check %s {%s}', path, file_id)
652
 
            if (not self.specific_files or 
653
 
                is_inside_or_parent_of_any(self.specific_files, path)):
 
652
            if (not specific_files or 
 
653
                is_inside_or_parent_of_any(specific_files, path)):
654
654
                    # mutter('%s selected for commit', path)
655
655
                    ie = new_ie.copy()
656
656
                    ie.revision = None
677
677
            else:
678
678
                self.reporter.snapshot_change(change, path)
679
679
 
680
 
        if not self.specific_files:
681
 
            return
682
 
 
683
 
        # ignore removals that don't match filespec
684
 
        for path, new_ie in self.basis_inv.iter_entries():
685
 
            if new_ie.file_id in self.work_inv:
686
 
                continue
687
 
            if is_inside_any(self.specific_files, path):
688
 
                continue
689
 
            ie = new_ie.copy()
690
 
            ie.revision = None
691
 
            self.builder.record_entry_contents(ie, self.parent_invs, path,
692
 
                                               self.basis_tree)
 
680
        # Unversion IDs that were found to be deleted
 
681
        self.work_tree.unversion(deleted_ids)
 
682
 
 
683
        # If specific files/directories were nominated, it is possible
 
684
        # that some data from outside those needs to be preserved from
 
685
        # the basis tree. For example, if a file x is moved from out of
 
686
        # directory foo into directory bar and the user requests
 
687
        # ``commit foo``, then information about bar/x must also be
 
688
        # recorded.
 
689
        if specific_files:
 
690
            for path, new_ie in self.basis_inv.iter_entries():
 
691
                if new_ie.file_id in work_inv:
 
692
                    continue
 
693
                if is_inside_any(specific_files, path):
 
694
                    continue
 
695
                ie = new_ie.copy()
 
696
                ie.revision = None
 
697
                self.builder.record_entry_contents(ie, self.parent_invs, path,
 
698
                                                   self.basis_tree)
 
699
 
 
700
        # Report what was deleted. We could skip this when no deletes are
 
701
        # detected to gain a performance win, but it arguably serves as a
 
702
        # 'safety check' by informing the user whenever anything disappears.
 
703
        for path, ie in self.basis_inv.iter_entries():
 
704
            if ie.file_id not in self.builder.new_inventory:
 
705
                self.reporter.deleted(path)
693
706
 
694
707
    def _emit_progress_set_stage(self, name, show_entries=False):
695
708
        """Set the progress stage and emit an update to the progress bar."""
714
727
            text = "%s - Stage" % (self.pb_stage_name)
715
728
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
716
729
 
717
 
    def _report_deletes(self):
718
 
        for path, ie in self.basis_inv.iter_entries():
719
 
            if ie.file_id not in self.builder.new_inventory:
720
 
                self.reporter.deleted(path)
721