~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Vincent Ladeuil
  • Date: 2007-11-04 15:24:27 UTC
  • mto: (2961.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 2962.
  • Revision ID: v.ladeuil+lp@free.fr-20071104152427-p9k7e4toywa87wfc
Review feedback.

* doc/en/user-guide/authentication_conf.txt: 
New file. Authentication configuration file documentation.

* doc/en/user-guide/configuration.txt: 
Slight modifications, add authentication.conf reference.

* doc/en/mini-tutorial/index.txt: 
Fix make docs warning.

* doc/developers/authentication-ring.txt: 
Small cleanups noticed during
doc/en/user-guide/authentication_conf.txt redaction.

* bzrlib/transport/http/_urllib.py:
(HttpTransport_urllib._perform): Use a dict() instead of {} syntax.

* bzrlib/tests/blackbox/test_whoami.py:
(TestWhoami.test_whoami_branch): Delete BZREMAIL related tests.

* bzrlib/config.py:
(Config.username): BZREMAIL deleted, has been obsolete for more
than a year.
(AuthenticationConfig.__init__): Review feedback, since keeping a
callback as an attribute is useless, call it now and keep the
filename itself as an attribute.
(AuthenticationConfig.get_credentials): Use a dict() instead of {}
syntax.

* NEWS: 
Updated as per Martin's suggestion.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
# The newly committed revision is going to have a shape corresponding
19
 
# to that of the working tree.  Files that are not in the
 
19
# to that of the working inventory.  Files that are not in the
20
20
# working tree and that were in the predecessor are reported as
21
21
# removed --- this can include files that were either removed from the
22
22
# inventory or deleted in the working tree.  If they were only
25
25
# We then consider the remaining entries, which will be in the new
26
26
# version.  Directory entries are simply copied across.  File entries
27
27
# must be checked to see if a new version of the file should be
28
 
# recorded.  For each parent revision tree, we check to see what
 
28
# recorded.  For each parent revision inventory, we check to see what
29
29
# version of the file was present.  If the file was present in at
30
30
# least one tree, and if it was the same version in all the trees,
31
31
# then we can just refer to that version.  Otherwise, a new version
59
59
from bzrlib import (
60
60
    debug,
61
61
    errors,
62
 
    revision,
 
62
    inventory,
63
63
    tree,
64
64
    )
65
65
from bzrlib.branch import Branch
71
71
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any,
72
72
                            is_inside_or_parent_of_any,
73
73
                            minimum_path_selection,
74
 
                            quotefn, sha_file, split_lines,
75
 
                            splitpath,
76
 
                            )
 
74
                            quotefn, sha_file, split_lines)
77
75
from bzrlib.testament import Testament
78
76
from bzrlib.trace import mutter, note, warning, is_quiet
79
77
from bzrlib.xml5 import serializer_v5
80
 
from bzrlib.inventory import InventoryEntry, make_entry
 
78
from bzrlib.inventory import Inventory, InventoryEntry
81
79
from bzrlib import symbol_versioning
82
80
from bzrlib.symbol_versioning import (deprecated_passed,
83
81
        deprecated_function,
91
89
    """I report on progress of a commit."""
92
90
 
93
91
    def started(self, revno, revid, location=None):
94
 
        if location is None:
95
 
            symbol_versioning.warn("As of bzr 1.0 you must pass a location "
96
 
                                   "to started.", DeprecationWarning,
97
 
                                   stacklevel=2)
98
92
        pass
99
93
 
100
94
    def snapshot_change(self, change, path):
137
131
 
138
132
    def started(self, revno, rev_id, location=None):
139
133
        if location is not None:
140
 
            location = ' to: ' + unescape_for_display(location, 'utf-8')
 
134
            location = ' to "' + unescape_for_display(location, 'utf-8') + '"'
141
135
        else:
142
 
            # When started was added, location was only made optional by
143
 
            # accident.  Matt Nordhoff 20071129
144
 
            symbol_versioning.warn("As of bzr 1.0 you must pass a location "
145
 
                                   "to started.", DeprecationWarning,
146
 
                                   stacklevel=2)
147
136
            location = ''
148
 
        self._note('Committing%s', location)
 
137
        self._note('Committing revision %d%s.', revno, location)
149
138
 
150
139
    def completed(self, revno, rev_id):
151
140
        self._note('Committed revision %d.', revno)
204
193
               reporter=None,
205
194
               config=None,
206
195
               message_callback=None,
207
 
               recursive='down',
208
 
               exclude=None):
 
