/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-06-28 16:50:06 UTC
  • mfrom: (2561 +trunk)
  • mto: (2520.4.116 bzr.mpbundle)
  • mto: This revision was merged to the branch mainline in revision 2572.
  • Revision ID: abentley@panoramicfeedback.com-20070628165006-m7bd56ngqs26rd91
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
109
109
    def _note(self, format, *args):
110
110
        """Output a message.
111
111
 
112
 
        Messages are output by writing directly to stderr instead of
113
 
        using bzrlib.trace.note(). The latter constantly updates the
114
 
        log file as we go causing an unnecessary performance hit.
115
 
 
116
 
        Subclasses may choose to override this method but need to be aware
117
 
        of its potential impact on performance.
 
112
        Subclasses may choose to override this method.
118
113
        """
119
 
        bzrlib.ui.ui_factory.clear_term()
120
 
        sys.stderr.write((format + "\n") % args)
 
114
        note(format, *args)
121
115
 
122
116
    def snapshot_change(self, change, path):
123
117
        if change == 'unchanged':
269
263
            if self.config is None:
270
264
                self.config = self.branch.get_config()
271
265
 
272
 
            self.work_inv = self.work_tree.inventory
273
 
            self.basis_inv = self.basis_tree.inventory
 
266
            # If provided, ensure the specified files are versioned
274
267
            if specific_files is not None:
275
 
                # Ensure specified files are versioned
276
 
                # (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.
277
271
                # XXX: Dont we have filter_unversioned to do this more
278
272
                # cheaply?
279
273
                tree.find_ids_across_trees(specific_files,
280
274
                                           [self.basis_tree, self.work_tree])
281
275
 
282
 
            # Setup the progress bar ...
283
 
            # one to finish, one for rev and inventory, and one for each
284
 
            # inventory entry, and the same for the new inventory.
285
 
            # note that this estimate is too long when we do a partial tree
286
 
            # commit which excludes some new files from being considered.
287
 
            # The estimate is corrected when we populate the new inv.
288
 
            self.pb_total = len(self.work_inv) + 5
289
 
            self.pb_count = 0
 
276
            # Setup the progress bar. As the number of files that need to be
 
277
            # committed in unknown, progress is reported as stages.
 
278
            # We keep track of entries separately though and include that
 
279
            # information in the progress bar during the relevant stages.
 
280
            self.pb_stage_name = ""
 
281
            self.pb_stage_count = 0
 
282
            self.pb_stage_total = 4
 
283
            if self.bound_branch:
 
284
                self.pb_stage_total += 1
 
285
            self.pb.show_pct = False
 
286
            self.pb.show_spinner = False
 
287
            self.pb.show_eta = False
 
288
            self.pb.show_count = True
 
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
 
299
            self._emit_progress_set_stage("Collecting changes", show_entries=True)
296
300
            self.builder = self.branch.get_commit_builder(self.parents,
297
301
                self.config, timestamp, timezone, committer, revprops, rev_id)
298
 
            self._remove_deleted()
299
 
            self._populate_new_inv()
300
 
            self._report_deletes()
 
302
            self._update_builder_with_changes()
301
303
            self._check_pointless()
302
 
            self._emit_progress_update()
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
            self._emit_progress_set_stage("Saving data locally")
309
310
            self.builder.finish_inventory()
310
 
            self._emit_progress_update()
 
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
315
317
 
316
318
            # Add revision data to the local branch
317
319
            self.rev_id = self.builder.commit(self.message)
318
 
            self._emit_progress_update()
319
320
            
320
 
            # upload revision data to the master.
 
321
            # Upload revision data to the master.
321
322
            # this will propagate merged revisions too if needed.
322
323
            if self.bound_branch:
 
324
                self._emit_progress_set_stage("Uploading data to master branch")
323
325
                self.master_branch.repository.fetch(self.branch.repository,
324
326
                                                    revision_id=self.rev_id)
325
327
                # now the master has the revision data
332
334
            self.branch.set_last_revision_info(new_revno, self.rev_id)
333
335
 
