~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Andrew Bennetts
  • Date: 2011-07-11 02:06:18 UTC
  • mfrom: (6018 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6020.
  • Revision ID: andrew.bennetts@canonical.com-20110711020618-gcjgoeb5azwjrnu6
Merge lp:bzr/2.4.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
17
 
19
18
# The newly committed revision is going to have a shape corresponding
20
19
# to that of the working tree.  Files that are not in the
73
72
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
74
73
from bzrlib import symbol_versioning
75
74
from bzrlib.urlutils import unescape_for_display
76
 
from bzrlib.i18n import gettext
 
75
 
77
76
 
78
77
class NullCommitReporter(object):
79
78
    """I report on progress of a commit."""
114
113
        note(format, *args)
115
114
 
116
115
    def snapshot_change(self, change, path):
117
 
        if path == '' and change in (gettext('added'), gettext('modified')):
 
116
        if path == '' and change in ('added', 'modified'):
118
117
            return
119
118
        self._note("%s %s", change, path)
120
119
 
128
127
                                   "to started.", DeprecationWarning,
129
128
                                   stacklevel=2)
130
129
            location = ''
131
 
        self._note(gettext('Committing%s'), location)
 
130
        self._note('Committing%s', location)
132
131
 
133
132
    def completed(self, revno, rev_id):
134
 
        self._note(gettext('Committed revision %d.'), revno)
 
133
        self._note('Committed revision %d.', revno)
135
134
        # self._note goes to the console too; so while we want to log the
136
135
        # rev_id, we can't trivially only log it. (See bug 526425). Long
137
136
        # term we should rearrange the reporting structure, but for now
140
139
        mutter('Committed revid %s as revno %d.', rev_id, revno)
141
140
 
142
141
    def deleted(self, path):
143
 
        self._note(gettext('deleted %s'), path)
 
142
        self._note('deleted %s', path)
144
143
 
145
144
    def missing(self, path):
146
 
        self._note(gettext('missing %s'), path)
 
145
        self._note('missing %s', path)
147
146
 
148
147
    def renamed(self, change, old_path, new_path):
149
148
        self._note('%s %s => %s', change, old_path, new_path)
166
165
    """
167
166
    def __init__(self,
168
167
                 reporter=None,
169
 
                 config_stack=None):
 
168
                 config=None):
170
169
        """Create a Commit object.
171
170
 
172
171
        :param reporter: the default reporter to use or None to decide later
