/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: Vincent Ladeuil
  • Date: 2011-12-01 11:41:07 UTC
  • mto: This revision was merged to the branch mainline in revision 6347.
  • Revision ID: v.ladeuil+lp@free.fr-20111201114107-bfjulsggilgm6iei
Add the devnotes/configurations.txt as doc/developers/new-config-rationale.txt with minimal edits to use http:/pad.lv to reference bugs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
52
52
from bzrlib import (
53
53
    debug,
54
54
    errors,
55
 
    revision,
56
55
    trace,
57
56
    tree,
 
57
    ui,
58
58
    )
59
59
from bzrlib.branch import Branch
60
60
from bzrlib.cleanup import OperationWithCleanups
72
72
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
73
73
from bzrlib import symbol_versioning
74
74
from bzrlib.urlutils import unescape_for_display
75
 
import bzrlib.ui
76
 
 
 
75
from bzrlib.i18n import gettext
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 ('added', 'modified'):
 
116
        if path == '' and change in (gettext('added'), gettext('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('Committing%s', location)
 
130
        self._note(gettext('Committing%s'), location)
132
131
 
133
132
    def completed(self, revno, rev_id):
134
 
        self._note('Committed revision %d.', revno)
 
133
        self._note(gettext('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('deleted %s', path)
 
142
        self._note(gettext('deleted %s'), path)
144
143
 
145
144
    def missing(self, path):
146
 
        self._note('missing %s', path)
 
145
        self._note(gettext('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)
174
173
        self.reporter = reporter
175
174
        self.config = config
176
175
 
 
176
    @staticmethod
 
177
    def update_revprops(revprops, branch, authors=None, author=None,
 
178
                        local=False, possible_master_transports=None):
 
179
        if revprops is None:
 
180
            revprops = {}
 
181
        if possible_master_transports is None:
 
182
            possible_master_transports = []
 
183
        if not 'branch-nick' in revprops:
 
184
            revprops['branch-nick'] = branch._get_nick(
 
185
                local,
 
186
                possible_master_transports)
 
187
        if authors is not None:
 
188
            if author is not None:
 
189
                raise AssertionError('Specifying both author and authors '
 
190
                        'is not allowed. Specify just authors instead')
 
191
            if 'author' in revprops or 'authors' in revprops:
 
192
                # XXX: maybe we should just accept one of them?
 
193
                raise AssertionError('author property given twice')
 
194
            if authors:
 
195
                for individual in authors:
 
196
                    if '\n' in individual:
 
197
                        raise AssertionError('\\n is not a valid character '
 
198
                                'in an author identity')
 
199
                revprops['authors'] = '\n'.join(authors)
 
200
        if author is not None:
 
201
            symbol_versioning.warn('The parameter author was deprecated'
 
202
                   ' in version 1.13. Use authors instead',
 
203
                   DeprecationWarning)
 
204
            if 'author' in revprops or 'authors' in revprops:
 
205
                # XXX: maybe we should just accept one of them?
 
206
                raise AssertionError('author property given twice')
 
207
            if '\n' in author:
 
208
                raise AssertionError('\\n is not a valid character '
 
209
                        'in an author identity')
 
210
            revprops['authors'] = author
 
211
        return revprops
 
212
 
177
213
    def commit(self,
178
214
               message=None,
179
215
               timestamp=None,
192
228
               message_callback=None,
193
229
               recursive='down',
194
230
               exclude=None,
195
 
               possible_master_transports=None):
 
231
               possible_master_transports=None,
 
232
               lossy=False):
196
233
        """Commit working copy as a new revision.
197
234
 
198
235
        :param message: the commit message (it or message_callback is required)
225
262
        :param exclude: None or a list of relative paths to exclude from the
226
263
            commit. Pending changes to excluded files will be ignored by the
227
264
            commit.
 
265
        :param lossy: When committing to a foreign VCS, ignore any
 
266
            data that can not be natively represented.
228
267
        """
229
268
        operation = OperationWithCleanups(self._commit)
230
269
        self.revprops = revprops or {}
246
285
               message_callback=message_callback,
247
286
               recursive=recursive,
248
287
               exclude=exclude,
249
 
               possible_master_transports=possible_master_transports)
 
288
               possible_master_transports=possible_master_transports,
 
289
               lossy=lossy)
250
290
 
251
291
    def _commit(self, operation, message, timestamp, timezone, committer,
252
292
            specific_files, rev_id, allow_pointless, strict, verbose,
253
293
            working_tree, local, reporter, message_callback, recursive,
254
 
            exclude, possible_master_transports):
 
294
            exclude, possible_master_transports, lossy):
255
295
        mutter('preparing to commit')
256
296
 
257
297
        if working_tree is None:
289
329
                minimum_path_selection(specific_files))
290
330
        else:
291
331
            self.specific_files = None
292
 
            
 
332
 
293
333
        self.allow_pointless = allow_pointless
294
334
        self.message_callback = message_callback
295
335
        self.timestamp = timestamp
309
349
            not self.branch.repository._format.supports_tree_reference and
310
350
            (self.branch.repository._format.fast_deltas or
311
351
             len(self.parents) < 2))
