109
109
def _note(self, format, *args):
110
110
"""Output a message.
112
Messages are output by writing directly to stderr instead of
113
using bzrlib.trace.note(). The latter constantly updates the
114
log file as we go causing an unnecessary performance hit.
116
Subclasses may choose to override this method but need to be aware
117
of its potential impact on performance.
112
Subclasses may choose to override this method.
119
bzrlib.ui.ui_factory.clear_term()
120
sys.stderr.write((format + "\n") % args)
122
116
def snapshot_change(self, change, path):
123
117
if change == 'unchanged':
269
263
if self.config is None:
270
264
self.config = self.branch.get_config()
272
self.work_inv = self.work_tree.inventory
273
self.basis_inv = self.basis_tree.inventory
266
# If provided, ensure the specified files are versioned
274
267
if specific_files is not None:
275
# Ensure specified files are versioned
276
# (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.
277
271
# XXX: Dont we have filter_unversioned to do this more
279
273
tree.find_ids_across_trees(specific_files,
280
274
[self.basis_tree, self.work_tree])
282
# Setup the progress bar ...
283
# one to finish, one for rev and inventory, and one for each
284
# inventory entry, and the same for the new inventory.
285
# note that this estimate is too long when we do a partial tree
286
# commit which excludes some new files from being considered.
287
# The estimate is corrected when we populate the new inv.
288
self.pb_total = len(self.work_inv) + 5
276
# Setup the progress bar. As the number of files that need to be
277
# committed in unknown, progress is reported as stages.
278
# We keep track of entries separately though and include that
279
# information in the progress bar during the relevant stages.
280
self.pb_stage_name = ""
281
self.pb_stage_count = 0
282
self.pb_stage_total = 4
283
if self.bound_branch:
284
self.pb_stage_total += 1
285
self.pb.show_pct = False
286
self.pb.show_spinner = False
287
self.pb.show_eta = False
288
self.pb.show_count = True
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
299
self._emit_progress_set_stage("Collecting changes", show_entries=True)
296
300
self.builder = self.branch.get_commit_builder(self.parents,
297
301
self.config, timestamp, timezone, committer, revprops, rev_id)
298
self._remove_deleted()
299
self._populate_new_inv()
300
self._report_deletes()
302
self._update_builder_with_changes()
301
303
self._check_pointless()
302
self._emit_progress_update()
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
self._emit_progress_set_stage("Saving data locally")
309
310
self.builder.finish_inventory()
310
self._emit_progress_update()
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
332
334
self.branch.set_last_revision_info(new_revno, self.rev_id)
334
336
# Make the working tree up to date with the branch
337
self._emit_progress_set_stage("Updating the working tree")
335
338
rev_tree = self.builder.revision_tree()
336
339
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
337
340
self.reporter.completed(new_revno, self.rev_id)
339
342
# Process the post commit hooks, if any
343
self._emit_progress_set_stage("Running post commit hooks")
340
344
self._process_hooks(old_revno, new_revno)
341
self._emit_progress_update()
344
347
return self.rev_id
563
566
mutter('commit parent ghost revision {%s}', revision)
565
def _remove_deleted(self):
566
"""Remove deleted files from the working inventories.
568
This is done prior to taking the working inventory as the
569
basis for the new committed inventory.
571
This returns true if any files
572
*that existed in the basis inventory* were deleted.
573
Files that were added and deleted
574
in the working copy don't matter.
576
specific = self.specific_files
578
deleted_paths = set()
579
for path, ie in self.work_inv.iter_entries():
580
if is_inside_any(deleted_paths, path):
581
# The tree will delete the required ids recursively.
583
if specific and not is_inside_any(specific, path):
585
if not self.work_tree.has_filename(path):
586
deleted_paths.add(path)
587
self.reporter.missing(path)
588
deleted_ids.append(ie.file_id)
589
self.work_tree.unversion(deleted_ids)
591
def _populate_new_inv(self):
592
"""Build revision inventory.
594
This creates a new empty inventory. Depending on
595
which files are selected for commit, and what is present in the
596
current tree, the new inventory is populated. inventory entries
597
which are candidates for modification have their revision set to
598
None; inventory entries that are carried over untouched have their
599
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.
601
580
# ESEPARATIONOFCONCERNS: this function is diffing and using the diff
602
581
# results to create a new inventory at the same time, which results
603
582
# in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
604
583
# ADHB 11-07-2006
605
mutter("Selecting files for commit with filter %s", self.specific_files)
606
assert self.work_inv.root is not None
607
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()
608
593
if not self.builder.record_root_entry:
609
594
symbol_versioning.warn('CommitBuilders should support recording'
610
595
' the root entry as of bzr 0.10.', DeprecationWarning,
612
597
self.builder.new_inventory.add(self.basis_inv.root.copy())
614
self._emit_progress_update()
601
deleted_paths = set()
615
602
for path, new_ie in entries:
616
self._emit_progress_update()
603
self._emit_progress_next_entry()
617
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)
619
620
kind = self.work_tree.kind(file_id)
620
621
if kind == 'tree-reference' and self.recursive == 'down':
677
678
self.reporter.snapshot_change(change, path)
679
if not self.specific_files:
682
# ignore removals that don't match filespec
683
for path, new_ie in self.basis_inv.iter_entries():
684
if new_ie.file_id in self.work_inv:
686
if is_inside_any(self.specific_files, path):
690
self.builder.record_entry_contents(ie, self.parent_invs, path,
693
def _emit_progress_update(self):
694
"""Emit an update to the progress bar."""
695
self.pb.update("Committing", self.pb_count, self.pb_total)
698
def _report_deletes(self):
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.
699
703
for path, ie in self.basis_inv.iter_entries():
700
704
if ie.file_id not in self.builder.new_inventory:
701
705
self.reporter.deleted(path)
707
def _emit_progress_set_stage(self, name, show_entries=False):
708
"""Set the progress stage and emit an update to the progress bar."""
709
self.pb_stage_name = name
710
self.pb_stage_count += 1
711
self.pb_entries_show = show_entries
713
self.pb_entries_count = 0
714
self.pb_entries_total = '?'
715
self._emit_progress()
717
def _emit_progress_next_entry(self):
718
"""Emit an update to the progress bar and increment the file count."""
719
self.pb_entries_count += 1
720
self._emit_progress()
722
def _emit_progress(self):
723
if self.pb_entries_show:
724
text = "%s [Entry %d/%s] - Stage" % (self.pb_stage_name,
725
self.pb_entries_count,str(self.pb_entries_total))
727
text = "%s - Stage" % (self.pb_stage_name)
728
self.pb.update(text, self.pb_stage_count, self.pb_stage_total)