~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Vincent Ladeuil
  • Date: 2016-02-07 18:23:13 UTC
  • mto: (6615.3.1 2.7)
  • mto: This revision was merged to the branch mainline in revision 6620.
  • Revision ID: v.ladeuil+lp@free.fr-20160207182313-jwz7z3vj4mpyjn7y
Ensure http://pad.lv/1323805 won't come back.

Since the 2.6.0 release pypi policy changed and release tarballs can't be
hosted on launchpad anymore, they have to be uploaded to
http://pypi.python.org/pypi


This fixes setup.py sdist to generate the right tarball with nearly the same
content as the one produced for 2.7.0.

Such a tarball have been uploaded to pypi properly signed and tested for
installation in venv.

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
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
49
50
# TODO: Change the parameter 'rev_id' to 'revision_id' to be consistent with
50
51
# the rest of the code; add a deprecation of the old name.
51
52
 
52
 
import os
53
 
import re
54
 
import sys
55
 
import time
56
 
 
57
 
from cStringIO import StringIO
58
 
 
59
53
from bzrlib import (
60
54
    debug,
61
55
    errors,
62
 
    revision,
63
56
    trace,
64
57
    tree,
65
 
    xml_serializer,
 
58
    ui,
66
59
    )
67
60
from bzrlib.branch import Branch
68
61
from bzrlib.cleanup import OperationWithCleanups
72
65
                           StrictCommitFailed
73
66
                           )
74
67
from bzrlib.osutils import (get_user_encoding,
75
 
                            kind_marker, isdir,isfile, is_inside_any,
76
 
                            is_inside_or_parent_of_any,
 
68
                            is_inside_any,
77
69
                            minimum_path_selection,
78
 
                            quotefn, sha_file, split_lines,
79
70
                            splitpath,
80
71
                            )
81
 
from bzrlib.testament import Testament
82
 
from bzrlib.trace import mutter, note, warning, is_quiet
 
72
from bzrlib.trace import mutter, note, is_quiet
83
73
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
84
74
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
75
from bzrlib.urlutils import unescape_for_display
90
 
import bzrlib.ui
91
 
 
 
76
from bzrlib.i18n import gettext
92
77
 
93
78
class NullCommitReporter(object):
94
79
    """I report on progress of a commit."""
129
114
        note(format, *args)
130
115
 
131
116
    def snapshot_change(self, change, path):
132
 
        if path == '' and change in ('added', 'modified'):
 
117
        if path == '' and change in (gettext('added'), gettext('modified')):
133
118
            return
134
119
        self._note("%s %s", change, path)
135
120
 
143
128
                                   "to started.", DeprecationWarning,
144
129
                                   stacklevel=2)
145
130
            location = ''
146
 
        self._note('Committing%s', location)
 
131
        self._note(gettext('Committing%s'), location)
147
132
 
148
133
    def completed(self, revno, rev_id):
149
 
        self._note('Committed revision %d.', revno)
 
134
        self._note(gettext('Committed revision %d.'), revno)
 
135
        # self._note goes to the console too; so while we want to log the
 
136
        # rev_id, we can't trivially only log it. (See bug 526425). Long
 
137
        # term we should rearrange the reporting structure, but for now
 
138
        # we just mutter seperately. We mutter the revid and revno together
 
139
        # so that concurrent bzr invocations won't lead to confusion.
 
140
        mutter('Committed revid %s as revno %d.', rev_id, revno)
150
141
 
151
142
    def deleted(self, path):
152
 
        self._note('deleted %s', path)
 
143
        self._note(gettext('deleted %s'), path)
153
144
 
154
145
    def missing(self, path):
155
 
        self._note('missing %s', path)
 
146
        self._note(gettext('missing %s'), path)
156
147
 
157
148
    def renamed(self, change, old_path, new_path):
158
149
        self._note('%s %s => %s', change, old_path, new_path)
175
166
    """
176
167
    def __init__(self,
177
168
                 reporter=None,
178
 
                 config=None):
 
169
                 config_stack=None):
179
170
        """Create a Commit object.
180
171
 
181
172
        :param reporter: the default reporter to use or None to decide later