196
               recursive='down'):
209
197
        """Commit working copy as a new revision.
210
198
 
211
199
        :param message: the commit message (it or message_callback is required)
233
221
        :param verbose: if True and the reporter is not None, report everything
234
222
        :param recursive: If set to 'down', commit in any subtrees that have
235
223
            pending changes of any sort during this commit.
236
 
        :param exclude: None or a list of relative paths to exclude from the
237
 
            commit. Pending changes to excluded files will be ignored by the
238
 
            commit. 
239
224
        """
240
225
        mutter('preparing to commit')
241
226
 
259
244
        self.bound_branch = None
260
245
        self.any_entries_changed = False
261
246
        self.any_entries_deleted = False
262
 
        if exclude is not None:
263
 
            self.exclude = sorted(
264
 
                minimum_path_selection(exclude))
265
 
        else:
266
 
            self.exclude = []
267
247
        self.local = local
268
248
        self.master_branch = None
269
249
        self.master_locked = False
289
269
 
290
270
        self.work_tree.lock_write()
291
271
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
292
 
        self.basis_revid = self.work_tree.last_revision()
293
272
        self.basis_tree = self.work_tree.basis_tree()
294
273
        self.basis_tree.lock_read()
295
274
        try:
338
317
            self.pb.show_count = True
339
318
            self.pb.show_bar = True
340
319
 
 
320
            # After a merge, a selected file commit is not supported.
 
321
            # See 'bzr help merge' for an explanation as to why.
341
322
            self.basis_inv = self.basis_tree.inventory
342
323
            self._gather_parents()
343
 
            # After a merge, a selected file commit is not supported.
344
 
            # See 'bzr help merge' for an explanation as to why.
345
324
            if len(self.parents) > 1 and self.specific_files:
346
325
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
347
 
            # Excludes are a form of selected file commit.
348
 
            if len(self.parents) > 1 and self.exclude:
349
 
                raise errors.CannotCommitSelectedFileMerge(self.exclude)
350
326
 
351
327
            # Collect the changes
