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.
562
564
# Make sure the local branch is identical to the master
563
master_info = self.master_branch.last_revision_info()
564
local_info = self.branch.last_revision_info()
565
if local_info != master_info:
565
master_revid = self.master_branch.last_revision()
566
local_revid = self.branch.last_revision()
567
if local_revid != master_revid:
566
568
raise errors.BoundBranchOutOfDate(self.branch,
569
571
# Now things are ready to change the master branch
570
572
# so grab the lock
586
588
# - in a checkout scenario the tree may have no
587
589
# parents but the branch may do.
588
590
first_tree_parent = breezy.revision.NULL_REVISION
589
old_revno, master_last = self.master_branch.last_revision_info()
592
old_revno, master_last = self.master_branch.last_revision_info()
593
except errors.UnsupportedOperation:
594
master_last = self.master_branch.last_revision()
595
old_revno = self.branch.revision_id_to_revno(master_last)
590
596
if master_last != first_tree_parent:
591
597
if master_last != breezy.revision.NULL_REVISION:
592
598
raise errors.OutOfDateTree(self.work_tree)
614
620
# this would be nicer with twisted.python.reflect.namedAny
615
621
for hook in hooks:
616
622
result = eval(hook + '(branch, rev_id)',
617
{'branch':self.branch,
619
'rev_id':self.rev_id})
623
{'branch': self.branch,
625
'rev_id': self.rev_id})
620
626
# process new style post commit hooks
621
627
self._process_hooks("post_commit", old_revno, new_revno)
642
648
if hook_name == "pre_commit":
643
649
future_tree = self.builder.revision_tree()
644
650
tree_delta = future_tree.changes_from(self.basis_tree,
647
653
for hook in Branch.hooks[hook_name]:
648
654
# show the running hook in the progress bar. As hooks may
670
676
mutter("Selecting files for commit with filter %r", specific_files)
672
678
self._check_strict()
673
iter_changes = self.work_tree.iter_changes(self.basis_tree,
674
specific_files=specific_files)
679
iter_changes = self.work_tree.iter_changes(
680
self.basis_tree, specific_files=specific_files)
676
682
iter_changes = filter_excluded(iter_changes, self.exclude)
677
683
iter_changes = self._filter_iter_changes(iter_changes)
678
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)
684
for path, fs_hash in self.builder.record_iter_changes(
685
self.work_tree, self.basis_revid, iter_changes):
686
self.work_tree._observed_sha1(path, fs_hash)
682
688
def _filter_iter_changes(self, iter_changes):
683
689
"""Process iter_changes.
685
This method reports on the changes in iter_changes to the user, and
691
This method reports on the changes in iter_changes to the user, and
686
692
converts 'missing' entries in the iter_changes iterator to 'deleted'
687
693
entries. 'missing' entries have their
706
712
deleted_paths.append(change[1][1])
707
713
# Reset the new path (None) and new versioned flag (False)
708
714
change = (change[0], (change[1][0], None), change[2],
709
(change[3][0], False)) + change[4:]
715
(change[3][0], False)) + change[4:]
710
716
new_path = change[1][1]
711
717
versioned = False
712
718
elif kind == 'tree-reference':
713
719
if self.recursive == 'down':
714
self._commit_nested_tree(change[0], change[1][1])
720
self._commit_nested_tree(change[1][1])
715
721
if change[3][0] or change[3][1]:
717
723
if report_changes:
720
726
elif old_path is None:
721
727
reporter.snapshot_change(gettext('added'), new_path)
722
728
elif old_path != new_path:
723
reporter.renamed(gettext('renamed'), old_path, new_path)
729
reporter.renamed(gettext('renamed'),
726
self.work_tree.branch.repository._format.rich_root_data):
733
or self.work_tree.branch.repository._format.rich_root_data):
727
734
# Don't report on changes to '' in non rich root
729
reporter.snapshot_change(gettext('modified'), new_path)
736
reporter.snapshot_change(
737
gettext('modified'), new_path)
730
738
self._next_progress_entry()
731
739
# Unversion files that were found to be deleted
732
740
self.deleted_paths = deleted_paths
740
748
for unknown in self.work_tree.unknowns():
741
749
raise StrictCommitFailed()
743
def _commit_nested_tree(self, file_id, path):
751
def _commit_nested_tree(self, path):
744
752
"Commit a nested tree."
745
sub_tree = self.work_tree.get_nested_tree(path, file_id)
753
sub_tree = self.work_tree.get_nested_tree(path)
746
754
# FIXME: be more comprehensive here:
747
755
# this works when both trees are in --trees repository,
748
756
# but when both are bound to a different repository,
750
758
# finally implement the explicit-caches approach design
751
759
# a while back - RBC 20070306.
752
760
if sub_tree.branch.repository.has_same_location(
753
self.work_tree.branch.repository):
761
self.work_tree.branch.repository):
754
762
sub_tree.branch.repository = \
755
763
self.work_tree.branch.repository
757
765
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)
766
recursive=self.recursive,
767
message_callback=self.message_callback,
768
timestamp=self.timestamp,
769
timezone=self.timezone,
770
committer=self.committer,
771
allow_pointless=self.allow_pointless,
772
strict=self.strict, verbose=self.verbose,
773
local=self.local, reporter=self.reporter)
765
774
except PointlessCommit:
766
return self.work_tree.get_reference_revision(path, file_id)
775
return self.work_tree.get_reference_revision(path)
768
777
def _set_progress_stage(self, name, counter=False):
769
778
"""Set the progress stage and emit an update to the progress bar."""
783
792
def _emit_progress(self):
784
793
if self.pb_entries_count is not None:
785
794
text = gettext("{0} [{1}] - Stage").format(self.pb_stage_name,
786
self.pb_entries_count)
795
self.pb_entries_count)
788
797
text = gettext("%s - Stage") % (self.pb_stage_name, )
789
798
self.pb.update(text, self.pb_stage_count, self.pb_stage_total)