~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 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
49
49
# TODO: Change the parameter 'rev_id' to 'revision_id' to be consistent with
50
50
# the rest of the code; add a deprecation of the old name.
51
51
 
52
 
import os
53
 
import re
54
 
import sys
55
 
import time
56
 
 
57
 
from cStringIO import StringIO
58
 
 
59
52
from bzrlib import (
60
53
    debug,
61
54
    errors,
62
 
    revision,
63
55
    trace,
64
56
    tree,
65
 
    xml_serializer,
 
57
    ui,
66
58
    )
67
59
from bzrlib.branch import Branch
68
60
from bzrlib.cleanup import OperationWithCleanups
72
64
                           StrictCommitFailed
73
65
                           )
74
66
from bzrlib.osutils import (get_user_encoding,
75
 
                            kind_marker, isdir,isfile, is_inside_any,
76
 
                            is_inside_or_parent_of_any,
 
67
                            is_inside_any,
77
68
                            minimum_path_selection,
78
 
                            quotefn, sha_file, split_lines,
79
69
                            splitpath,
80
70
                            )
81
 
from bzrlib.testament import Testament
82
 
from bzrlib.trace import mutter, note, warning, is_quiet
 
71
from bzrlib.trace import mutter, note, is_quiet
83
72
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
84
73
from bzrlib import symbol_versioning
85
 
from bzrlib.symbol_versioning import (deprecated_passed,
86
 
        deprecated_function,
87
 
        DEPRECATED_PARAMETER)
88
 
from bzrlib.workingtree import WorkingTree
89
74
from bzrlib.urlutils import unescape_for_display
90
 
import bzrlib.ui
91
 
 
 
75
from bzrlib.i18n import gettext
92
76
 
93
77
class NullCommitReporter(object):
94
78
    """I report on progress of a commit."""
129
113
        note(format, *args)
130
114
 
131
115
    def snapshot_change(self, change, path):
132
 
        if path == '' and change in ('added', 'modified'):
 
116
        if path == '' and change in (gettext('added'), gettext('modified')):
133
117
            return
134
118
        self._note("%s %s", change, path)
135
119
 
143
127
                                   "to started.", DeprecationWarning,
144
128
                                   stacklevel=2)
145
129
            location = ''
146
 
        self._note('Committing%s', location)
 
130
        self._note(gettext('Committing%s'), location)
147
131
 
148
132
    def completed(self, revno, rev_id):
149
 
        self._note('Committed revision %d.', revno)
 
133
        self._note(gettext('Committed revision %d.'), revno)
 
134
        # self._note goes to the console too; so while we want to log the
 
135
        # rev_id, we can't trivially only log it. (See bug 526425). Long
 
136
        # term we should rearrange the reporting structure, but for now
 
137
        # we just mutter seperately. We mutter the revid and revno together
 
138
        # so that concurrent bzr invocations won't lead to confusion.
 
139
        mutter('Committed revid %s as revno %d.', rev_id, revno)
150
140
 
151
141
    def deleted(self, path):
152
 
        self._note('deleted %s', path)
 
142
        self._note(gettext('deleted %s'), path)
153
143
 
154
144
    def missing(self, path):
155
 
        self._note('missing %s', path)
 
145
        self._note(gettext('missing %s'), path)
156
146
 
157
147
    def renamed(self, change, old_path, new_path):
158
148
        self._note('%s %s => %s', change, old_path, new_path)
183
173
        self.reporter = reporter
184
174
        self.config = config
185
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
 
186
213
    def commit(self,
187
214
               message=None,
188
215
               timestamp=None,
201
228
               message_callback=None,
202
229
               recursive='down',
203
230
               exclude=None,
204
 
               possible_master_transports=None):
 
231
               possible_master_transports=None,
 
232
               lossy=False):
