71
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any,
71
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any,
72
72
is_inside_or_parent_of_any,
73
minimum_path_selection,
73
74
quotefn, sha_file, split_lines)
74
75
from bzrlib.testament import Testament
75
76
from bzrlib.trace import mutter, note, warning, is_quiet
241
242
" parameter is required for commit().")
243
244
self.bound_branch = None
245
self.any_entries_changed = False
246
self.any_entries_deleted = False
244
247
self.local = local
245
248
self.master_branch = None
246
249
self.master_locked = False
247
250
self.rev_id = None
248
self.specific_files = specific_files
251
if specific_files is not None:
252
self.specific_files = sorted(
253
minimum_path_selection(specific_files))
255
self.specific_files = None
256
self.specific_file_ids = None
249
257
self.allow_pointless = allow_pointless
250
258
self.recursive = recursive
251
259
self.revprops = revprops
280
288
self.config = self.branch.get_config()
282
290
# If provided, ensure the specified files are versioned
283
if specific_files is not None:
284
# Note: We don't actually need the IDs here. This routine
291
if self.specific_files is not None:
285
293
# is being called because it raises PathNotVerisonedError
286
# as a side effect of finding the IDs.
294
# as a side effect of finding the IDs. We later use the ids we
295
# found as input to the working tree inventory iterator, so we
296
# only consider those ids rather than examining the whole tree
287
298
# XXX: Dont we have filter_unversioned to do this more
289
tree.find_ids_across_trees(specific_files,
290
[self.basis_tree, self.work_tree])
300
self.specific_file_ids = tree.find_ids_across_trees(
301
specific_files, [self.basis_tree, self.work_tree])
292
303
# Setup the progress bar. As the number of files that need to be
293
304
# committed in unknown, progress is reported as stages.
383
394
return NullCommitReporter()
384
395
return ReportCommitToLog()
386
def _any_real_changes(self):
387
"""Are there real changes between new_inventory and basis?
389
For trees without rich roots, inv.root.revision changes every commit.
390
But if that is the only change, we want to treat it as though there
393
new_entries = self.builder.new_inventory.iter_entries()
394
basis_entries = self.basis_inv.iter_entries()
395
new_path, new_root_ie = new_entries.next()
396
basis_path, basis_root_ie = basis_entries.next()
398
# This is a copy of InventoryEntry.__eq__ only leaving out .revision
399
def ie_equal_no_revision(this, other):
400
return ((this.file_id == other.file_id)
401
and (this.name == other.name)
402
and (this.symlink_target == other.symlink_target)
403
and (this.text_sha1 == other.text_sha1)
404
and (this.text_size == other.text_size)
405
and (this.text_id == other.text_id)
406
and (this.parent_id == other.parent_id)
407
and (this.kind == other.kind)
408
and (this.executable == other.executable)
409
and (this.reference_revision == other.reference_revision)
411
if not ie_equal_no_revision(new_root_ie, basis_root_ie):
414
for new_ie, basis_ie in zip(new_entries, basis_entries):
415
if new_ie != basis_ie:
418
# No actual changes present
421
397
def _check_pointless(self):
422
398
if self.allow_pointless:
435
411
# If length == 1, then we only have the root entry. Which means
436
412
# that there is no real difference (only the root could be different)
437
if (len(self.builder.new_inventory) != 1 and self._any_real_changes()):
413
if len(self.builder.new_inventory) != 1 and (self.any_entries_changed
414
or self.any_entries_deleted):
439
416
raise PointlessCommit()
671
648
# recorded in their previous state. For more details, see
672
649
# https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
673
650
if specific_files:
674
for path, new_ie in self.basis_inv.iter_entries():
675
if new_ie.file_id in self.builder.new_inventory:
651
for path, old_ie in self.basis_inv.iter_entries():
652
if old_ie.file_id in self.builder.new_inventory:
677
654
if is_inside_any(specific_files, path):
681
self.builder.record_entry_contents(ie, self.parent_invs, path,
656
if old_ie.kind == 'directory':
657
self._next_progress_entry()
659
# Note: specific file commits after a merge are currently
660
# prohibited. This test is for sanity/safety in case it's
661
# required after that changes.
662
if len(self.parents) > 1:
664
if self.builder.record_entry_contents(ie, self.parent_invs, path,
666
self.any_entries_changed = True
668
# note that deletes have occurred
669
if set(self.basis_inv._byid.keys()) - set(self.builder.new_inventory._byid.keys()):
670
self.any_entries_deleted = True
684
671
# Report what was deleted.
685
if self.reporter.is_verbose():
672
if self.any_entries_deleted and self.reporter.is_verbose():
686
673
for path, ie in self.basis_inv.iter_entries():
687
674
if ie.file_id not in self.builder.new_inventory:
688
675
self.reporter.deleted(path)
699
686
deleted_paths = set()
700
687
work_inv = self.work_tree.inventory
701
688
assert work_inv.root is not None
702
entries = work_inv.iter_entries_by_dir()
689
entries = work_inv.iter_entries_by_dir(
690
specific_file_ids=self.specific_file_ids, yield_parents=True)
703
691
if not self.builder.record_root_entry:
705
693
for path, existing_ie in entries:
717
704
# deleted files matching that filter.
718
705
if is_inside_any(deleted_paths, path):
720
if not specific_files or is_inside_any(specific_files, path):
721
if not self.work_tree.has_filename(path):
722
deleted_paths.add(path)
723
self.reporter.missing(path)
724
deleted_ids.append(file_id)
707
if not self.work_tree.has_filename(path):
708
deleted_paths.add(path)
709
self.reporter.missing(path)
710
deleted_ids.append(file_id)
727
713
kind = self.work_tree.kind(file_id)
728
714
# TODO: specific_files filtering before nested tree processing
735
721
# Note: I don't particularly want to have the existing_ie
736
722
# parameter but the test suite currently (28-Jun-07) breaks
737
723
# without it thanks to a unicode normalisation issue. :-(
738
definitely_changed = kind != existing_ie.kind
724
definitely_changed = kind != existing_ie.kind
739
725
self._record_entry(path, file_id, specific_files, kind, name,
740
726
parent_id, definitely_changed, existing_ie, report_changes)
772
758
report_changes=True):
773
759
"Record the new inventory entry for a path if any."
774
760
# mutter('check %s {%s}', path, file_id)
775
if (not specific_files or
776
is_inside_or_parent_of_any(specific_files, path)):
777
# mutter('%s selected for commit', path)
778
if definitely_changed or existing_ie is None:
779
ie = inventory.make_entry(kind, name, parent_id, file_id)
781
ie = existing_ie.copy()
761
# mutter('%s selected for commit', path)
762
if definitely_changed or existing_ie is None:
763
ie = inventory.make_entry(kind, name, parent_id, file_id)
784
# mutter('%s not selected for commit', path)
785
if self.basis_inv.has_id(file_id):
786
ie = self.basis_inv[file_id].copy()
788
# this entry is new and not being committed
791
self.builder.record_entry_contents(ie, self.parent_invs,
792
path, self.work_tree)
794
self._report_change(ie, path)
765
ie = existing_ie.copy()
767
if self.builder.record_entry_contents(ie, self.parent_invs,
768
path, self.work_tree):
769
self.any_entries_changed = True
771
self._report_change(ie, path)
797
774
def _report_change(self, ie, path):