~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-28 07:08:27 UTC
  • mfrom: (2553.1.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070628070827-h5s313dg5tnag9vj
(robertc) Show the names of commit hooks during commit.

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,
61
60
    errors,
62
61
    inventory,
63
62
    tree,
256
255
            # Check that the working tree is up to date
257
256
            old_revno,new_revno = self._check_out_of_date_tree()
258
257
 
 
258
            if strict:
 
259
                # raise an exception as soon as we find a single unknown.
 
260
                for unknown in self.work_tree.unknowns():
 
261
                    raise StrictCommitFailed()
 
262
                   
259
263
            if self.config is None:
260
264
                self.config = self.branch.get_config()
261
265
 
282
286
            self.pb.show_spinner = False
283
287
            self.pb.show_eta = False
284
288
            self.pb.show_count = True
285
 
            self.pb.show_bar = True
 
289
            self.pb.show_bar = False
286
290
 
287
291
            # After a merge, a selected file commit is not supported.
288
292
            # See 'bzr help merge' for an explanation as to why.
292
296
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
293
297
            
294
298
            # Collect the changes
295
 
            self._set_progress_stage("Collecting changes",
296
 
                    entries_title="Directory")
 
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
302
            self._update_builder_with_changes()
303
306
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
304
307
            # weave lines, because nothing should be recorded until it is known
305
308
            # that commit will succeed.
306
 
            self._set_progress_stage("Saving data locally")
 
309
            self._emit_progress_set_stage("Saving data locally")
307
310
            self.builder.finish_inventory()
308
311
 
309
312
            # Prompt the user for a commit message if none provided
318
321
            # Upload revision data to the master.
319
322
            # this will propagate merged revisions too if needed.
320
323
            if self.bound_branch:
321
 
                self._set_progress_stage("Uploading data to master branch")
 
324
                self._emit_progress_set_stage("Uploading data to master branch")
322
325
                self.master_branch.repository.fetch(self.branch.repository,
323
326
                                                    revision_id=self.rev_id)
324
327
                # now the master has the revision data
331
334
            self.branch.set_last_revision_info(new_revno, self.rev_id)
332
335
 
333
336
            # Make the working tree up to date with the branch
334
 
            self._set_progress_stage("Updating the working tree")
 
337
            self._emit_progress_set_stage("Updating the working tree")
335
338
            rev_tree = self.builder.revision_tree()
336
339
            self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
337
340
            self.reporter.completed(new_revno, self.rev_id)
464
467
    def _process_hooks(self, old_revno, new_revno):
465
468
        """Process any registered commit hooks."""
466
469
        # Process the post commit hooks, if any
467
 
        self._set_progress_stage("Running post commit hooks")
 
470
        self._emit_progress_set_stage("Running post commit hooks")
468
471
        # old style commit hooks - should be deprecated ? (obsoleted in
469
472
        # 0.15)
470
473
        if self.config.post_commit() is not None:
498
501
            self.pb_stage_name = "Running post commit hooks [%s]" % \
499
502
                Branch.hooks.get_hook_name(hook)
500
503
            self._emit_progress()
501
 
            if 'hooks' in debug.debug_flags:
502
 
                mutter("Invoking commit hook: %r", hook)
503
504
            hook(hook_local, hook_master, old_revno, old_revid, new_revno,
504
505
                self.rev_id)
505
506
 
590
591
 
591
592
        specific_files = self.specific_files
592
593
        mutter("Selecting files for commit with filter %s", specific_files)
 
594
        work_inv = self.work_tree.inventory
 
595
        assert work_inv.root is not None
 
596
        self.pb_entries_total = len(work_inv)
593
597
 
594
598
        # Check and warn about old CommitBuilders
 
599
        entries = work_inv.iter_entries()
595
600
        if not self.builder.record_root_entry:
596
601
            symbol_versioning.warn('CommitBuilders should support recording'
597
602
                ' the root entry as of bzr 0.10.', DeprecationWarning, 
598
603
                stacklevel=1)
599
604
            self.builder.new_inventory.add(self.basis_inv.root.copy())
600
 
 
601
 
        # Build the new inventory
602
 
        self._populate_from_inventory(specific_files)
603
 
 
604
 
        # If specific files are selected, then all un-selected files must be
605
 
        # recorded in their previous state. For more details, see
606
 
        # https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
607
 
        if specific_files:
608
 
            for path, new_ie in self.basis_inv.iter_entries():
609
 
                if new_ie.file_id in self.builder.new_inventory:
610
 
                    continue
611
 
                if is_inside_any(specific_files, path):
612
 
                    continue
613
 
                ie = new_ie.copy()
614
 
                ie.revision = None
615
 
                self.builder.record_entry_contents(ie, self.parent_invs, path,
616
 
                                                   self.basis_tree)
617
 
 
618
 
        # Report what was deleted. We could skip this when no deletes are
619
 
        # detected to gain a performance win, but it arguably serves as a
620
 
        # 'safety check' by informing the user whenever anything disappears.
621
 
        for path, ie in self.basis_inv.iter_entries():
622
 
            if ie.file_id not in self.builder.new_inventory:
623
 
                self.reporter.deleted(path)
624
 
 
625
 
    def _populate_from_inventory(self, specific_files):
626
 
        """Populate the CommitBuilder by walking the working tree inventory."""
627
 
        if self.strict:
628
 
            # raise an exception as soon as we find a single unknown.
629
 
            for unknown in self.work_tree.unknowns():
630
 
                raise StrictCommitFailed()
631
 
               
 
605
            entries.next()
 
606
 
632
607
        deleted_ids = []
633
608
        deleted_paths = set()
634
 
        work_inv = self.work_tree.inventory
635
 
        assert work_inv.root is not None
636
 
        entries = work_inv.iter_entries()
637
 
        if not self.builder.record_root_entry:
638
 
            entries.next()
639
 
        for path, existing_ie in entries:
640
 
            file_id = existing_ie.file_id
641
 
            name = existing_ie.name
642
 
            parent_id = existing_ie.parent_id
643
 
            kind = existing_ie.kind
644
 
            if kind == 'directory':
645
 
                self._next_progress_entry()
 
609
        for path, new_ie in entries:
 
610
            self._emit_progress_next_entry()
 
611
            file_id = new_ie.file_id
646
612
 
647
613
            # Skip files that have been deleted from the working tree.
648
614
            # The deleted files/directories are also recorded so they
659
625
                    continue
660
626
            try:
661
627
                kind = self.work_tree.kind(file_id)
662
 
                # TODO: specific_files filtering before nested tree processing
663
628
                if kind == 'tree-reference' and self.recursive == 'down':
664
 
                    self._commit_nested_tree(file_id, path)
 
629
                    # nested tree: commit in it
 
630
                    sub_tree = WorkingTree.open(self.work_tree.abspath(path))
 
631
                    # FIXME: be more comprehensive here:
 
632
                    # this works when both trees are in --trees repository,
 
633
                    # but when both are bound to a different repository,
 
634
                    # it fails; a better way of approaching this is to 
 
635
                    # finally implement the explicit-caches approach design
 
636
                    # a while back - RBC 20070306.
 
637
                    if (sub_tree.branch.repository.bzrdir.root_transport.base
 
638
                        ==
 
639
                        self.work_tree.branch.repository.bzrdir.root_transport.base):
 
640
                        sub_tree.branch.repository = \
 
641
                            self.work_tree.branch.repository
 
642
                    try:
 
643
                        sub_tree.commit(message=None, revprops=self.revprops,
 
644
                            recursive=self.recursive,
 
645
                            message_callback=self.message_callback,
 
646
                            timestamp=self.timestamp, timezone=self.timezone,
 
647
                            committer=self.committer,
 
648
                            allow_pointless=self.allow_pointless,
 
649
                            strict=self.strict, verbose=self.verbose,
 
650
                            local=self.local, reporter=self.reporter)
 
651
                    except errors.PointlessCommit:
 
652
                        pass
 
653
                if kind != new_ie.kind:
 
654
                    new_ie = inventory.make_entry(kind, new_ie.name,
 
655
                                                  new_ie.parent_id, file_id)
665
656
            except errors.NoSuchFile:
666
657
                pass
667
 
 
668
 
            # Record an entry for this item
669
 
            # Note: I don't particularly want to have the existing_ie
670
 
            # parameter but the test suite currently (28-Jun-07) breaks
671
 
            # without it thanks to a unicode normalisation issue. :-(
672
 
            definitely_changed = kind != existing_ie.kind 
673
 
            self._record_entry(path, file_id, specific_files, kind, name,
674
 
                parent_id, definitely_changed, existing_ie)
675
 
 
676
 
        # Unversion IDs that were found to be deleted
677
 
        self.work_tree.unversion(deleted_ids)
678
 
 
679
 
    def _commit_nested_tree(self, file_id, path):
680
 
        "Commit a nested tree."
681
 
        sub_tree = self.work_tree.get_nested_tree(file_id, path)
682
 
        # FIXME: be more comprehensive here:
683
 
        # this works when both trees are in --trees repository,
684
 
        # but when both are bound to a different repository,
685
 
        # it fails; a better way of approaching this is to 
686
 
        # finally implement the explicit-caches approach design
687
 
        # a while back - RBC 20070306.
688
 
        if (sub_tree.branch.repository.bzrdir.root_transport.base
689
 
            ==
690
 
            self.work_tree.branch.repository.bzrdir.root_transport.base):
691
 
            sub_tree.branch.repository = \
692
 
                self.work_tree.branch.repository
693
 
        try:
694
 
            sub_tree.commit(message=None, revprops=self.revprops,
695
 
                recursive=self.recursive,
696
 
                message_callback=self.message_callback,
697
 
                timestamp=self.timestamp, timezone=self.timezone,
698
 
                committer=self.committer,
699
 
                allow_pointless=self.allow_pointless,
700
 
                strict=self.strict, verbose=self.verbose,
701
 
                local=self.local, reporter=self.reporter)
702
 
        except errors.PointlessCommit:
703
 
            pass
704
 
 
705
 
    def _record_entry(self, path, file_id, specific_files, kind, name,
706
 
                      parent_id, definitely_changed, existing_ie=None):
707
 
        "Record the new inventory entry for a path if any."
708
 
        # mutter('check %s {%s}', path, file_id)
709
 
        if (not specific_files or 
710
 
            is_inside_or_parent_of_any(specific_files, path)):
711
 
                # mutter('%s selected for commit', path)
712
 
                if definitely_changed or existing_ie is None:
713
 
                    ie = inventory.make_entry(kind, name, parent_id, file_id)
 
658
            # mutter('check %s {%s}', path, file_id)
 
659
            if (not specific_files or 
 
660
                is_inside_or_parent_of_any(specific_files, path)):
 
661
                    # mutter('%s selected for commit', path)
 
662
                    ie = new_ie.copy()
 
663
                    ie.revision = None
 
664
            else:
 
665
                # mutter('%s not selected for commit', path)
 
666
                if self.basis_inv.has_id(file_id):
 
667
                    ie = self.basis_inv[file_id].copy()
714
668
                else:
715
 
                    ie = existing_ie.copy()
716
 
                    ie.revision = None
717
 
        else:
718
 
            # mutter('%s not selected for commit', path)
719
 
            if self.basis_inv.has_id(file_id):
720
 
                ie = self.basis_inv[file_id].copy()
721
 
            else:
722
 
                # this entry is new and not being committed
723
 
                ie = None
724
 
        if ie is not None:
 
669
                    # this entry is new and not being committed
 
670
                    continue
725
671
            self.builder.record_entry_contents(ie, self.parent_invs, 
726
672
                path, self.work_tree)
727
 
            self._report_change(ie, path)
728
 
        return ie
729
 
 
730
 
    def _report_change(self, ie, path):
731
 
        """Report a change to the user.
732
 
 
733
 
        The change that has occurred is described relative to the basis
734
 
        inventory.
735
 
        """
736
 
        if (self.basis_inv.has_id(ie.file_id)):
737
 
            basis_ie = self.basis_inv[ie.file_id]
738
 
        else:
739
 
            basis_ie = None
740
 
        change = ie.describe_change(basis_ie, ie)
741
 
        if change in (InventoryEntry.RENAMED, 
742
 
            InventoryEntry.MODIFIED_AND_RENAMED):
743
 
            old_path = self.basis_inv.id2path(ie.file_id)
744
 
            self.reporter.renamed(change, old_path, path)
745
 
        else:
746
 
            self.reporter.snapshot_change(change, path)
747
 
 
748
 
    def _set_progress_stage(self, name, entries_title=None):
 
673
            # describe the nature of the change that has occurred relative to
 
674
            # the basis inventory.
 
675
            if (self.basis_inv.has_id(ie.file_id)):
 
676
                basis_ie = self.basis_inv[ie.file_id]
 
677
            else:
 
678
                basis_ie = None
 
679
            change = ie.describe_change(basis_ie, ie)
 
680
            if change in (InventoryEntry.RENAMED, 
 
681
                InventoryEntry.MODIFIED_AND_RENAMED):
 
682
                old_path = self.basis_inv.id2path(ie.file_id)
 
683
                self.reporter.renamed(change, old_path, path)
 
684
            else:
 
685
                self.reporter.snapshot_change(change, path)
 
686
 
 
687
        # Unversion IDs that were found to be deleted
 
688
        self.work_tree.unversion(deleted_ids)
 
689
 
 
690
        # If specific files/directories were nominated, it is possible
 
691
        # that some data from outside those needs to be preserved from
 
692
        # the basis tree. For example, if a file x is moved from out of
 
693
        # directory foo into directory bar and the user requests
 
694
        # ``commit foo``, then information about bar/x must also be
 
695
        # recorded.
 
696
        if specific_files:
 
697
            for path, new_ie in self.basis_inv.iter_entries():
 
698
                if new_ie.file_id in work_inv:
 
699
                    continue
 
700
                if is_inside_any(specific_files, path):
 
701
                    continue
 
702
                ie = new_ie.copy()
 
703
                ie.revision = None
 
704
                self.builder.record_entry_contents(ie, self.parent_invs, path,
 
705
                                                   self.basis_tree)
 
706
 
 
707
        # Report what was deleted. We could skip this when no deletes are
 
708
        # detected to gain a performance win, but it arguably serves as a
 
709
        # 'safety check' by informing the user whenever anything disappears.
 
710
        for path, ie in self.basis_inv.iter_entries():
 
711
            if ie.file_id not in self.builder.new_inventory:
 
712
                self.reporter.deleted(path)
 
713
 
 
714
    def _emit_progress_set_stage(self, name, show_entries=False):
749
715
        """Set the progress stage and emit an update to the progress bar."""
750
716
        self.pb_stage_name = name
751
717
        self.pb_stage_count += 1
752
 
        self.pb_entries_title = entries_title
753
 
        if entries_title is not None:
 
718
        self.pb_entries_show = show_entries
 
719
        if show_entries:
754
720
            self.pb_entries_count = 0
755
721
            self.pb_entries_total = '?'
756
722
        self._emit_progress()
757
723
 
758
 
    def _next_progress_entry(self):
759
 
        """Emit an update to the progress bar and increment the entry count."""
 
724
    def _emit_progress_next_entry(self):
 
725
        """Emit an update to the progress bar and increment the file count."""
760
726
        self.pb_entries_count += 1
761
727
        self._emit_progress()
762
728
 
763
729
    def _emit_progress(self):
764
 
        if self.pb_entries_title:
765
 
            if self.pb_entries_total == '?':
766
 
                text = "%s [%s %d] - Stage" % (self.pb_stage_name,
767
 
                    self.pb_entries_title, self.pb_entries_count)
768
 
            else:
769
 
                text = "%s [%s %d/%s] - Stage" % (self.pb_stage_name,
770
 
                    self.pb_entries_title, self.pb_entries_count,
771
 
                    str(self.pb_entries_total))
 
730
        if self.pb_entries_show:
 
731
            text = "%s [Entry %d/%s] - Stage" % (self.pb_stage_name,
 
732
                self.pb_entries_count,str(self.pb_entries_total))
772
733
        else:
773
734
            text = "%s - Stage" % (self.pb_stage_name)
774
735
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)