173
172
        """
174
173
        self.reporter = reporter
175
 
        self.config_stack = config_stack
 
174
        self.config = config
176
175
 
177
176
    @staticmethod
178
177
    def update_revprops(revprops, branch, authors=None, author=None,
269
268
        operation = OperationWithCleanups(self._commit)
270
269
        self.revprops = revprops or {}
271
270
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
272
 
        self.config_stack = config or self.config_stack
 
271
        self.config = config or self.config
273
272
        return operation.run(
274
273
               message=message,
275
274
               timestamp=timestamp,
364
363
        self._check_bound_branch(operation, possible_master_transports)
365
364
 
366
365
        # Check that the working tree is up to date
367
 
        old_revno, old_revid, new_revno = self._check_out_of_date_tree()
 
366
        old_revno, new_revno = self._check_out_of_date_tree()
368
367
 
369
368
        # Complete configuration setup
370
369
        if reporter is not None:
371
370
            self.reporter = reporter
372
371
        elif self.reporter is None:
373
372
            self.reporter = self._select_reporter()
374
 
        if self.config_stack is None:
375
 
            self.config_stack = self.work_tree.get_config_stack()
 
373
        if self.config is None:
 
374
            self.config = self.branch.get_config()
376
375
 
377
376
        self._set_specific_file_ids()
378
377
 
406
405
        self._set_progress_stage("Collecting changes", counter=True)
407
406
        self._lossy = lossy
408
407
        self.builder = self.branch.get_commit_builder(self.parents,
409
 
            self.config_stack, timestamp, timezone, committer, self.revprops,
 
408
            self.config, timestamp, timezone, committer, self.revprops,
410
409
            rev_id, lossy=lossy)
411
410
        if not self.builder.supports_record_entry_contents and self.exclude:
412
411
            self.builder.abort()
413
412
            raise errors.ExcludesUnsupported(self.branch.repository)
414
413
 
415
 
        if self.builder.updates_branch and self.bound_branch:
416
 
            self.builder.abort()
417
 
            raise AssertionError(
418
 
                "bound branches not supported for commit builders "
419
 
                "that update the branch")
420
 
 
421
414
        try:
422
415
            self.builder.will_record_deletes()
423
416
            # find the location being committed to
452
445
            self.builder.abort()
453
446
            raise
454
447
 
455
 
        self._update_branches(old_revno, old_revid, new_revno)
 
448
        self._process_pre_hooks(old_revno, new_revno)
 
449
 
 
450
        # Upload revision data to the master.
 
451
        # this will propagate merged revisions too if needed.
 
452
        if self.bound_branch:
 
453
            self._set_progress_stage("Uploading data to master branch")
 
454
            # 'commit' to the master first so a timeout here causes the
 
455
            # local branch to be out of date
 
456
            (new_revno, self.rev_id) = self.master_branch.import_last_revision_info_and_tags(
 
457
                self.branch, new_revno, self.rev_id, lossy=lossy)
 
458
            if lossy:
 
459
                self.branch.fetch(self.master_branch, self.rev_id)
 
460
 
 
461
        # and now do the commit locally.
 
462
        self.branch.set_last_revision_info(new_revno, self.rev_id)
 
463
 
 
464
        # Merge local tags to remote
 
465
        if self.bound_branch:
 
466
            self._set_progress_stage("Merging tags to master branch")
 
467
            tag_conflicts = self.branch.tags.merge_to(self.master_branch.tags)
 
468
            if tag_conflicts:
 
469
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
 
470
                note("Conflicting tags in bound branch:\n" +
 
471
                    "\n".join(warning_lines))
456
472
 
457
473
        # Make the working tree be up to date with the branch. This
458
474
        # includes automatic changes scheduled to be made to the tree, such
465
481
        self._process_post_hooks(old_revno, new_revno)
466
482
        return self.rev_id
467
483
 
468
 
    def _update_branches(self, old_revno, old_revid, new_revno):
469
 
        """Update the master and local branch to the new revision.
470
 
 
471
 
        This will try to make sure that the master branch is updated
472
 
        before the local branch.
473
 
 
474
 
        :param old_revno: Revision number of master branch before the
475
 
            commit
476
 
        :param old_revid: Tip of master branch before the commit
477
 
        :param new_revno: Revision number of the new commit
478
 
        """
479
 
        if not self.builder.updates_branch:
480
 
            self._process_pre_hooks(old_revno, new_revno)
481
 
 
482
 
            # Upload revision data to the master.
483
 
            # this will propagate merged revisions too if needed.
484
 
            if self.bound_branch:
485
 
                self._set_progress_stage("Uploading data to master branch")
486
 
                # 'commit' to the master first so a timeout here causes the
487
 
                # local branch to be out of date
488
 
                (new_revno, self.rev_id) = self.master_branch.import_last_revision_info_and_tags(
489
 
                    self.branch, new_revno, self.rev_id, lossy=self._lossy)
490
 
                if self._lossy:
491
 
                    self.branch.fetch(self.master_branch, self.rev_id)
492
 
 
493
 
            # and now do the commit locally.
494
 
            self.branch.set_last_revision_info(new_revno, self.rev_id)
495
 
        else:
496
 
            try:
497
 
                self._process_pre_hooks(old_revno, new_revno)
498
 
            except:
499
 
                # The commit builder will already have updated the branch,
500
 
                # revert it.
501
 
                self.branch.set_last_revision_info(old_revno, old_revid)
502
 
                raise
503
 
 
504
 
        # Merge local tags to remote
505
 
        if self.bound_branch:
506
 
            self._set_progress_stage("Merging tags to master branch")
507
 
            tag_updates, tag_conflicts = self.branch.tags.merge_to(
508
 
                self.master_branch.tags)
509
 
            if tag_conflicts:
510
 
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
511
 
                note( gettext("Conflicting tags in bound branch:\n{0}".format(
512
 
                    "\n".join(warning_lines))) )
513
 
 
514
484
    def _select_reporter(self):
515
485
        """Select the CommitReporter to use."""
516
486
        if is_quiet():
573
543
    def _check_out_of_date_tree(self):
574
544
        """Check that the working tree is up to date.
575
545
 
576
 
        :return: old_revision_number, old_revision_id, new_revision_number
577
 
            tuple
 
546
        :return: old_revision_number,new_revision_number tuple