352
328
            self._set_progress_stage("Collecting changes",
377
353
 
378
354
                # Prompt the user for a commit message if none provided
379
355
                message = message_callback(self)
 
356
                assert isinstance(message, unicode), type(message)
380
357
                self.message = message
381
358
                self._escape_commit_message()
382
359
 
392
369
            # Upload revision data to the master.
393
370
            # this will propagate merged revisions too if needed.
394
371
            if self.bound_branch:
395
 
                if not self.master_branch.repository.has_same_location(
396
 
                        self.branch.repository):
397
 
                    self._set_progress_stage("Uploading data to master branch")
398
 
                    self.master_branch.repository.fetch(self.branch.repository,
399
 
                        revision_id=self.rev_id)
 
372
                self._set_progress_stage("Uploading data to master branch")
 
373
                self.master_branch.repository.fetch(self.branch.repository,
 
374
                                                    revision_id=self.rev_id)
400
375
                # now the master has the revision data
401
376
                # 'commit' to the master first so a timeout here causes the
402
377
                # local branch to be out of date
408
383
 
409
384
            # Make the working tree up to date with the branch
410
385
            self._set_progress_stage("Updating the working tree")
411
 
            self.work_tree.update_basis_by_delta(self.rev_id,
412
 
                 self._basis_delta)
 
386
            rev_tree = self.builder.revision_tree()
 
387
            # XXX: This will need to be changed if we support doing a
 
388
            # selective commit while a merge is still pending - then we'd
 
389
            # still have multiple parents after the commit.
 
390
            #
 
391
            # XXX: update_basis_by_delta is slower at present because it works
 
392
            # on inventories, so this is not active until there's a native
 
393
            # dirstate implementation.
 
394
            ## self.work_tree.update_basis_by_delta(self.rev_id,
 
395
            ##      self._basis_delta)
 
396
            self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
413
397
            self.reporter.completed(new_revno, self.rev_id)
414
398
            self._process_post_hooks(old_revno, new_revno)
415
399
        finally:
430
414
            return
431
415
        # TODO: we could simplify this by using self._basis_delta.
432
416
 
433
 
        # The initial commit adds a root directory, but this in itself is not
434
 
        # a worthwhile commit.
435
 
        if (self.basis_revid == revision.NULL_REVISION and
436
 
            len(self.builder.new_inventory) == 1):
 
417
        # The inital commit adds a root directory, but this in itself is not
 
418
        # a worthwhile commit.  
 
419
        if len(self.basis_inv) == 0 and len(self.builder.new_inventory) == 1:
437
420
            raise PointlessCommit()
 
421
        # Shortcut, if the number of entries changes, then we obviously have
 
422
        # a change
 
423
        if len(self.builder.new_inventory) != len(self.basis_inv):
 
424
            return
438
425
        # If length == 1, then we only have the root entry. Which means
439
426
        # that there is no real difference (only the root could be different)
440
 
        # unless deletes occured, in which case the length is irrelevant.
441
 
        if (self.any_entries_deleted or 
442
 
            (len(self.builder.new_inventory) != 1 and
443
 
             self.any_entries_changed)):
 
427
        if len(self.builder.new_inventory) != 1 and (self.any_entries_changed
 
428
            or self.any_entries_deleted):
444
429
            return
445
430
        raise PointlessCommit()
446
431
 
660
645
        # in bugs like #46635.  Any reason not to use/enhance Tree.changes_from?
661
646
        # ADHB 11-07-2006
662
647
 
663
 
        exclude = self.exclude
664
 
        specific_files = self.specific_files or []
 
648
        specific_files = self.specific_files
665
649
        mutter("Selecting files for commit with filter %s", specific_files)
666
650
 
667
651
        # Build the new inventory
668
 
        self._populate_from_inventory()
 
652
        self._populate_from_inventory(specific_files)
669
653
 
670
654
        # If specific files are selected, then all un-selected files must be
671
655
        # recorded in their previous state. For more details, see
672
656
        # https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
673
 
        if specific_files or exclude:
 
657
        if specific_files:
674
658
            for path, old_ie in self.basis_inv.iter_entries():
675
659
                if old_ie.file_id in self.builder.new_inventory:
676
660
                    # already added - skip.
677
661
                    continue
678
 
                if (is_inside_any(specific_files, path)
679
 
                    and not is_inside_any(exclude, path)):
680
 
                    # was inside the selected path, and not excluded - if not
681
 
                    # present it has been deleted so skip.
 
662
                if is_inside_any(specific_files, path):
 
663
                    # was inside the selected path, if not present it has been
 
664
                    # deleted so skip.
682
665
                    continue
683
 
                # From here down it was either not selected, or was excluded:
684
666
                if old_ie.kind == 'directory':
685
667
                    self._next_progress_entry()
686
 
                # We preserve the entry unaltered.
 
668
                # not in final inv yet, was not in the selected files, so is an
 
669
                # entry to be preserved unaltered.
687
670
                ie = old_ie.copy()
688
671
                # Note: specific file commits after a merge are currently
689
672
                # prohibited. This test is for sanity/safety in case it's
703
686
            set(self.builder.new_inventory._byid.keys())
704
687
        if deleted_ids:
705
688
            self.any_entries_deleted = True
706
 
            deleted = [(self.basis_tree.id2path(file_id), file_id)
 
689
            deleted = [(self.basis_inv.id2path(file_id), file_id)
707
690
                for file_id in deleted_ids]
708
691
            deleted.sort()
709
692
            # XXX: this is not quite directory-order sorting
711
694
                self._basis_delta.append((path, None, file_id, None))
712
695
                self.reporter.deleted(path)
713
696
 
714
 
    def _populate_from_inventory(self):
 
697
    def _populate_from_inventory(self, specific_files):
715
698
        """Populate the CommitBuilder by walking the working tree inventory."""
716
699
        if self.strict:
717
700
            # raise an exception as soon as we find a single unknown.
718
701
            for unknown in self.work_tree.unknowns():
719
702
                raise StrictCommitFailed()
720
 
        
721
 
        specific_files = self.specific_files
722
 
        exclude = self.exclude
 
703
               
723
704
        report_changes = self.reporter.is_verbose()
724
705
        deleted_ids = []
725
 
        # A tree of paths that have been deleted. E.g. if foo/bar has been
726
 
        # deleted, then we have {'foo':{'bar':{}}}
727
 
        deleted_paths = {}
728
 
        # XXX: Note that entries may have the wrong kind because the entry does
729
 
        # not reflect the status on disk.
 
706
        deleted_paths = set()
730
707
        work_inv = self.work_tree.inventory
731
 
        # NB: entries will include entries within the excluded ids/paths
732
 
        # because iter_entries_by_dir has no 'exclude' facility today.
 
708
        assert work_inv.root is not None
 
709
        # XXX: Note that entries may have the wrong kind.
733
710
        entries = work_inv.iter_entries_by_dir(
734
711
            specific_file_ids=self.specific_file_ids, yield_parents=True)
735
712
        for path, existing_ie in entries:
740
717
            if kind == 'directory':
741
718
                self._next_progress_entry()
742
719
            # Skip files that have been deleted from the working tree.
743
 
            # The deleted path ids are also recorded so they can be explicitly
744
 
            # unversioned later.
745
 
            if deleted_paths:
746
 
                path_segments = splitpath(path)
747
 
                deleted_dict = deleted_paths
748
 
                for segment in path_segments:
749
 
                    deleted_dict = deleted_dict.get(segment, None)
750
 
                    if not deleted_dict:
751
 
                        # We either took a path not present in the dict
752
 
                        # (deleted_dict was None), or we've reached an empty
753
 
                        # child dir in the dict, so are now a sub-path.
754
 
                        break
755
 
                else:
756
 
                    deleted_dict = None
757
 
                if deleted_dict is not None:
758
 
                    # the path has a deleted parent, do not add it.
759
 
                    continue
760
 
            if exclude and is_inside_any(exclude, path):
761
 
                # Skip excluded paths. Excluded paths are processed by
762
 
                # _update_builder_with_changes.
 
720
            # The deleted files/directories are also recorded so they
 
721
            # can be explicitly unversioned later. Note that when a
 
722
            # filter of specific files is given, we must only skip/record
 
723
            # deleted files matching that filter.
 
724
            if is_inside_any(deleted_paths, path):
763
725
                continue
764
726
            content_summary = self.work_tree.path_content_summary(path)
765
 
            # Note that when a filter of specific files is given, we must only
766
 
            # skip/record deleted files matching that filter.
767
727
            if not specific_files or is_inside_any(specific_files, path):
768
728
                if content_summary[0] == 'missing':
769
 
                    if not deleted_paths:
770
 
                        # path won't have been split yet.
771
 
                        path_segments = splitpath(path)
772
 
                    deleted_dict = deleted_paths
773
 
                    for segment in path_segments:
774
 
                        deleted_dict = deleted_dict.setdefault(segment, {})
 
729
                    deleted_paths.add(path)
775
730
                    self.reporter.missing(path)
776
731
                    deleted_ids.append(file_id)
777
732
                    continue
839
794
        # mutter('check %s {%s}', path, file_id)
840
795
        # mutter('%s selected for commit', path)
841
796
        if definitely_changed or existing_ie is None:
842
 
            ie = make_entry(kind, name, parent_id, file_id)
 
797
            ie = inventory.make_entry(kind, name, parent_id, file_id)
843
798
        else:
844
799
            ie = existing_ie.copy()
845
800
            ie.revision = None