182
173
        """
183
174
        self.reporter = reporter
184
 
        self.config = config
 
175
        self.config_stack = config_stack
 
176
 
 
177
    @staticmethod
 
178
    def update_revprops(revprops, branch, authors=None, author=None,
 
179
                        local=False, possible_master_transports=None):
 
180
        if revprops is None:
 
181
            revprops = {}
 
182
        if possible_master_transports is None:
 
183
            possible_master_transports = []
 
184
        if not 'branch-nick' in revprops:
 
185
            revprops['branch-nick'] = branch._get_nick(
 
186
                local,
 
187
                possible_master_transports)
 
188
        if authors is not None:
 
189
            if author is not None:
 
190
                raise AssertionError('Specifying both author and authors '
 
191
                        'is not allowed. Specify just authors instead')
 
192
            if 'author' in revprops or 'authors' in revprops:
 
193
                # XXX: maybe we should just accept one of them?
 
194
                raise AssertionError('author property given twice')
 
195
            if authors:
 
196
                for individual in authors:
 
197
                    if '\n' in individual:
 
198
                        raise AssertionError('\\n is not a valid character '
 
199
                                'in an author identity')
 
200
                revprops['authors'] = '\n'.join(authors)
 
201
        if author is not None:
 
202
            symbol_versioning.warn('The parameter author was deprecated'
 
203
                   ' in version 1.13. Use authors instead',
 
204
                   DeprecationWarning)
 
205
            if 'author' in revprops or 'authors' in revprops:
 
206
                # XXX: maybe we should just accept one of them?
 
207
                raise AssertionError('author property given twice')
 
208
            if '\n' in author:
 
209
                raise AssertionError('\\n is not a valid character '
 
210
                        'in an author identity')
 
211
            revprops['authors'] = author
 
212
        return revprops
185
213
 
186
214
    def commit(self,
187
215
               message=None,
201
229
               message_callback=None,
202
230
               recursive='down',
203
231
               exclude=None,
204
 
               possible_master_transports=None):
 
232
               possible_master_transports=None,
 
233
               lossy=False):
205
234
        """Commit working copy as a new revision.
206
235
 
207
236
        :param message: the commit message (it or message_callback is required)
234
263
        :param exclude: None or a list of relative paths to exclude from the
235
264
            commit. Pending changes to excluded files will be ignored by the
236
265
            commit.
 
266
        :param lossy: When committing to a foreign VCS, ignore any
 
267
            data that can not be natively represented.