312
 
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
352
        self.pb = ui.ui_factory.nested_progress_bar()
313
353
        operation.add_cleanup(self.pb.finished)
314
354
        self.basis_revid = self.work_tree.last_revision()
315
355
        self.basis_tree = self.work_tree.basis_tree()
323
363
        self._check_bound_branch(operation, possible_master_transports)
324
364
 
325
365
        # Check that the working tree is up to date
326
 
        old_revno, new_revno = self._check_out_of_date_tree()
 
366
        old_revno, old_revid, new_revno = self._check_out_of_date_tree()
327
367
 
328
368
        # Complete configuration setup
329
369
        if reporter is not None:
343
383
        self.pb_stage_count = 0
344
384
        self.pb_stage_total = 5
345
385
        if self.bound_branch:
346
 
            self.pb_stage_total += 1
 
386
            # 2 extra stages: "Uploading data to master branch" and "Merging
 
387
            # tags to master branch"
 
388
            self.pb_stage_total += 2
347
389
        self.pb.show_pct = False
348
390
        self.pb.show_spinner = False
349
391
        self.pb.show_eta = False
361
403
 
362
404
        # Collect the changes
363
405
        self._set_progress_stage("Collecting changes", counter=True)
 
406
        self._lossy = lossy
364
407
        self.builder = self.branch.get_commit_builder(self.parents,
365
 
            self.config, timestamp, timezone, committer, self.revprops, rev_id)
 
408
            self.config, timestamp, timezone, committer, self.revprops,
 
409
            rev_id, lossy=lossy)
 
410
        if not self.builder.supports_record_entry_contents and self.exclude:
 
411
            self.builder.abort()
 
412
            raise errors.ExcludesUnsupported(self.branch.repository)
 
413
 
 
414
        if self.builder.updates_branch and self.bound_branch:
 
415
            self.builder.abort()
 
416
            raise AssertionError(
 
417
                "bound branches not supported for commit builders "
 
418
                "that update the branch")
366
419
 
367
420
        try:
368
421
            self.builder.will_record_deletes()
395
448
        except Exception, e:
396
449
            mutter("aborting commit write group because of exception:")
397
450
            trace.log_exception_quietly()
398
 
            note("aborting commit write group: %r" % (e,))
399
451
            self.builder.abort()
400
452
            raise
401
453
 
402
 
        self._process_pre_hooks(old_revno, new_revno)
403
 
 
404
 
        # Upload revision data to the master.
405
 
        # this will propagate merged revisions too if needed.
406
 
        if self.bound_branch:
407
 
            self._set_progress_stage("Uploading data to master branch")
408
 
            # 'commit' to the master first so a timeout here causes the
409
 
            # local branch to be out of date
410
 
            self.master_branch.import_last_revision_info(
411
 
                self.branch.repository, new_revno, self.rev_id)
412
 
 
413
 
        # and now do the commit locally.
414
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
 
454
        self._update_branches(old_revno, old_revid, new_revno)
415
455
 
416
456
        # Make the working tree be up to date with the branch. This
417
457
        # includes automatic changes scheduled to be made to the tree, such
424
464
        self._process_post_hooks(old_revno, new_revno)
425
465
        return self.rev_id
426
466
 
 
467
    def _update_branches(self, old_revno, old_revid, new_revno):
 
468
        """Update the master and local branch to the new revision.
 
469
 
 
470
        This will try to make sure that the master branch is updated
 
471
        before the local branch.
 
472
 
 
473
        :param old_revno: Revision number of master branch before the
 
474
            commit
 
475
        :param old_revid: Tip of master branch before the commit
 
476
        :param new_revno: Revision number of the new commit
 
477
        """
 
478
        if not self.builder.updates_branch:
 
479
            self._process_pre_hooks(old_revno, new_revno)
 
480
 
 
481
            # Upload revision data to the master.
 
482
            # this will propagate merged revisions too if needed.
 
483
            if self.bound_branch:
 
484
                self._set_progress_stage("Uploading data to master branch")
 
485
                # 'commit' to the master first so a timeout here causes the
 
486
                # local branch to be out of date
 
487
                (new_revno, self.rev_id) = self.master_branch.import_last_revision_info_and_tags(
 
488
                    self.branch, new_revno, self.rev_id, lossy=self._lossy)
 
489
                if self._lossy:
 
490
                    self.branch.fetch(self.master_branch, self.rev_id)
 
491
 
 
492
            # and now do the commit locally.
 
493
            self.branch.set_last_revision_info(new_revno, self.rev_id)
 
494
        else:
 
495
            try:
 
