~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

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
17
18
 
18
19
# The newly committed revision is going to have a shape corresponding
19
20
# to that of the working tree.  Files that are not in the
52
53
from bzrlib import (
53
54
    debug,
54
55
    errors,
55
 
    revision,
56
56
    trace,
57
57
    tree,
58
58
    ui,
73
73
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
74
74
from bzrlib import symbol_versioning
75
75
from bzrlib.urlutils import unescape_for_display
76
 
 
 
76
from bzrlib.i18n import gettext
77
77
 
78
78
class NullCommitReporter(object):
79
79
    """I report on progress of a commit."""
114
114
        note(format, *args)
115
115
 
116
116
    def snapshot_change(self, change, path):
117
 
        if path == '' and change in ('added', 'modified'):
 
117
        if path == '' and change in (gettext('added'), gettext('modified')):
118
118
            return
119
119
        self._note("%s %s", change, path)
120
120
 
128
128
                                   "to started.", DeprecationWarning,
129
129
                                   stacklevel=2)
130
130
            location = ''
131
 
        self._note('Committing%s', location)
 
131
        self._note(gettext('Committing%s'), location)
132
132
 
133
133
    def completed(self, revno, rev_id):
134
 
        self._note('Committed revision %d.', revno)
 
134
        self._note(gettext('Committed revision %d.'), revno)
135
135
        # self._note goes to the console too; so while we want to log the
136
136
        # rev_id, we can't trivially only log it. (See bug 526425). Long
137
137
        # term we should rearrange the reporting structure, but for now
140
140
        mutter('Committed revid %s as revno %d.', rev_id, revno)
141
141
 
142
142
    def deleted(self, path):
143
 
        self._note('deleted %s', path)
 
143
        self._note(gettext('deleted %s'), path)
144
144
 
145
145
    def missing(self, path):
146
 
        self._note('missing %s', path)
 
146
        self._note(gettext('missing %s'), path)
147
147
 
148
148
    def renamed(self, change, old_path, new_path):
149
149
        self._note('%s %s => %s', change, old_path, new_path)
166
166
    """
167
167
    def __init__(self,
168
168
                 reporter=None,
169
 
                 config=None):
 
169
                 config_stack=None):
170
170
        """Create a Commit object.
171
171
 
172
172
        :param reporter: the default reporter to use or None to decide later