334
336
            # Make the working tree up to date with the branch
 
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)
338
 
 
339
 
            # Process the post commit hooks, if any
340
341
            self._process_hooks(old_revno, new_revno)
341
 
            self._emit_progress_update()
342
342
        finally:
343
343
            self._cleanup()
344
344
        return self.rev_id
466
466
 
467
467
    def _process_hooks(self, old_revno, new_revno):
468
468
        """Process any registered commit hooks."""
 
469
        # Process the post commit hooks, if any
 
470
        self._emit_progress_set_stage("Running post commit hooks")
469
471
        # old style commit hooks - should be deprecated ? (obsoleted in
470
472
        # 0.15)
471
473
        if self.config.post_commit() is not None:
491
493
        else:
492
494
            old_revid = bzrlib.revision.NULL_REVISION
493
495
        for hook in Branch.hooks['post_commit']:
 
496
            # show the running hook in the progress bar. As hooks may
 
497
            # end up doing nothing (e.g. because they are not configured by
 
498
            # the user) this is still showing progress, not showing overall
 
499
            # actions - its up to each plugin to show a UI if it want's to
 
500
            # (such as 'Emailing diff to foo@example.com').
 
501
            self.pb_stage_name = "Running post commit hooks [%s]" % \
 
502
                Branch.hooks.get_hook_name(hook)
 
503
            self._emit_progress()
494
504
            hook(hook_local, hook_master, old_revno, old_revid, new_revno,
495
505
                self.rev_id)
496
506
 
562
572
            else:
563
573
                mutter('commit parent ghost revision {%s}', revision)
564
574
 
565
 
    def _remove_deleted(self):
566
 
        """Remove deleted files from the working inventories.
567
 
 
568
 
        This is done prior to taking the working inventory as the
569
 
        basis for the new committed inventory.
570
 
 
571
 
        This returns true if any files
572
 
        *that existed in the basis inventory* were deleted.
573
 
        Files that were added and deleted
574
 
        in the working copy don't matter.
575
 
        """
576
 
        specific = self.specific_files
577
 
        deleted_ids = []
578
 
        deleted_paths = set()
579
 
        for path, ie in self.work_inv.iter_entries():
580
 
            if is_inside_any(deleted_paths, path):
581
 
                # The tree will delete the required ids recursively.
582
 
                continue
583
 
            if specific and not is_inside_any(specific, path):
584
 
                continue
585
 
            if not self.work_tree.has_filename(path):
586
 
                deleted_paths.add(path)
587
 
                self.reporter.missing(path)
588
 
                deleted_ids.append(ie.file_id)
589
 
        self.work_tree.unversion(deleted_ids)
590
 
 
591
 
    def _populate_new_inv(self):
592
 
        """Build revision inventory.
593
 
 
594
 
        This creates a new empty inventory. Depending on
595
 
        which files are selected for commit, and what is present in the
596
 
        current tree, the new inventory is populated. inventory entries 
597
 
        which are candidates for modification have their revision set to
598
 
        None; inventory entries that are carried over untouched have their
599
 
        revision set to their prior value.
600
 
        """
 
575
    def _update_builder_with_changes(self):
 
576
        """Update the commit builder with the data about what has changed.
 
577
        """
 
578
        # Build the revision inventory.
 
579
        #
 
580
        # This starts by creating a new empty inventory. Depending on
 
581
        # which files are selected for commit, and what is present in the
 
582
        # current tree, the new inventory is populated. inventory entries 
 
583
        # which are candidates for modification have their revision set to
 
584
        # None; inventory entries that are carried over untouched have their
 
585
        # revision set to their prior value.
 
586
        #
601
587
        # ESEPARATIONOFCONCERNS: this function is diffing and using the diff
602
588
        # results to create a new inventory at the same time, which results
603
589
        # in bugs like #46635.  Any reason not to use/enhance Tree.changes_from?
604
590
        # ADHB 11-07-2006
605
 
        mutter("Selecting files for commit with filter %s", self.specific_files)
606
 
        assert self.work_inv.root is not None
607
 
        entries = self.work_inv.iter_entries()
 
