285
285
:param lossy: When committing to a foreign VCS, ignore any
286
286
data that can not be natively represented.
288
operation = OperationWithCleanups(self._commit)
289
self.revprops = revprops or {}
290
# XXX: Can be set on __init__ or passed in - this is a bit ugly.
291
self.config_stack = config or self.config_stack
292
return operation.run(
297
specific_files=specific_files,
299
allow_pointless=allow_pointless,
302
working_tree=working_tree,
305
message_callback=message_callback,
308
possible_master_transports=possible_master_transports,
311
def _commit(self, operation, message, timestamp, timezone, committer,
312
specific_files, rev_id, allow_pointless, strict, verbose,
313
working_tree, local, reporter, message_callback, recursive,
314
exclude, possible_master_transports, lossy):
315
mutter('preparing to commit')
317
if working_tree is None:
318
raise BzrError("working_tree must be passed into commit().")
320
self.work_tree = working_tree
321
self.branch = self.work_tree.branch
322
if getattr(self.work_tree, 'requires_rich_root', lambda: False)():
323
if not self.branch.repository.supports_rich_root():
324
raise errors.RootNotRich()
325
if message_callback is None:
326
if message is not None:
327
if isinstance(message, bytes):
328
message = message.decode(get_user_encoding())
330
def message_callback(x):
333
raise BzrError("The message or message_callback keyword"
334
" parameter is required for commit().")
336
self.bound_branch = None
337
self.any_entries_deleted = False
338
if exclude is not None:
339
self.exclude = sorted(
340
minimum_path_selection(exclude))
344
self.master_branch = None
345
self.recursive = recursive
347
# self.specific_files is None to indicate no filter, or any iterable to
348
# indicate a filter - [] means no files at all, as per iter_changes.
349
if specific_files is not None:
350
self.specific_files = sorted(
351
minimum_path_selection(specific_files))
353
self.specific_files = None
355
self.allow_pointless = allow_pointless
356
self.message_callback = message_callback
357
self.timestamp = timestamp
358
self.timezone = timezone
359
self.committer = committer
361
self.verbose = verbose
363
self.work_tree.lock_write()
364
operation.add_cleanup(self.work_tree.unlock)
365
self.parents = self.work_tree.get_parent_ids()
366
self.pb = ui.ui_factory.nested_progress_bar()
367
operation.add_cleanup(self.pb.finished)
368
self.basis_revid = self.work_tree.last_revision()
369
self.basis_tree = self.work_tree.basis_tree()
370
self.basis_tree.lock_read()
371
operation.add_cleanup(self.basis_tree.unlock)
372
# Cannot commit with conflicts present.
373
if len(self.work_tree.conflicts()) > 0:
374
raise ConflictsInTree
376
# Setup the bound branch variables as needed.
377
self._check_bound_branch(operation, possible_master_transports)
379
if self.config_stack is None:
380
self.config_stack = self.work_tree.get_config_stack()
382
# Check that the working tree is up to date
383
old_revno, old_revid, new_revno = self._check_out_of_date_tree()
385
# Complete configuration setup
386
if reporter is not None:
387
self.reporter = reporter
388
elif self.reporter is None:
389
self.reporter = self._select_reporter()
391
# Setup the progress bar. As the number of files that need to be
392
# committed in unknown, progress is reported as stages.
393
# We keep track of entries separately though and include that
394
# information in the progress bar during the relevant stages.
395
self.pb_stage_name = ""
396
self.pb_stage_count = 0
397
self.pb_stage_total = 5
398
if self.bound_branch:
399
# 2 extra stages: "Uploading data to master branch" and "Merging
400
# tags to master branch"
401
self.pb_stage_total += 2
402
self.pb.show_pct = False
403
self.pb.show_spinner = False
404
self.pb.show_eta = False
405
self.pb.show_count = True
406
self.pb.show_bar = True
408
# After a merge, a selected file commit is not supported.
409
# See 'bzr help merge' for an explanation as to why.
410
if len(self.parents) > 1 and self.specific_files is not None:
411
raise CannotCommitSelectedFileMerge(self.specific_files)
412
# Excludes are a form of selected file commit.
413
if len(self.parents) > 1 and self.exclude:
414
raise CannotCommitSelectedFileMerge(self.exclude)
416
# Collect the changes
417
self._set_progress_stage("Collecting changes", counter=True)
419
self.builder = self.branch.get_commit_builder(
420
self.parents, self.config_stack, timestamp, timezone, committer,
421
self.revprops, rev_id, lossy=lossy)
423
if self.builder.updates_branch and self.bound_branch:
425
raise AssertionError(
426
"bound branches not supported for commit builders "
427
"that update the branch")
430
# find the location being committed to
288
with ExitStack() as stack:
289
self.revprops = revprops or {}
290
# XXX: Can be set on __init__ or passed in - this is a bit ugly.
291
self.config_stack = config or self.config_stack
292
mutter('preparing to commit')
294
if working_tree is None:
295
raise BzrError("working_tree must be passed into commit().")
297
self.work_tree = working_tree
298
self.branch = self.work_tree.branch
299
if getattr(self.work_tree, 'requires_rich_root', lambda: False)():
300
if not self.branch.repository.supports_rich_root():
301
raise errors.RootNotRich()
302
if message_callback is None:
303
if message is not None:
304
if isinstance(message, bytes):
305
message = message.decode(get_user_encoding())
307
def message_callback(x):
310
raise BzrError("The message or message_callback keyword"
311
" parameter is required for commit().")
313
self.bound_branch = None
314
self.any_entries_deleted = False
315
if exclude is not None:
316
self.exclude = sorted(
317
minimum_path_selection(exclude))
321
self.master_branch = None
322
self.recursive = recursive
324
# self.specific_files is None to indicate no filter, or any iterable to
325
# indicate a filter - [] means no files at all, as per iter_changes.
326
if specific_files is not None:
327
self.specific_files = sorted(
328
minimum_path_selection(specific_files))
330
self.specific_files = None
332
self.allow_pointless = allow_pointless
333
self.message_callback = message_callback
334
self.timestamp = timestamp
335
self.timezone = timezone
336
self.committer = committer
338
self.verbose = verbose
340
stack.enter_context(self.work_tree.lock_write())
341
self.parents = self.work_tree.get_parent_ids()
342
self.pb = ui.ui_factory.nested_progress_bar()
343
stack.callback(self.pb.finished)
344
self.basis_revid = self.work_tree.last_revision()
345
self.basis_tree = self.work_tree.basis_tree()
346
stack.enter_context(self.basis_tree.lock_read())
347
# Cannot commit with conflicts present.
348
if len(self.work_tree.conflicts()) > 0:
349
raise ConflictsInTree
351
# Setup the bound branch variables as needed.
352
self._check_bound_branch(stack, possible_master_transports)
353
if self.config_stack is None:
354
self.config_stack = self.work_tree.get_config_stack()
356
# Check that the working tree is up to date
357
old_revno, old_revid, new_revno = self._check_out_of_date_tree()
359
# Complete configuration setup
360
if reporter is not None:
361
self.reporter = reporter
362
elif self.reporter is None:
363
self.reporter = self._select_reporter()
365
# Setup the progress bar. As the number of files that need to be
366
# committed in unknown, progress is reported as stages.
367
# We keep track of entries separately though and include that
368
# information in the progress bar during the relevant stages.
369
self.pb_stage_name = ""
370
self.pb_stage_count = 0
371
self.pb_stage_total = 5
431
372
if self.bound_branch:
432
master_location = self.master_branch.base
434
master_location = self.branch.base
436
# report the start of the commit
437
self.reporter.started(new_revno, self.rev_id, master_location)
439
self._update_builder_with_changes()
440
self._check_pointless()
442
# TODO: Now the new inventory is known, check for conflicts.
443
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
444
# weave lines, because nothing should be recorded until it is known
445
# that commit will succeed.
446
self._set_progress_stage("Saving data locally")
447
self.builder.finish_inventory()
449
# Prompt the user for a commit message if none provided
450
message = message_callback(self)
451
self.message = message
453
# Add revision data to the local branch
454
self.rev_id = self.builder.commit(self.message)
457
mutter("aborting commit write group because of exception:")
458
trace.log_exception_quietly()
462
self._update_branches(old_revno, old_revid, new_revno)
464
# Make the working tree be up to date with the branch. This
465
# includes automatic changes scheduled to be made to the tree, such
466
# as updating its basis and unversioning paths that were missing.
467
self.work_tree.unversion(self.deleted_paths)
468
self._set_progress_stage("Updating the working tree")
469
self.work_tree.update_basis_by_delta(self.rev_id,
470
self.builder.get_basis_delta())
471
self.reporter.completed(new_revno, self.rev_id)
472
self._process_post_hooks(old_revno, new_revno)
373
# 2 extra stages: "Uploading data to master branch" and "Merging
374
# tags to master branch"
375
self.pb_stage_total += 2
376
self.pb.show_pct = False
377
self.pb.show_spinner = False
378
self.pb.show_eta = False
379
self.pb.show_count = True
380
self.pb.show_bar = True
382
# After a merge, a selected file commit is not supported.
383
# See 'bzr help merge' for an explanation as to why.
384
if len(self.parents) > 1 and self.specific_files is not None:
385
raise CannotCommitSelectedFileMerge(self.specific_files)
386
# Excludes are a form of selected file commit.
387
if len(self.parents) > 1 and self.exclude:
388
raise CannotCommitSelectedFileMerge(self.exclude)
390
# Collect the changes
391
self._set_progress_stage("Collecting changes", counter=True)
393
self.builder = self.branch.get_commit_builder(
394
self.parents, self.config_stack, timestamp, timezone, committer,
395
self.revprops, rev_id, lossy=lossy)
397
if self.builder.updates_branch and self.bound_branch:
399
raise AssertionError(
400
"bound branches not supported for commit builders "
401
"that update the branch")
404
# find the location being committed to
405
if self.bound_branch:
406
master_location = self.master_branch.base
408
master_location = self.branch.base
410
# report the start of the commit
411
self.reporter.started(new_revno, self.rev_id, master_location)
413
self._update_builder_with_changes()
414
self._check_pointless()
416
# TODO: Now the new inventory is known, check for conflicts.
417
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
418
# weave lines, because nothing should be recorded until it is known
419
# that commit will succeed.
420
self._set_progress_stage("Saving data locally")
421
self.builder.finish_inventory()
423
# Prompt the user for a commit message if none provided
424
message = message_callback(self)
425
self.message = message
427
# Add revision data to the local branch
428
self.rev_id = self.builder.commit(self.message)
431
mutter("aborting commit write group because of exception:")
432
trace.log_exception_quietly()
436
self._update_branches(old_revno, old_revid, new_revno)
438
# Make the working tree be up to date with the branch. This
439
# includes automatic changes scheduled to be made to the tree, such
440
# as updating its basis and unversioning paths that were missing.
441
self.work_tree.unversion(self.deleted_paths)
442
self._set_progress_stage("Updating the working tree")
443
self.work_tree.update_basis_by_delta(self.rev_id,
444
self.builder.get_basis_delta())
445
self.reporter.completed(new_revno, self.rev_id)
446
self._process_post_hooks(old_revno, new_revno)
475
449
def _update_branches(self, old_revno, old_revid, new_revno):
476
450
"""Update the master and local branch to the new revision.