578
547
        """
579
548
        try:
580
549
            first_tree_parent = self.work_tree.get_parent_ids()[0]
593
562
        else:
594
563
            # ghost parents never appear in revision history.
595
564
            new_revno = 1
596
 
        return old_revno, master_last, new_revno
 
565
        return old_revno,new_revno
597
566
 
598
567
    def _process_pre_hooks(self, old_revno, new_revno):
599
568
        """Process any registered pre commit hooks."""
605
574
        # Process the post commit hooks, if any
606
575
        self._set_progress_stage("Running post_commit hooks")
607
576
        # old style commit hooks - should be deprecated ? (obsoleted in
608
 
        # 0.15^H^H^H^H 2.5.0)
609
 
        post_commit = self.config_stack.get('post_commit')
610
 
        if post_commit is not None:
611
 
            hooks = post_commit.split(' ')
 
577
        # 0.15)
 
578
        if self.config.post_commit() is not None:
 
579
            hooks = self.config.post_commit().split(' ')
612
580
            # this would be nicer with twisted.python.reflect.namedAny
613
581
            for hook in hooks:
614
582
                result = eval(hook + '(branch, rev_id)',
667
635
        # entries and the order is preserved when doing this.
668
636
        if self.use_record_iter_changes:
669
637
            return
670
 
        self.basis_inv = self.basis_tree.root_inventory
 
638
        self.basis_inv = self.basis_tree.inventory
671
639
        self.parent_invs = [self.basis_inv]
672
640
        for revision in self.parents[1:]:
673
641
            if self.branch.repository.has_revision(revision):
726
694
                # Reset the new path (None) and new versioned flag (False)
727
695
                change = (change[0], (change[1][0], None), change[2],
728
696
                    (change[3][0], False)) + change[4:]
729
 
                new_path = change[1][1]
730
 
                versioned = False
731
697
            elif kind == 'tree-reference':
732
698
                if self.recursive == 'down':
733
699
                    self._commit_nested_tree(change[0], change[1][1])
737
703
                    if new_path is None:
738
704
                        reporter.deleted(old_path)
739
705
                    elif old_path is None:
740
 
                        reporter.snapshot_change(gettext('added'), new_path)
 
706
                        reporter.snapshot_change('added', new_path)
741
707
                    elif old_path != new_path:
742
 
                        reporter.renamed(gettext('renamed'), old_path, new_path)
 
708
                        reporter.renamed('renamed', old_path, new_path)
743
709
                    else:
744
710
                        if (new_path or 
745
711
                            self.work_tree.branch.repository._format.rich_root_data):
746
712
                            # Don't report on changes to '' in non rich root
747
713
                            # repositories.
748
 
                            reporter.snapshot_change(gettext('modified'), new_path)
 
714
                            reporter.snapshot_change('modified', new_path)
749
715
            self._next_progress_entry()
750
716
        # Unversion IDs that were found to be deleted
751
717
        self.deleted_ids = deleted_ids
834
800
        deleted_paths = {}
835
801
        # XXX: Note that entries may have the wrong kind because the entry does
836
802
        # not reflect the status on disk.
 
803
        work_inv = self.work_tree.inventory
837
804
        # NB: entries will include entries within the excluded ids/paths
838
805
        # because iter_entries_by_dir has no 'exclude' facility today.
839
 
        entries = self.work_tree.iter_entries_by_dir(
 
806
        entries = work_inv.iter_entries_by_dir(
840
807
            specific_file_ids=self.specific_file_ids, yield_parents=True)
841
808
        for path, existing_ie in entries:
842
809
            file_id = existing_ie.file_id
973
940
            self.reporter.renamed(change, old_path, path)
974
941
            self._next_progress_entry()
975
942
        else:
976
 
            if change == gettext('unchanged'):
 
943
            if change == 'unchanged':
977
944
                return
978
945
            self.reporter.snapshot_change(change, path)
979
946
            self._next_progress_entry()
995
962
 
996
963
    def _emit_progress(self):
997
964
        if self.pb_entries_count is not None:
998
 
            text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
 
965
            text = "%s [%d] - Stage" % (self.pb_stage_name,
999
966
                self.pb_entries_count)
1000
967
        else:
1001
 
            text = gettext("%s - Stage") % (self.pb_stage_name, )
 
968
            text = "%s - Stage" % (self.pb_stage_name, )
1002
969
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
1003
970
 
1004
971
    def _set_specific_file_ids(self):