591
 
 
592
        specific_files = self.specific_files
 
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)
 
597
 
 
598
        # Check and warn about old CommitBuilders
 
599
        entries = work_inv.iter_entries()
608
600
        if not self.builder.record_root_entry:
609
601
            symbol_versioning.warn('CommitBuilders should support recording'
610
602
                ' the root entry as of bzr 0.10.', DeprecationWarning, 
611
603
                stacklevel=1)
612
604
            self.builder.new_inventory.add(self.basis_inv.root.copy())
613
605
            entries.next()
614
 
            self._emit_progress_update()
 
606
 
 
607
        deleted_ids = []
 
608
        deleted_paths = set()
615
609
        for path, new_ie in entries:
616
 
            self._emit_progress_update()
 
610
            self._emit_progress_next_entry()
617
611
            file_id = new_ie.file_id
 
612
 
 
613
            # Skip files that have been deleted from the working tree.
 
614
            # The deleted files/directories are also recorded so they
 
615
            # can be explicitly unversioned later. Note that when a
 
616
            # filter of specific files is given, we must only skip/record
 
617
            # deleted files matching that filter.
 
618
            if is_inside_any(deleted_paths, path):
 
619
                continue
 
620
            if not specific_files or is_inside_any(specific_files, path):
 
621
                if not self.work_tree.has_filename(path):
 
622
                    deleted_paths.add(path)
 
623
                    self.reporter.missing(path)
 
624
                    deleted_ids.append(file_id)
 
625
                    continue
618
626
            try:
619
627
                kind = self.work_tree.kind(file_id)
620
628
                if kind == 'tree-reference' and self.recursive == 'down':
648
656
            except errors.NoSuchFile:
649
657
                pass
650
658
            # mutter('check %s {%s}', path, file_id)
651
 
            if (not self.specific_files or 
652
 
                is_inside_or_parent_of_any(self.specific_files, path)):
 
659
            if (not specific_files or 
 
660
                is_inside_or_parent_of_any(specific_files, path)):
653
661
                    # mutter('%s selected for commit', path)
654
662
                    ie = new_ie.copy()
655
663
                    ie.revision = None
676
684
            else:
677
685
                self.reporter.snapshot_change(change, path)
678
686
 
679
 
        if not self.specific_files:
680
 
            return
681
 
 
682
 
        # ignore removals that don't match filespec
683
 
        for path, new_ie in self.basis_inv.iter_entries():
684
 
            if new_ie.file_id in self.work_inv:
685
 
                continue
686
 
            if is_inside_any(self.specific_files, path):
687
 
                continue
688
 
            ie = new_ie.copy()
689
 
            ie.revision = None
690
 
            self.builder.record_entry_contents(ie, self.parent_invs, path,
691
 
                                               self.basis_tree)
692
 
 
693
 
    def _emit_progress_update(self):
694
 
        """Emit an update to the progress bar."""
695
 
        self.pb.update("Committing", self.pb_count, self.pb_total)
696
 
        self.pb_count += 1
697
 
 
698
 
    def _report_deletes(self):
 
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.
699
710
        for path, ie in self.basis_inv.iter_entries():
700
711
            if ie.file_id not in self.builder.new_inventory:
701
712
                self.reporter.deleted(path)
702
713
 
 
714
    def _emit_progress_set_stage(self, name, show_entries=False):
 
715
        """Set the progress stage and emit an update to the progress bar."""
 
716
        self.pb_stage_name = name
 
717
        self.pb_stage_count += 1
 
718
        self.pb_entries_show = show_entries
 
719
        if show_entries:
 
720
            self.pb_entries_count = 0
 
721
            self.pb_entries_total = '?'
 
722
        self._emit_progress()
 
723
 
 
724
    def _emit_progress_next_entry(self):
 
725
        """Emit an update to the progress bar and increment the file count."""
 
726
        self.pb_entries_count += 1
 
727
        self._emit_progress()
 
728
 
 
729
    def _emit_progress(self):
 
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))
 
733
        else:
 
734
            text = "%s - Stage" % (self.pb_stage_name)
 
735
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
703
736