205
233
        """Commit working copy as a new revision.
206
234
 
207
235
        :param message: the commit message (it or message_callback is required)
234
262
        :param exclude: None or a list of relative paths to exclude from the
235
263
            commit. Pending changes to excluded files will be ignored by the
236
264
            commit.
 
265
        :param lossy: When committing to a foreign VCS, ignore any
 
266
            data that can not be natively represented.
237
267
        """
238
268
        operation = OperationWithCleanups(self._commit)
 
269
        self.revprops = revprops or {}
 
270
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
 
271
        self.config = config or self.config
239
272
        return operation.run(
240
273
               message=message,
241
274
               timestamp=timestamp,
246
279
               allow_pointless=allow_pointless,
247
280
               strict=strict,
248
281
               verbose=verbose,
249
 
               revprops=revprops,
250
282
               working_tree=working_tree,
251
283
               local=local,
252
284
               reporter=reporter,
253
 
               config=config,
254
285
               message_callback=message_callback,
255
286
               recursive=recursive,
256
287
               exclude=exclude,
257
 
               possible_master_transports=possible_master_transports)
 
288
               possible_master_transports=possible_master_transports,
 
289
               lossy=lossy)
258
290
 
259
291
    def _commit(self, operation, message, timestamp, timezone, committer,
260
 
            specific_files, rev_id, allow_pointless, strict, verbose, revprops,
261
 
            working_tree, local, reporter, config, message_callback, recursive,
262
 
            exclude, possible_master_transports):
 
292
            specific_files, rev_id, allow_pointless, strict, verbose,
 
293
            working_tree, local, reporter, message_callback, recursive,
 
294
            exclude, possible_master_transports, lossy):
263
295
        mutter('preparing to commit')
264
296
 
265
297
        if working_tree is None:
297
329
                minimum_path_selection(specific_files))
298
330
        else:
299
331
            self.specific_files = None
300
 
            
 
332
 
301
333
        self.allow_pointless = allow_pointless
302
 
        self.revprops = revprops
303
334
        self.message_callback = message_callback
304
335
        self.timestamp = timestamp
305
336
        self.timezone = timezone
318
349
            not self.branch.repository._format.supports_tree_reference and
319
350
            (self.branch.repository._format.fast_deltas or
320
351
             len(self.parents) < 2))
321
 
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
352
        self.pb = ui.ui_factory.nested_progress_bar()
322
353
        operation.add_cleanup(self.pb.finished)
323
354
        self.basis_revid = self.work_tree.last_revision()
324
355
        self.basis_tree = self.work_tree.basis_tree()
352
383
        self.pb_stage_count = 0
353
384
        self.pb_stage_total = 5
354
385
        if self.bound_branch:
355
 
            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
356
389
        self.pb.show_pct = False
357
390
        self.pb.show_spinner = False
358
391
        self.pb.show_eta = False
370
403
 
371
404
        # Collect the changes
372
405
        self._set_progress_stage("Collecting changes", counter=True)
 
406
        self._lossy = lossy
373
407
        self.builder = self.branch.get_commit_builder(self.parents,
374
 
            self.config, timestamp, timezone, committer, 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)
375
413
 
376
414
        try:
377
415
            self.builder.will_record_deletes()
404
442
        except Exception, e:
405
443
            mutter("aborting commit write group because of exception:")
406
444
            trace.log_exception_quietly()
407
 
            note("aborting commit write group: %r" % (e,))
408
445
            self.builder.abort()
409
446
            raise
410
447
 
416
453
            self._set_progress_stage("Uploading data to master branch")
417
454
            # 'commit' to the master first so a timeout here causes the
418
455
            # local branch to be out of date
419
 
            self.master_branch.import_last_revision_info(
420
 
                self.branch.repository, new_revno, self.rev_id)
 
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)
421
460
 
422
461
        # and now do the commit locally.
423
462
        self.branch.set_last_revision_info(new_revno, self.rev_id)
424
463
 
 
464
        # Merge local tags to remote
 
465
        if self.bound_branch:
 
