/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 bzrlib/commit.py

  • Committer: Aaron Bentley
  • Date: 2007-07-03 18:00:34 UTC
  • mfrom: (2577 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2578.
  • Revision ID: abentley@panoramicfeedback.com-20070703180034-zuiuqbo5mx0tsa1n
Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
from cStringIO import StringIO
58
58
 
59
59
from bzrlib import (
 
60
    debug,
60
61
    errors,
61
62
    inventory,
62
63
    tree,
263
264
            if self.config is None:
264
265
                self.config = self.branch.get_config()
265
266
 
266
 
            self.work_inv = self.work_tree.inventory
267
 
            self.basis_inv = self.basis_tree.inventory
 
267
            # If provided, ensure the specified files are versioned
268
268
            if specific_files is not None:
269
 
                # Ensure specified files are versioned
270
 
                # (We don't actually need the ids here)
 
269
                # Note: We don't actually need the IDs here. This routine
 
270
                # is being called because it raises PathNotVerisonedError
 
271
                # as a side effect of finding the IDs.
271
272
                # XXX: Dont we have filter_unversioned to do this more
272
273
                # cheaply?
273
274
                tree.find_ids_across_trees(specific_files,
288
289
            self.pb.show_count = True
289
290
            self.pb.show_bar = False
290
291
 
 
292
            # After a merge, a selected file commit is not supported.
 
293
            # See 'bzr help merge' for an explanation as to why.
 
294
            self.basis_inv = self.basis_tree.inventory
291
295
            self._gather_parents()
292
296
            if len(self.parents) > 1 and self.specific_files:
293
297
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
294
298
            
295
 
            # Build the new inventory
 
299
            # Collect the changes
296
300
            self._emit_progress_set_stage("Collecting changes", show_entries=True)
297
301
            self.builder = self.branch.get_commit_builder(self.parents,
298
302
                self.config, timestamp, timezone, committer, revprops, rev_id)
299
 
            self._remove_deleted()
300
 
            self._populate_new_inv()
301
 
            self._report_deletes()
 
303
            self._update_builder_with_changes()
302
304
            self._check_pointless()
303
305
 
304
 
            # TODO: Now the new inventory is known, check for conflicts and
305
 
            # prompt the user for a commit message.
 
306
            # TODO: Now the new inventory is known, check for conflicts.
306
307
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
307
308
            # weave lines, because nothing should be recorded until it is known
308
309
            # that commit will succeed.
309
310
            self._emit_progress_set_stage("Saving data locally")
310
311
            self.builder.finish_inventory()
 
312
 
 
313
            # Prompt the user for a commit message if none provided
311
314
            message = message_callback(self)
312
315
            assert isinstance(message, unicode), type(message)
313
316
            self.message = message
336
339
            rev_tree = self.builder.revision_tree()
337
340
            self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
338
341
            self.reporter.completed(new_revno, self.rev_id)
339
 
 
340
 
            # Process the post commit hooks, if any
341
 
            self._emit_progress_set_stage("Running post commit hooks")
342
342
            self._process_hooks(old_revno, new_revno)
343
343
        finally:
344
344
            self._cleanup()
467
467
 
468
468
    def _process_hooks(self, old_revno, new_revno):
469
469
        """Process any registered commit hooks."""
 
470
        # Process the post commit hooks, if any
 
471
        self._emit_progress_set_stage("Running post commit hooks")
470
472
        # old style commit hooks - should be deprecated ? (obsoleted in
471
473
        # 0.15)
472
474
        if self.config.post_commit() is not None:
492
494
        else:
493
495
            old_revid = bzrlib.revision.NULL_REVISION
494
496
        for hook in Branch.hooks['post_commit']:
 
497
            # show the running hook in the progress bar. As hooks may
 
498
            # end up doing nothing (e.g. because they are not configured by
 
499
            # the user) this is still showing progress, not showing overall
 
500
            # actions - its up to each plugin to show a UI if it want's to
 
501
            # (such as 'Emailing diff to foo@example.com').
 
502
            self.pb_stage_name = "Running post commit hooks [%s]" % \
 
503
                Branch.hooks.get_hook_name(hook)
 
504
            self._emit_progress()
 
505
            if 'hooks' in debug.debug_flags:
 
506
                mutter("Invoking commit hook: %r", hook)
495
507
            hook(hook_local, hook_master, old_revno, old_revid, new_revno,
496
508
                self.rev_id)
497
509
 
563
575
            else:
564
576
                mutter('commit parent ghost revision {%s}', revision)
565
577
 
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
 
        """
 
578
    def _update_builder_with_changes(self):
 
579
        """Update the commit builder with the data about what has changed.
 
580
        """
 
581
        # Build the revision inventory.
 
582
        #
 
583
        # This starts by creating a new empty inventory. Depending on
 
584
        # which files are selected for commit, and what is present in the
 
585
        # current tree, the new inventory is populated. inventory entries 
 
586
        # which are candidates for modification have their revision set to
 
587
        # None; inventory entries that are carried over untouched have their
 
588
        # revision set to their prior value.
 
589
        #
602
590
        # ESEPARATIONOFCONCERNS: this function is diffing and using the diff
603
591
        # results to create a new inventory at the same time, which results
604
592
        # in bugs like #46635.  Any reason not to use/enhance Tree.changes_from?
605
593
        # 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()
 
594
 
 
595
        specific_files = self.specific_files
 
596
        mutter("Selecting files for commit with filter %s", specific_files)
 
597
        work_inv = self.work_tree.inventory
 
598
        assert work_inv.root is not None
 
599
        self.pb_entries_total = len(work_inv)
 
600
 
 
601
        # Check and warn about old CommitBuilders
 
602
        entries = work_inv.iter_entries()
609
603
        if not self.builder.record_root_entry:
610
604
            symbol_versioning.warn('CommitBuilders should support recording'
611
605
                ' the root entry as of bzr 0.10.', DeprecationWarning, 
612
606
                stacklevel=1)
613
607
            self.builder.new_inventory.add(self.basis_inv.root.copy())
614
608
            entries.next()
615
 
        self.pb_entries_total = len(self.work_inv)
 
609
 
 
610
        deleted_ids = []
 
611
        deleted_paths = set()
616
612
        for path, new_ie in entries:
617
613
            self._emit_progress_next_entry()
618
614
            file_id = new_ie.file_id
 
615
 
 
616
            # Skip files that have been deleted from the working tree.
 
617
            # The deleted files/directories are also recorded so they
 
618
            # can be explicitly unversioned later. Note that when a
 
619
            # filter of specific files is given, we must only skip/record
 
620
            # deleted files matching that filter.
 
621
            if is_inside_any(deleted_paths, path):
 
622
                continue
 
623
            if not specific_files or is_inside_any(specific_files, path):
 
624
                if not self.work_tree.has_filename(path):
 
625
                    deleted_paths.add(path)
 
626
                    self.reporter.missing(path)
 
627
                    deleted_ids.append(file_id)
 
628
                    continue
619
629
            try:
620
630
                kind = self.work_tree.kind(file_id)
621
631
                if kind == 'tree-reference' and self.recursive == 'down':
649
659
            except errors.NoSuchFile:
650
660
                pass
651
661
            # 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)):
 
662
            if (not specific_files or 
 
663
                is_inside_or_parent_of_any(specific_files, path)):
654
664
                    # mutter('%s selected for commit', path)
655
665
                    ie = new_ie.copy()
656
666
                    ie.revision = None
677
687
            else:
678
688
                self.reporter.snapshot_change(change, path)
679
689
 
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)
 
690
        # Unversion IDs that were found to be deleted
 
691
        self.work_tree.unversion(deleted_ids)
 
692
 
 
693
        # If specific files/directories were nominated, it is possible
 
694
        # that some data from outside those needs to be preserved from
 
695
        # the basis tree. For example, if a file x is moved from out of
 
696
        # directory foo into directory bar and the user requests
 
697
        # ``commit foo``, then information about bar/x must also be
 
698
        # recorded.
 
699
        if specific_files:
 
700
            for path, new_ie in self.basis_inv.iter_entries():
 
701
                if new_ie.file_id in work_inv:
 
702
                    continue
 
703
                if is_inside_any(specific_files, path):
 
704
                    continue
 
705
                ie = new_ie.copy()
 
706
                ie.revision = None
 
707
                self.builder.record_entry_contents(ie, self.parent_invs, path,
 
708
                                                   self.basis_tree)
 
709
 
 
710
        # Report what was deleted. We could skip this when no deletes are
 
711
        # detected to gain a performance win, but it arguably serves as a
 
712
        # 'safety check' by informing the user whenever anything disappears.
 
713
        for path, ie in self.basis_inv.iter_entries():
 
714
            if ie.file_id not in self.builder.new_inventory:
 
715
                self.reporter.deleted(path)
693
716
 
694
717
    def _emit_progress_set_stage(self, name, show_entries=False):
695
718
        """Set the progress stage and emit an update to the progress bar."""
714
737
            text = "%s - Stage" % (self.pb_stage_name)
715
738
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
716
739
 
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