67
66
from .osutils import (get_user_encoding,
69
68
minimum_path_selection,
72
70
from .trace import mutter, note, is_quiet
73
71
from .urlutils import unescape_for_display
101
99
new_path = change[1][1]
103
101
new_excluded = (new_path is not None and
104
is_inside_any(exclude, new_path))
102
is_inside_any(exclude, new_path))
106
104
old_excluded = (old_path is not None and
107
is_inside_any(exclude, old_path))
105
is_inside_any(exclude, old_path))
109
107
if old_excluded and new_excluded:
212
211
if possible_master_transports is None:
213
212
possible_master_transports = []
214
if (not u'branch-nick' in revprops and
213
if (u'branch-nick' not in revprops and
215
214
branch.repository._format.supports_storing_branch_nick):
216
215
revprops[u'branch-nick'] = branch._get_nick(
251
250
"""Commit working copy as a new revision.
253
252
:param message: the commit message (it or message_callback is required)
254
:param message_callback: A callback: message = message_callback(cmt_obj)
253
:param message_callback: A callback: message =
254
message_callback(cmt_obj)
256
256
:param timestamp: if not None, seconds-since-epoch for a
257
257
postdated/predated commit.
288
288
# XXX: Can be set on __init__ or passed in - this is a bit ugly.
289
289
self.config_stack = config or self.config_stack
290
290
return operation.run(
295
specific_files=specific_files,
297
allow_pointless=allow_pointless,
300
working_tree=working_tree,
303
message_callback=message_callback,
306
possible_master_transports=possible_master_transports,
295
specific_files=specific_files,
297
allow_pointless=allow_pointless,
300
working_tree=working_tree,
303
message_callback=message_callback,
306
possible_master_transports=possible_master_transports,
309
309
def _commit(self, operation, message, timestamp, timezone, committer,
310
specific_files, rev_id, allow_pointless, strict, verbose,
311
working_tree, local, reporter, message_callback, recursive,
312
exclude, possible_master_transports, lossy):
310
specific_files, rev_id, allow_pointless, strict, verbose,
311
working_tree, local, reporter, message_callback, recursive,
312
exclude, possible_master_transports, lossy):
313
313
mutter('preparing to commit')
315
315
if working_tree is None:
324
324
if message is not None:
325
325
if isinstance(message, bytes):
326
326
message = message.decode(get_user_encoding())
327
message_callback = lambda x: message
328
def message_callback(x):
329
331
raise BzrError("The message or message_callback keyword"
330
332
" parameter is required for commit().")
411
413
# Collect the changes
412
414
self._set_progress_stage("Collecting changes", counter=True)
413
415
self._lossy = lossy
414
self.builder = self.branch.get_commit_builder(self.parents,
415
self.config_stack, timestamp, timezone, committer, self.revprops,
416
self.builder = self.branch.get_commit_builder(
417
self.parents, self.config_stack, timestamp, timezone, committer,
418
self.revprops, rev_id, lossy=lossy)
418
420
if self.builder.updates_branch and self.bound_branch:
419
421
self.builder.abort()
462
464
self.work_tree.unversion(self.deleted_paths)
463
465
self._set_progress_stage("Updating the working tree")
464
466
self.work_tree.update_basis_by_delta(self.rev_id,
465
self.builder.get_basis_delta())
467
self.builder.get_basis_delta())
466
468
self.reporter.completed(new_revno, self.rev_id)
467
469
self._process_post_hooks(old_revno, new_revno)
468
470
return self.rev_id
499
501
self._process_pre_hooks(old_revno, new_revno)
502
except BaseException:
501
503
# The commit builder will already have updated the branch,
503
505
self.branch.set_last_revision_info(old_revno, old_revid)
510
512
self.master_branch.tags)
511
513
if tag_conflicts:
512
514
warning_lines = [' ' + name for name, _, _ in tag_conflicts]
513
note( gettext("Conflicting tags in bound branch:\n{0}".format(
514
"\n".join(warning_lines))) )
515
note(gettext("Conflicting tags in bound branch:\n{0}".format(
516
"\n".join(warning_lines))))
516
518
def _select_reporter(self):
517
519
"""Select the CommitReporter to use."""
551
553
# If the master branch is bound, we must fail
552
554
master_bound_location = self.master_branch.get_bound_location()
553
555
if master_bound_location:
554
raise errors.CommitToDoubleBoundBranch(self.branch,
555
self.master_branch, master_bound_location)
556
raise errors.CommitToDoubleBoundBranch(
557
self.branch, self.master_branch, master_bound_location)
557
559
# TODO: jam 20051230 We could automatically push local
558
560
# commits to the remote branch if they would fit.
564
566
local_info = self.branch.last_revision_info()
565
567
if local_info != master_info:
566
568
raise errors.BoundBranchOutOfDate(self.branch,
569
571
# Now things are ready to change the master branch
570
572
# so grab the lock
614
616
# this would be nicer with twisted.python.reflect.namedAny
615
617
for hook in hooks:
616
618
result = eval(hook + '(branch, rev_id)',
617
{'branch':self.branch,
619
'rev_id':self.rev_id})
619
{'branch': self.branch,
621
'rev_id': self.rev_id})
620
622
# process new style post commit hooks
621
623
self._process_hooks("post_commit", old_revno, new_revno)
642
644
if hook_name == "pre_commit":
643
645
future_tree = self.builder.revision_tree()
644
646
tree_delta = future_tree.changes_from(self.basis_tree,
647
649
for hook in Branch.hooks[hook_name]:
648
650
# show the running hook in the progress bar. As hooks may
670
672
mutter("Selecting files for commit with filter %r", specific_files)
672
674
self._check_strict()
673
iter_changes = self.work_tree.iter_changes(self.basis_tree,
674
specific_files=specific_files)
675
iter_changes = self.work_tree.iter_changes(
676
self.basis_tree, specific_files=specific_files)
676
678
iter_changes = filter_excluded(iter_changes, self.exclude)
677
679
iter_changes = self._filter_iter_changes(iter_changes)
678
680
for file_id, path, fs_hash in self.builder.record_iter_changes(
679
self.work_tree, self.basis_revid, iter_changes):
680
self.work_tree._observed_sha1(file_id, path, fs_hash)
681
self.work_tree, self.basis_revid, iter_changes):
682
self.work_tree._observed_sha1(path, fs_hash)
682
684
def _filter_iter_changes(self, iter_changes):
683
685
"""Process iter_changes.
685
This method reports on the changes in iter_changes to the user, and
687
This method reports on the changes in iter_changes to the user, and
686
688
converts 'missing' entries in the iter_changes iterator to 'deleted'
687
689
entries. 'missing' entries have their
706
708
deleted_paths.append(change[1][1])
707
709
# Reset the new path (None) and new versioned flag (False)
708
710
change = (change[0], (change[1][0], None), change[2],
709
(change[3][0], False)) + change[4:]
711
(change[3][0], False)) + change[4:]
710
712
new_path = change[1][1]
711
713
versioned = False
712
714
elif kind == 'tree-reference':
713
715
if self.recursive == 'down':
714
self._commit_nested_tree(change[0], change[1][1])
716
self._commit_nested_tree(change[1][1])
715
717
if change[3][0] or change[3][1]:
717
719
if report_changes:
720
722
elif old_path is None:
721
723
reporter.snapshot_change(gettext('added'), new_path)
722
724
elif old_path != new_path:
723
reporter.renamed(gettext('renamed'), old_path, new_path)
725
reporter.renamed(gettext('renamed'),
726
self.work_tree.branch.repository._format.rich_root_data):
729
or self.work_tree.branch.repository._format.rich_root_data):
727
730
# Don't report on changes to '' in non rich root
729
reporter.snapshot_change(gettext('modified'), new_path)
732
reporter.snapshot_change(
733
gettext('modified'), new_path)
730
734
self._next_progress_entry()
731
735
# Unversion files that were found to be deleted
732
736
self.deleted_paths = deleted_paths
740
744
for unknown in self.work_tree.unknowns():
741
745
raise StrictCommitFailed()
743
def _commit_nested_tree(self, file_id, path):
747
def _commit_nested_tree(self, path):
744
748
"Commit a nested tree."
745
sub_tree = self.work_tree.get_nested_tree(path, file_id)
749
sub_tree = self.work_tree.get_nested_tree(path)
746
750
# FIXME: be more comprehensive here:
747
751
# this works when both trees are in --trees repository,
748
752
# but when both are bound to a different repository,
750
754
# finally implement the explicit-caches approach design
751
755
# a while back - RBC 20070306.
752
756
if sub_tree.branch.repository.has_same_location(
753
self.work_tree.branch.repository):
757
self.work_tree.branch.repository):
754
758
sub_tree.branch.repository = \
755
759
self.work_tree.branch.repository
757
761
return sub_tree.commit(message=None, revprops=self.revprops,
758
recursive=self.recursive,
759
message_callback=self.message_callback,
760
timestamp=self.timestamp, timezone=self.timezone,
761
committer=self.committer,
762
allow_pointless=self.allow_pointless,
763
strict=self.strict, verbose=self.verbose,
764
local=self.local, reporter=self.reporter)
762
recursive=self.recursive,
763
message_callback=self.message_callback,
764
timestamp=self.timestamp,
765
timezone=self.timezone,
766
committer=self.committer,
767
allow_pointless=self.allow_pointless,
768
strict=self.strict, verbose=self.verbose,
769
local=self.local, reporter=self.reporter)
765
770
except PointlessCommit:
766
return self.work_tree.get_reference_revision(path, file_id)
771
return self.work_tree.get_reference_revision(path)
768
773
def _set_progress_stage(self, name, counter=False):
769
774
"""Set the progress stage and emit an update to the progress bar."""
783
788
def _emit_progress(self):
784
789
if self.pb_entries_count is not None:
785
790
text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
786
self.pb_entries_count)
791
self.pb_entries_count)
788
793
text = gettext("%s - Stage") % (self.pb_stage_name, )
789
794
self.pb.update(text, self.pb_stage_count, self.pb_stage_total)