263
263
if self.config is None:
264
264
self.config = self.branch.get_config()
266
self.work_inv = self.work_tree.inventory
267
self.basis_inv = self.basis_tree.inventory
266
# If provided, ensure the specified files are versioned
268
267
if specific_files is not None:
269
# Ensure specified files are versioned
270
# (We don't actually need the ids here)
268
# Note: We don't actually need the IDs here. This routine
269
# is being called because it raises PathNotVerisonedError
270
# as a side effect of finding the IDs.
271
271
# XXX: Dont we have filter_unversioned to do this more
273
273
tree.find_ids_across_trees(specific_files,
288
288
self.pb.show_count = True
289
289
self.pb.show_bar = False
291
# After a merge, a selected file commit is not supported.
292
# See 'bzr help merge' for an explanation as to why.
293
self.basis_inv = self.basis_tree.inventory
291
294
self._gather_parents()
292
295
if len(self.parents) > 1 and self.specific_files:
293
296
raise errors.CannotCommitSelectedFileMerge(self.specific_files)
295
# Build the new inventory
298
# Collect the changes
296
299
self._emit_progress_set_stage("Collecting changes", show_entries=True)
297
300
self.builder = self.branch.get_commit_builder(self.parents,
298
301
self.config, timestamp, timezone, committer, revprops, rev_id)
299
self._remove_deleted()
300
self._populate_new_inv()
301
self._report_deletes()
302
self._update_builder_with_changes()
302
303
self._check_pointless()
304
# TODO: Now the new inventory is known, check for conflicts and
305
# prompt the user for a commit message.
305
# TODO: Now the new inventory is known, check for conflicts.
306
306
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
307
307
# weave lines, because nothing should be recorded until it is known
308
308
# that commit will succeed.
309
309
self._emit_progress_set_stage("Saving data locally")
310
310
self.builder.finish_inventory()
312
# Prompt the user for a commit message if none provided
311
313
message = message_callback(self)
312
314
assert isinstance(message, unicode), type(message)
313
315
self.message = message
564
566
mutter('commit parent ghost revision {%s}', revision)
566
def _remove_deleted(self):
567
"""Remove deleted files from the working inventories.
569
This is done prior to taking the working inventory as the
570
basis for the new committed inventory.
572
This returns true if any files
573
*that existed in the basis inventory* were deleted.
574
Files that were added and deleted
575
in the working copy don't matter.
577
specific = self.specific_files
579
deleted_paths = set()
580
for path, ie in self.work_inv.iter_entries():
581
if is_inside_any(deleted_paths, path):
582
# The tree will delete the required ids recursively.
584
if specific and not is_inside_any(specific, path):
586
if not self.work_tree.has_filename(path):
587
deleted_paths.add(path)
588
self.reporter.missing(path)
589
deleted_ids.append(ie.file_id)
590
self.work_tree.unversion(deleted_ids)
592
def _populate_new_inv(self):
593
"""Build revision inventory.
595
This creates a new empty inventory. Depending on
596
which files are selected for commit, and what is present in the
597
current tree, the new inventory is populated. inventory entries
598
which are candidates for modification have their revision set to
599
None; inventory entries that are carried over untouched have their
600
revision set to their prior value.
568
def _update_builder_with_changes(self):
569
"""Update the commit builder with the data about what has changed.
571
# Build the revision inventory.
573
# This starts by creating a new empty inventory. Depending on
574
# which files are selected for commit, and what is present in the
575
# current tree, the new inventory is populated. inventory entries
576
# which are candidates for modification have their revision set to
577
# None; inventory entries that are carried over untouched have their
578
# revision set to their prior value.
602
580
# ESEPARATIONOFCONCERNS: this function is diffing and using the diff
603
581
# results to create a new inventory at the same time, which results
604
582
# in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
605
583
# ADHB 11-07-2006
606
mutter("Selecting files for commit with filter %s", self.specific_files)
607
assert self.work_inv.root is not None
608
entries = self.work_inv.iter_entries()
585
specific_files = self.specific_files
586
mutter("Selecting files for commit with filter %s", specific_files)
587
work_inv = self.work_tree.inventory
588
assert work_inv.root is not None
589
self.pb_entries_total = len(work_inv)
591
# Check and warn about old CommitBuilders
592
entries = work_inv.iter_entries()
609
593
if not self.builder.record_root_entry:
610
594
symbol_versioning.warn('CommitBuilders should support recording'
611
595
' the root entry as of bzr 0.10.', DeprecationWarning,
613
597
self.builder.new_inventory.add(self.basis_inv.root.copy())
615
self.pb_entries_total = len(self.work_inv)
601
deleted_paths = set()
616
602
for path, new_ie in entries:
617
603
self._emit_progress_next_entry()
618
604
file_id = new_ie.file_id
606
# Skip files that have been deleted from the working tree.
607
# The deleted files/directories are also recorded so they
608
# can be explicitly unversioned later. Note that when a
609
# filter of specific files is given, we must only skip/record
610
# deleted files matching that filter.
611
if is_inside_any(deleted_paths, path):
613
if not specific_files or is_inside_any(specific_files, path):
614
if not self.work_tree.has_filename(path):
615
deleted_paths.add(path)
616
self.reporter.missing(path)
617
deleted_ids.append(file_id)
620
620
kind = self.work_tree.kind(file_id)
621
621
if kind == 'tree-reference' and self.recursive == 'down':
678
678
self.reporter.snapshot_change(change, path)
680
if not self.specific_files:
683
# ignore removals that don't match filespec
684
for path, new_ie in self.basis_inv.iter_entries():
685
if new_ie.file_id in self.work_inv:
687
if is_inside_any(self.specific_files, path):
691
self.builder.record_entry_contents(ie, self.parent_invs, path,
680
# Unversion IDs that were found to be deleted
681
self.work_tree.unversion(deleted_ids)
683
# If specific files/directories were nominated, it is possible
684
# that some data from outside those needs to be preserved from
685
# the basis tree. For example, if a file x is moved from out of
686
# directory foo into directory bar and the user requests
687
# ``commit foo``, then information about bar/x must also be
690
for path, new_ie in self.basis_inv.iter_entries():
691
if new_ie.file_id in work_inv:
693
if is_inside_any(specific_files, path):
697
self.builder.record_entry_contents(ie, self.parent_invs, path,
700
# Report what was deleted. We could skip this when no deletes are
701
# detected to gain a performance win, but it arguably serves as a
702
# 'safety check' by informing the user whenever anything disappears.
703
for path, ie in self.basis_inv.iter_entries():
704
if ie.file_id not in self.builder.new_inventory:
705
self.reporter.deleted(path)
694
707
def _emit_progress_set_stage(self, name, show_entries=False):
695
708
"""Set the progress stage and emit an update to the progress bar."""