237
268
        """
238
269
        operation = OperationWithCleanups(self._commit)
 
270
        self.revprops = revprops or {}
 
271
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
 
272
        self.config_stack = config or self.config_stack
239
273
        return operation.run(
240
274
               message=message,
241
275
               timestamp=timestamp,
246
280
               allow_pointless=allow_pointless,
247
281
               strict=strict,
248
282
               verbose=verbose,
249
 
               revprops=revprops,
250
283
               working_tree=working_tree,
251
284
               local=local,
252
285
               reporter=reporter,
253
 
               config=config,
254
286
               message_callback=message_callback,
255
287
               recursive=recursive,
256
288
               exclude=exclude,
257
 
               possible_master_transports=possible_master_transports)
 
289
               possible_master_transports=possible_master_transports,
 
290
               lossy=lossy)
258
291
 
259
292
    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):
 
293
            specific_files, rev_id, allow_pointless, strict, verbose,
 
294
            working_tree, local, reporter, message_callback, recursive,
 
295
            exclude, possible_master_transports, lossy):
263
296
        mutter('preparing to commit')
264
297
 
265
298
        if working_tree is None:
297
330
                minimum_path_selection(specific_files))
298
331
        else:
299
332
            self.specific_files = None
300
 
            
 
333
 
301
334
        self.allow_pointless = allow_pointless
302
 
        self.revprops = revprops
303
335
        self.message_callback = message_callback
304
336
        self.timestamp = timestamp
305
337
        self.timezone = timezone
318
350
            not self.branch.repository._format.supports_tree_reference and
319
351
            (self.branch.repository._format.fast_deltas or
320
352
             len(self.parents) < 2))
321
 
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
353
        self.pb = ui.ui_factory.nested_progress_bar()
322
354
        operation.add_cleanup(self.pb.finished)
323
355
        self.basis_revid = self.work_tree.last_revision()
324
356
        self.basis_tree = self.work_tree.basis_tree()
332
364
        self._check_bound_branch(operation, possible_master_transports)
333
365
 
334
366
        # Check that the working tree is up to date
335
 
        old_revno, new_revno = self._check_out_of_date_tree()
 
367
        old_revno, old_revid, new_revno = self._check_out_of_date_tree()
336
368
 
337
369
        # Complete configuration setup
338
370
        if reporter is not None:
339
371
            self.reporter = reporter
340
372
        elif self.reporter is None:
341
373
            self.reporter = self._select_reporter()
342
 
        if self.config is None:
343
 
            self.config = self.branch.get_config()
 
374
        if self.config_stack is None:
 
375
            self.config_stack = self.work_tree.get_config_stack()
344
376
 
345
377
        self._set_specific_file_ids()
346
378
 
352
384
        self.pb_stage_count = 0
353
385
        self.pb_stage_total = 5
354
386
        if self.bound_branch:
355
 
            self.pb_stage_total += 1
 
387
            # 2 extra stages: "Uploading data to master branch" and "Merging
 
388
            # tags to master branch"
 
389
            self.pb_stage_total += 2
356
390
        self.pb.show_pct = False
357
391
        self.pb.show_spinner = False
358
392
        self.pb.show_eta = False
370
404
 
371
405
        # Collect the changes
372
406
        self._set_progress_stage("Collecting changes", counter=True)
 
407
        self._lossy = lossy
373
408
        self.builder = self.branch.get_commit_builder(self.parents,
374
 
            self.config, timestamp, timezone, committer, revprops, rev_id)
 
409
            self.config_stack, timestamp, timezone, committer, self.revprops,
 
410
            rev_id, lossy=lossy)
 
411
        if not self.builder.supports_record_entry_contents and self.exclude:
 
412
            self.builder.abort()
 
413
            raise errors.ExcludesUnsupported(self.branch.repository)
 
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")
375
420
 
376
421
        try:
377
422
            self.builder.will_record_deletes()
404
449
        except Exception, e:
405
450
            mutter("aborting commit write group because of exception:")
406
451
            trace.log_exception_quietly()
407
 
            note("aborting commit write group: %r" % (e,))
408
452
            self.builder.abort()
409
453
            raise
410
454
 
411
 
        self._process_pre_hooks(old_revno, new_revno)
412
 
 
413
 
        # Upload revision data to the master.
414
 
        # this will propagate merged revisions too if needed.
415
 
        if self.bound_branch:
416
 
            self._set_progress_stage("Uploading data to master branch")
417
 
            # 'commit' to the master first so a timeout here causes the
418
 
            # local branch to be out of date
419
 
            self.master_branch.import_last_revision_info(
420
 
                self.branch.repository, new_revno, self.rev_id)
421
 
 
422
 
        # and now do the commit locally.
423
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
 
455
        self._update_branches(old_revno, old_revid, new_revno)
424
456
 
425
457
        # Make the working tree be up to date with the branch. This
426
458
        # includes automatic changes scheduled to be made to the tree, such
433
465
        self._process_post_hooks(old_revno, new_revno)
434
466
        return self.rev_id
435
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
 
436
514
    def _select_reporter(self):
437
515
        """Select the CommitReporter to use."""
438
516
        if is_quiet():
445
523
        # A merge with no effect on files
446
524
        if len(self.parents) > 1:
447
525
            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
526
        if self.builder.any_changes():
458
527
            return
459
528
        raise PointlessCommit()
504
573
    def _check_out_of_date_tree(self):
505
574
        """Check that the working tree is up to date.
506
575
 
507
 
        :return: old_revision_number,new_revision_number tuple
 
576
        :return: old_revision_number, old_revision_id, new_revision_number
 
577
            tuple