466
            self._set_progress_stage("Merging tags to master branch")
 
467
            tag_updates, tag_conflicts = self.branch.tags.merge_to(
 
468
                self.master_branch.tags)
 
469
            if tag_conflicts:
 
470
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
 
471
                note( gettext("Conflicting tags in bound branch:\n{0}".format(
 
472
                    "\n".join(warning_lines))) )
 
473
 
425
474
        # Make the working tree be up to date with the branch. This
426
475
        # includes automatic changes scheduled to be made to the tree, such
427
476
        # as updating its basis and unversioning paths that were missing.
445
494
        # A merge with no effect on files
446
495
        if len(self.parents) > 1:
447
496
            return
448
 
        # TODO: we could simplify this by using self.builder.basis_delta.
449
 
 
450
 
        # The initial commit adds a root directory, but this in itself is not
451
 
        # a worthwhile commit.
452
 
        if (self.basis_revid == revision.NULL_REVISION and
453
 
            ((self.builder.new_inventory is not None and
454
 
             len(self.builder.new_inventory) == 1) or
455
 
            len(self.builder._basis_delta) == 1)):
456
 
            raise PointlessCommit()
457
497
        if self.builder.any_changes():
458
498
            return
459
499
        raise PointlessCommit()
655
695
                # Reset the new path (None) and new versioned flag (False)
656
696
                change = (change[0], (change[1][0], None), change[2],
657
697
                    (change[3][0], False)) + change[4:]
 
698
                new_path = change[1][1]
 
699
                versioned = False
658
700
            elif kind == 'tree-reference':
659
701
                if self.recursive == 'down':
660
702
                    self._commit_nested_tree(change[0], change[1][1])
664
706
                    if new_path is None:
665
707
                        reporter.deleted(old_path)
666
708
                    elif old_path is None:
667
 
                        reporter.snapshot_change('added', new_path)
 
709
                        reporter.snapshot_change(gettext('added'), new_path)
668
710
                    elif old_path != new_path:
669
 
                        reporter.renamed('renamed', old_path, new_path)
 
711
                        reporter.renamed(gettext('renamed'), old_path, new_path)
670
712
                    else:
671
713
                        if (new_path or 
672
714
                            self.work_tree.branch.repository._format.rich_root_data):
673
715
                            # Don't report on changes to '' in non rich root
674
716
                            # repositories.
675
 
                            reporter.snapshot_change('modified', new_path)
 
717
                            reporter.snapshot_change(gettext('modified'), new_path)
676
718
            self._next_progress_entry()
677
719
        # Unversion IDs that were found to be deleted
678
720
        self.deleted_ids = deleted_ids
684
726
        if self.specific_files or self.exclude:
685
727
            specific_files = self.specific_files or []
686
728
            for path, old_ie in self.basis_inv.iter_entries():
687
 
                if old_ie.file_id in self.builder.new_inventory:
 
729
                if self.builder.new_inventory.has_id(old_ie.file_id):
688
730
                    # already added - skip.
689
731
                    continue
690
732
                if (is_inside_any(specific_files, path)
901
943
            self.reporter.renamed(change, old_path, path)
902
944
            self._next_progress_entry()
903
945
        else:
904
 
            if change == 'unchanged':
 
946
            if change == gettext('unchanged'):
905
947
                return
906
948
            self.reporter.snapshot_change(change, path)
907
949
            self._next_progress_entry()
923
965
 
924
966
    def _emit_progress(self):
925
967
        if self.pb_entries_count is not None:
926
 
            text = "%s [%d] - Stage" % (self.pb_stage_name,
 
968
            text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
927
969
                self.pb_entries_count)
928
970
        else:
929
 
            text = "%s - Stage" % (self.pb_stage_name, )
 
971
            text = gettext("%s - Stage") % (self.pb_stage_name, )
930
972
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
931
973
 
932
974
    def _set_specific_file_ids(self):