~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-11-04 18:51:39 UTC
  • mfrom: (2961.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20071104185139-kaio3sneodg2kp71
Authentication ring implementation (read-only)

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
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
338
318
            self.pb.show_count = True
339
319
            self.pb.show_bar = True
340
320
 
 
321
            # After a merge, a selected file commit is not supported.
 
322
            # See 'bzr help merge' for an explanation as to why.
341
323
            self.basis_inv = self.basis_tree.inventory
342
324
            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
325
            if len(self.parents) > 1 and self.specific_files:
346
326
                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
327
 
351
328
            # Collect the changes
352
329
            self._set_progress_stage("Collecting changes",
377
354
 
378
355
                # Prompt the user for a commit message if none provided
379
356
                message = message_callback(self)
 
357
                assert isinstance(message, unicode), type(message)
380
358
                self.message = message
381
359
                self._escape_commit_message()
382
360
 
392
370
            # Upload revision data to the master.
393
371
            # this will propagate merged revisions too if needed.
394
372
            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)
 
373
                self._set_progress_stage("Uploading data to master branch")
 
374
                self.master_branch.repository.fetch(self.branch.repository,
 
375
                                                    revision_id=self.rev_id)
400
376
                # now the master has the revision data
401
377
                # 'commit' to the master first so a timeout here causes the
402
378
                # local branch to be out of date
408
384
 
409
385
            # Make the working tree up to date with the branch
410
386
            self._set_progress_stage("Updating the working tree")
411
 
            self.work_tree.update_basis_by_delta(self.rev_id,
412
 
                 self._basis_delta)
 
387
            rev_tree = self.builder.revision_tree()
 
388
            # XXX: This will need to be changed if we support doing a
 
389
            # selective commit while a merge is still pending - then we'd
 
390
            # still have multiple parents after the commit.
 
391
            #
 
392
            # XXX: update_basis_by_delta is slower at present because it works
 
393
            # on inventories, so this is not active until there's a native
 
394
            # dirstate implementation.
 
395
            ## self.work_tree.update_basis_by_delta(self.rev_id,
 
396
            ##      self._basis_delta)
 
397
            self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
413
398
            self.reporter.completed(new_revno, self.rev_id)
414
399
            self._process_post_hooks(old_revno, new_revno)
415
400
        finally:
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
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 = {}
 
706
        deleted_paths = set()
728
707
        # XXX: Note that entries may have the wrong kind because the entry does
729
708
        # not reflect the status on disk.
730
709
        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.
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