496
                self._process_pre_hooks(old_revno, new_revno)
 
497
            except:
 
498
                # The commit builder will already have updated the branch,
 
499
                # revert it.
 
500
                self.branch.set_last_revision_info(old_revno, old_revid)
 
501
                raise
 
502
 
 
503
        # Merge local tags to remote
 
504
        if self.bound_branch:
 
505
            self._set_progress_stage("Merging tags to master branch")
 
506
            tag_updates, tag_conflicts = self.branch.tags.merge_to(
 
507
                self.master_branch.tags)
 
508
            if tag_conflicts:
 
509
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
 
510
                note( gettext("Conflicting tags in bound branch:\n{0}".format(
 
511
                    "\n".join(warning_lines))) )
 
512
 
427
513
    def _select_reporter(self):
428
514
        """Select the CommitReporter to use."""
429
515
        if is_quiet():
436
522
        # A merge with no effect on files
437
523
        if len(self.parents) > 1:
438
524
            return
439
 
        # TODO: we could simplify this by using self.builder.basis_delta.
440
 
 
441
 
        # The initial commit adds a root directory, but this in itself is not
442
 
        # a worthwhile commit.
443
 
        if (self.basis_revid == revision.NULL_REVISION and
444
 
            ((self.builder.new_inventory is not None and
445
 
             len(self.builder.new_inventory) == 1) or
446
 
            len(self.builder._basis_delta) == 1)):
447
 
            raise PointlessCommit()
448
525
        if self.builder.any_changes():
449
526
            return
450
527
        raise PointlessCommit()
495
572
    def _check_out_of_date_tree(self):
496
573
        """Check that the working tree is up to date.
497
574
 
498
 
        :return: old_revision_number,new_revision_number tuple
 
575
        :return: old_revision_number, old_revision_id, new_revision_number
 
576
            tuple
499
577
        """
500
578
        try:
501
579
            first_tree_parent = self.work_tree.get_parent_ids()[0]
514
592
        else:
515
593
            # ghost parents never appear in revision history.
516
594
            new_revno = 1
517
 
        return old_revno,new_revno
 
595
        return old_revno, master_last, new_revno
518
596
 
519
597
    def _process_pre_hooks(self, old_revno, new_revno):
520
598
        """Process any registered pre commit hooks."""
646
724
                # Reset the new path (None) and new versioned flag (False)
647
725
                change = (change[0], (change[1][0], None), change[2],
648
726
                    (change[3][0], False)) + change[4:]
 
727
                new_path = change[1][1]
 
728
                versioned = False
649
729
            elif kind == 'tree-reference':
650
730
                if self.recursive == 'down':
651
731
                    self._commit_nested_tree(change[0], change[1][1])
655
735
                    if new_path is None:
656
736
                        reporter.deleted(old_path)
657
737
                    elif old_path is None:
658
 
                        reporter.snapshot_change('added', new_path)
 
738
                        reporter.snapshot_change(gettext('added'), new_path)
659
739
                    elif old_path != new_path:
660
 
                        reporter.renamed('renamed', old_path, new_path)
 
740
                        reporter.renamed(gettext('renamed'), old_path, new_path)
661
741
                    else:
662
742
                        if (new_path or 
663
743
                            self.work_tree.branch.repository._format.rich_root_data):
664
744
                            # Don't report on changes to '' in non rich root
665
745
                            # repositories.
666
 
                            reporter.snapshot_change('modified', new_path)
 
746
                            reporter.snapshot_change(gettext('modified'), new_path)
667
747
            self._next_progress_entry()
668
748
        # Unversion IDs that were found to be deleted
669
749
        self.deleted_ids = deleted_ids
675
755
        if self.specific_files or self.exclude:
676
756
            specific_files = self.specific_files or []
677
757
            for path, old_ie in self.basis_inv.iter_entries():
678
 
                if old_ie.file_id in self.builder.new_inventory:
 
758
                if self.builder.new_inventory.has_id(old_ie.file_id):
679
759
                    # already added - skip.
680
760
                    continue
681
761
                if (is_inside_any(specific_files, path)
892
972
            self.reporter.renamed(change, old_path, path)
893
973
            self._next_progress_entry()
894
974
        else:
895
 
            if change == 'unchanged':
 
975
            if change == gettext('unchanged'):
896
976
                return
897
977
            self.reporter.snapshot_change(change, path)
898
978
            self._next_progress_entry()
914
994
 
915
995
    def _emit_progress(self):
916
996
        if self.pb_entries_count is not None:
917
 
            text = "%s [%d] - Stage" % (self.pb_stage_name,
 
997
            text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
918
998
                self.pb_entries_count)
919
999
        else:
920
 
            text = "%s - Stage" % (self.pb_stage_name, )
 
1000
            text = gettext("%s - Stage") % (self.pb_stage_name, )
921
1001
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
922
1002
 
923
1003
    def _set_specific_file_ids(self):