173
173
        """
174
174
        self.reporter = reporter
175
 
        self.config = config
 
175
        self.config_stack = config_stack
176
176
 
177
177
    @staticmethod
178
178
    def update_revprops(revprops, branch, authors=None, author=None,
269
269
        operation = OperationWithCleanups(self._commit)
270
270
        self.revprops = revprops or {}
271
271
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
272
 
        self.config = config or self.config
 
272
        self.config_stack = config or self.config_stack
273
273
        return operation.run(
274
274
               message=message,
275
275
               timestamp=timestamp,
364
364
        self._check_bound_branch(operation, possible_master_transports)
365
365
 
366
366
        # Check that the working tree is up to date
367
 
        old_revno, new_revno = self._check_out_of_date_tree()
 
367
        old_revno, old_revid, new_revno = self._check_out_of_date_tree()
368
368
 
369
369
        # Complete configuration setup
370
370
        if reporter is not None:
371
371
            self.reporter = reporter
372
372
        elif self.reporter is None:
373
373
            self.reporter = self._select_reporter()
374
 
        if self.config is None:
375
 
            self.config = self.branch.get_config()
 
374
        if self.config_stack is None:
 
375
            self.config_stack = self.work_tree.get_config_stack()
376
376
 
377
377
        self._set_specific_file_ids()
378
378
 
406
406
        self._set_progress_stage("Collecting changes", counter=True)
407
407
        self._lossy = lossy
408
408
        self.builder = self.branch.get_commit_builder(self.parents,
409
 
            self.config, timestamp, timezone, committer, self.revprops,
 
409
            self.config_stack, timestamp, timezone, committer, self.revprops,
410
410
            rev_id, lossy=lossy)
411
411
        if not self.builder.supports_record_entry_contents and self.exclude:
412
412
            self.builder.abort()
413
413
            raise errors.ExcludesUnsupported(self.branch.repository)
414
414
 
 
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
 
415
421
        try:
416
422
            self.builder.will_record_deletes()
417
423
            # find the location being committed to
443
449
        except Exception, e:
444
450
            mutter("aborting commit write group because of exception:")
445
451
            trace.log_exception_quietly()
446
 
            note("aborting commit write group: %r" % (e,))
447
452
            self.builder.abort()
448
453
            raise
449
454
 
450
 
        self._process_pre_hooks(old_revno, new_revno)
451
 
 
452
 
        # Upload revision data to the master.
453
 
        # this will propagate merged revisions too if needed.
454
 
        if self.bound_branch:
455
 
            self._set_progress_stage("Uploading data to master branch")
456
 
            # 'commit' to the master first so a timeout here causes the
457
 
            # local branch to be out of date
458
 
            (new_revno, self.rev_id) = self.master_branch.import_last_revision_info_and_tags(
459
 
                self.branch, new_revno, self.rev_id, lossy=lossy)
460
 
            if lossy:
461
 
                self.branch.fetch(self.master_branch, self.rev_id)
462
 
 
463
 
        # and now do the commit locally.
464
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
465
 
 
466
 
        # Merge local tags to remote
467
 
        if self.bound_branch:
468
 
            self._set_progress_stage("Merging tags to master branch")
469
 
            tag_conflicts = self.branch.tags.merge_to(self.master_branch.tags)
470
 
            if tag_conflicts:
471
 
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
472
 
                note("Conflicting tags in bound branch:\n" +
473
 
                    "\n".join(warning_lines))
 
455
        self._update_branches(old_revno, old_revid, new_revno)
474
456
 
475
457
        # Make the working tree be up to date with the branch. This
476
458
        # includes automatic changes scheduled to be made to the tree, such
483
465
        self._process_post_hooks(old_revno, new_revno)
484
466
        return self.rev_id
485
467
 
 
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
 
486
514
    def _select_reporter(self):
487
515
        """Select the CommitReporter to use."""
488
516
        if is_quiet():
495
523
        # A merge with no effect on files
496
524
        if len(self.parents) > 1:
497
525
            return
498
 
        # TODO: we could simplify this by using self.builder.basis_delta.
499
 
 
500
 
        # The initial commit adds a root directory, but this in itself is not
501
 
        # a worthwhile commit.
502
 
        if (self.basis_revid == revision.NULL_REVISION and
503
 
            ((self.builder.new_inventory is not None and
504
 
             len(self.builder.new_inventory) == 1) or
505
 
            len(self.builder._basis_delta) == 1)):
506
 
            raise PointlessCommit()
507
526
        if self.builder.any_changes():
508
527
            return
509
528
        raise PointlessCommit()
554
573
    def _check_out_of_date_tree(self):
555
574
        """Check that the working tree is up to date.
556
575
 
557
 
        :return: old_revision_number,new_revision_number tuple
 
576
        :return: old_revision_number, old_revision_id, new_revision_number
 
577
            tuple