508
578
        """
509
579
        try:
510
580
            first_tree_parent = self.work_tree.get_parent_ids()[0]
523
593
        else:
524
594
            # ghost parents never appear in revision history.
525
595
            new_revno = 1
526
 
        return old_revno,new_revno
 
596
        return old_revno, master_last, new_revno
527
597
 
528
598
    def _process_pre_hooks(self, old_revno, new_revno):
529
599
        """Process any registered pre commit hooks."""
535
605
        # Process the post commit hooks, if any
536
606
        self._set_progress_stage("Running post_commit hooks")
537
607
        # old style commit hooks - should be deprecated ? (obsoleted in
538
 
        # 0.15)
539
 
        if self.config.post_commit() is not None:
540
 
            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(' ')
541
612
            # this would be nicer with twisted.python.reflect.namedAny
542
613
            for hook in hooks:
543
614
                result = eval(hook + '(branch, rev_id)',
596
667
        # entries and the order is preserved when doing this.
597
668
        if self.use_record_iter_changes:
598
669
            return
599
 
        self.basis_inv = self.basis_tree.inventory
 
670
        self.basis_inv = self.basis_tree.root_inventory
600
671
        self.parent_invs = [self.basis_inv]
601
672
        for revision in self.parents[1:]:
602
673
            if self.branch.repository.has_revision(revision):
655
726
                # Reset the new path (None) and new versioned flag (False)
656
727
                change = (change[0], (change[1][0], None), change[2],
657
728
                    (change[3][0], False)) + change[4:]
 
729
                new_path = change[1][1]
 
730
                versioned = False
658
731
            elif kind == 'tree-reference':
659
732
                if self.recursive == 'down':
660
733
                    self._commit_nested_tree(change[0], change[1][1])
664
737
                    if new_path is None:
665
738
                        reporter.deleted(old_path)
666
739
                    elif old_path is None:
667
 
                        reporter.snapshot_change('added', new_path)
 
740
                        reporter.snapshot_change(gettext('added'), new_path)
668
741
                    elif old_path != new_path:
669
 
                        reporter.renamed('renamed', old_path, new_path)
 
742
                        reporter.renamed(gettext('renamed'), old_path, new_path)
670
743
                    else:
671
744
                        if (new_path or 
672
745
                            self.work_tree.branch.repository._format.rich_root_data):
673
746
                            # Don't report on changes to '' in non rich root
674
747
                            # repositories.
675
 
                            reporter.snapshot_change('modified', new_path)
 
748
                            reporter.snapshot_change(gettext('modified'), new_path)
676
749
            self._next_progress_entry()
677
750
        # Unversion IDs that were found to be deleted
678
751
        self.deleted_ids = deleted_ids
684
757
        if self.specific_files or self.exclude:
685
758
            specific_files = self.specific_files or []
686
759
            for path, old_ie in self.basis_inv.iter_entries():
687
 
                if old_ie.file_id in self.builder.new_inventory:
 
760
                if self.builder.new_inventory.has_id(old_ie.file_id):
688
761
                    # already added - skip.
689
762
                    continue
690
763
                if (is_inside_any(specific_files, path)
761
834
        deleted_paths = {}
762
835
        # XXX: Note that entries may have the wrong kind because the entry does
763
836
        # not reflect the status on disk.
764
 
        work_inv = self.work_tree.inventory
765
837
        # NB: entries will include entries within the excluded ids/paths
766
838
        # because iter_entries_by_dir has no 'exclude' facility today.
767
 
        entries = work_inv.iter_entries_by_dir(
 
839
        entries = self.work_tree.iter_entries_by_dir(
768
840
            specific_file_ids=self.specific_file_ids, yield_parents=True)
769
841
        for path, existing_ie in entries:
770
842
            file_id = existing_ie.file_id
901
973
            self.reporter.renamed(change, old_path, path)
902
974
            self._next_progress_entry()
903
975
        else:
904
 
            if change == 'unchanged':
 
976
            if change == gettext('unchanged'):
905
977
                return
906
978
            self.reporter.snapshot_change(change, path)
907
979
            self._next_progress_entry()
923
995
 
924
996
    def _emit_progress(self):
925
997
        if self.pb_entries_count is not None:
926
 
            text = "%s [%d] - Stage" % (self.pb_stage_name,
 
998
            text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
927
999
                self.pb_entries_count)
928
1000
        else:
929
 
            text = "%s - Stage" % (self.pb_stage_name, )
 
1001
            text = gettext("%s - Stage") % (self.pb_stage_name, )
930
1002
        self.pb.update(text, self.pb_stage_count, self.pb_stage_total)
931
1003
 
932
1004
    def _set_specific_file_ids(self):