558
578
        """
559
579
        try:
560
580
            first_tree_parent = self.work_tree.get_parent_ids()[0]
573
593
        else:
574
594
            # ghost parents never appear in revision history.
575
595
            new_revno = 1
576
 
        return old_revno,new_revno
 
596
        return old_revno, master_last, new_revno
577
597
 
578
598
    def _process_pre_hooks(self, old_revno, new_revno):
579
599
        """Process any registered pre commit hooks."""
585
605
        # Process the post commit hooks, if any
586
606
        self._set_progress_stage("Running post_commit hooks")
587
607
        # old style commit hooks - should be deprecated ? (obsoleted in
588
 
        # 0.15)
589
 
        if self.config.post_commit() is not None:
590
 
            hooks = self.config.post_commit().split(' ')
 
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(' ')
591
612
            # this would be nicer with twisted.python.reflect.namedAny
592
613
            for hook in hooks:
593
614
                result = eval(hook + '(branch, rev_id)',
646
667
        # entries and the order is preserved when doing this.
647
668
        if self.use_record_iter_changes:
648
669
            return
649
 
        self.basis_inv = self.basis_tree.inventory
 
670
        self.basis_inv = self.basis_tree.root_inventory
650
671
        self.parent_invs = [self.basis_inv]
651
672
        for revision in self.parents[1:]:
652
673
            if self.branch.repository.has_revision(revision):
705
726
                # Reset the new path (None) and new versioned flag (False)
706
727
                change = (change[0], (change[1][0], None), change[2],
707
728
                    (change[3][0], False)) + change[4:]
 
729
                new_path = change[1][1]
 
730
                versioned = False
708
731
            elif kind == 'tree-reference':
709
732
                if self.recursive == 'down':
710
733
                    self._commit_nested_tree(change[0], change[1][1])
714
737
                    if new_path is None:
715
738
                        reporter.deleted(old_path)
716
739
                    elif old_path is None:
717
 
                        reporter.snapshot_change('added', new_path)
 
740
                        reporter.snapshot_change(gettext('added'), new_path)
718
741
                    elif old_path != new_path:
719
 
                        reporter.renamed('renamed', old_path, new_path)
 
742
                        reporter.renamed(gettext('renamed'), old_path, new_path)
720
743
                    else:
721
744
                        if (new_path or 
722
745
                            self.work_tree.branch.repository._format.rich_root_data):
723
746
                            # Don't report on changes to '' in non rich root
724
747
                            # repositories.
725
 
                            reporter.snapshot_change('modified', new_path)
 
748
                            reporter.snapshot_change(gettext('modified'), new_path)
726
749
            self._next_progress_entry()
727
750
        # Unversion IDs that were found to be deleted
728
751
        self.deleted_ids = deleted_ids
734
757
        if self.specific_files or self.exclude:
735
758
            specific_files = self.specific_files or []
736
759
            for path, old_ie in self.basis_inv.iter_entries():
737
 
                if old_ie.file_id in self.builder.new_inventory:
 
760
                if self.builder.new_inventory.has_id(old_ie.file_id):
738
761
                    # already added - skip.
739
762
                    continue
740
763
                if (is_inside_any(specific_files, path)
811
834
        deleted_paths = {}
812
835
        # XXX: Note that entries may have the wrong kind because the entry does
813
836
        # not reflect the status on disk.
814
 
        work_inv = self.work_tree.inventory
815
837
        # NB: entries will include entries within the excluded ids/paths
816
838
        # because iter_entries_by_dir has no 'exclude' facility today.
817
 
        entries = work_inv.iter_entries_by_dir(
 
839
        entries = self.work_tree.iter_entries_by_dir(
818
840
            specific_file_ids=self.specific_file_ids, yield_parents=True)
819
841
        for path, existing_ie in entries:
820
842
            file_id = existing_ie.file_id
951
973
            self.reporter.renamed(change, old_path, path)
952
974
            self._next_progress_entry()
953
975
        else:
954
 
            if change == 'unchanged':
 
976
            if change == gettext('unchanged'):
955
977
                return
956
978
            self.reporter.snapshot_change(change, path)
957
979
            self._next_progress_entry()
973
995
 
974
996
    def _emit_progress(self):
975
997
        if self.pb_entries_count is not None:
976
 
            text = "%s [%d] - Stage" % (self.pb_stage_name,
 
998
            text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
977
999
                self.pb_entries_count)
978
1000
        else:
979
 
            text = "%s - Stage" % (self.pb_stage_name, )
 
1001
            text = gettext("%s - Stage") % (self.pb_stage_name, )
980
1002
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
981
1003
 
982
1004
    def _set_specific_file_ids(self):