/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/commit.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
    ui,
58
58
    )
59
59
from .branch import Branch
60
 
from .cleanup import OperationWithCleanups
 
60
from .cleanup import ExitStack
61
61
import breezy.config
62
62
from .errors import (BzrError,
63
63
                     ConflictsInTree,
64
64
                     StrictCommitFailed
65
65
                     )
66
66
from .osutils import (get_user_encoding,
 
67
                      has_symlinks,
67
68
                      is_inside_any,
68
69
                      minimum_path_selection,
69
70
                      )
70
71
from .trace import mutter, note, is_quiet
 
72
from .tree import TreeChange
71
73
from .urlutils import unescape_for_display
72
74
from .i18n import gettext
73
75
 
95
97
    :return: iter_changes function
96
98
    """
97
99
    for change in iter_changes:
98
 
        old_path = change[1][0]
99
 
        new_path = change[1][1]
100
 
 
101
 
        new_excluded = (new_path is not None and
102
 
                        is_inside_any(exclude, new_path))
103
 
 
104
 
        old_excluded = (old_path is not None and
105
 
                        is_inside_any(exclude, old_path))
 
100
        new_excluded = (change.path[1] is not None and
 
101
                        is_inside_any(exclude, change.path[1]))
 
102
 
 
103
        old_excluded = (change.path[0] is not None and
 
104
                        is_inside_any(exclude, change.path[0]))
106
105
 
107
106
        if old_excluded and new_excluded:
108
107
            continue
159
158
            unescape_for_display(location, 'utf-8'))
160
159
 
161
160
    def completed(self, revno, rev_id):
162
 
        self._note(gettext('Committed revision %d.'), revno)
163
 
        # self._note goes to the console too; so while we want to log the
164
 
        # rev_id, we can't trivially only log it. (See bug 526425). Long
165
 
        # term we should rearrange the reporting structure, but for now
166
 
        # we just mutter seperately. We mutter the revid and revno together
167
 
        # so that concurrent bzr invocations won't lead to confusion.
168
 
        mutter('Committed revid %s as revno %d.', rev_id, revno)
 
161
        if revno is not None:
 
162
            self._note(gettext('Committed revision %d.'), revno)
 
163
            # self._note goes to the console too; so while we want to log the
 
164
            # rev_id, we can't trivially only log it. (See bug 526425). Long
 
165
            # term we should rearrange the reporting structure, but for now
 
166
            # we just mutter seperately. We mutter the revid and revno together
 
167
            # so that concurrent bzr invocations won't lead to confusion.
 
168
            mutter('Committed revid %s as revno %d.', rev_id, revno)
 
169
        else:
 
170
            self._note(gettext('Committed revid %s.'), rev_id)
169
171
 
170
172
    def deleted(self, path):
171
173
        self._note(gettext('deleted %s'), path)
283
285
        :param lossy: When committing to a foreign VCS, ignore any
284
286
            data that can not be natively represented.
285
287
        """
286
 
        operation = OperationWithCleanups(self._commit)
287
 
        self.revprops = revprops or {}
288
 
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
289
 
        self.config_stack = config or self.config_stack
290
 
        return operation.run(
291
 
            message=message,
292
 
            timestamp=timestamp,
293
 
            timezone=timezone,
294
 
            committer=committer,
295
 
            specific_files=specific_files,
296
 
            rev_id=rev_id,
297
 
            allow_pointless=allow_pointless,
298
 
            strict=strict,
299
 
            verbose=verbose,
300
 
            working_tree=working_tree,
301
 
            local=local,
302
 
            reporter=reporter,
303
 
            message_callback=message_callback,
304
 
            recursive=recursive,
305
 
            exclude=exclude,
306
 
            possible_master_transports=possible_master_transports,
307
 
            lossy=lossy)
308
 
 
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):
313
 
        mutter('preparing to commit')
314
 
 
315
 
        if working_tree is None:
316
 
            raise BzrError("working_tree must be passed into commit().")
317
 
        else:
318
 
            self.work_tree = working_tree
319
 
            self.branch = self.work_tree.branch
320
 
            if getattr(self.work_tree, 'requires_rich_root', lambda: False)():
321
 
                if not self.branch.repository.supports_rich_root():
322
 
                    raise errors.RootNotRich()
323
 
        if message_callback is None:
324
 
            if message is not None:
325
 
                if isinstance(message, bytes):
326
 
                    message = message.decode(get_user_encoding())
327
 
 
328
 
                def message_callback(x):
329
 
                    return message
330
 
            else:
331
 
                raise BzrError("The message or message_callback keyword"
332
 
                               " parameter is required for commit().")
333
 
 
334
 
        self.bound_branch = None
335
 
        self.any_entries_deleted = False
336
 
        if exclude is not None:
337
 
            self.exclude = sorted(
338
 
                minimum_path_selection(exclude))
339
 
        else:
340
 
            self.exclude = []
341
 
        self.local = local
342
 
        self.master_branch = None
343
 
        self.recursive = recursive
344
 
        self.rev_id = None
345
 
        # self.specific_files is None to indicate no filter, or any iterable to
346
 
        # indicate a filter - [] means no files at all, as per iter_changes.
347
 
        if specific_files is not None:
348
 
            self.specific_files = sorted(
349
 
                minimum_path_selection(specific_files))
350
 
        else:
351
 
            self.specific_files = None
352
 
 
353
 
        self.allow_pointless = allow_pointless
354
 
        self.message_callback = message_callback
355
 
        self.timestamp = timestamp
356
 
        self.timezone = timezone
357
 
        self.committer = committer
358
 
        self.strict = strict
359
 
        self.verbose = verbose
360
 
 
361
 
        self.work_tree.lock_write()
362
 
        operation.add_cleanup(self.work_tree.unlock)
363
 
        self.parents = self.work_tree.get_parent_ids()
364
 
        self.pb = ui.ui_factory.nested_progress_bar()
365
 
        operation.add_cleanup(self.pb.finished)
366
 
        self.basis_revid = self.work_tree.last_revision()
367
 
        self.basis_tree = self.work_tree.basis_tree()
368
 
        self.basis_tree.lock_read()
369
 
        operation.add_cleanup(self.basis_tree.unlock)
370
 
        # Cannot commit with conflicts present.
371
 
        if len(self.work_tree.conflicts()) > 0:
372
 
            raise ConflictsInTree
373
 
 
374
 
        # Setup the bound branch variables as needed.
375
 
        self._check_bound_branch(operation, possible_master_transports)
376
 
 
377
 
        # Check that the working tree is up to date
378
 
        old_revno, old_revid, new_revno = self._check_out_of_date_tree()
379
 
 
380
 
        # Complete configuration setup
381
 
        if reporter is not None:
382
 
            self.reporter = reporter
383
 
        elif self.reporter is None:
384
 
            self.reporter = self._select_reporter()
385
 
        if self.config_stack is None:
386
 
            self.config_stack = self.work_tree.get_config_stack()
387
 
 
388
 
        # Setup the progress bar. As the number of files that need to be
389
 
        # committed in unknown, progress is reported as stages.
390
 
        # We keep track of entries separately though and include that
391
 
        # information in the progress bar during the relevant stages.
392
 
        self.pb_stage_name = ""
393
 
        self.pb_stage_count = 0
394
 
        self.pb_stage_total = 5
395
 
        if self.bound_branch:
396
 
            # 2 extra stages: "Uploading data to master branch" and "Merging
397
 
            # tags to master branch"
398
 
            self.pb_stage_total += 2
399
 
        self.pb.show_pct = False
400
 
        self.pb.show_spinner = False
401
 
        self.pb.show_eta = False
402
 
        self.pb.show_count = True
403
 
        self.pb.show_bar = True
404
 
 
405
 
        # After a merge, a selected file commit is not supported.
406
 
        # See 'bzr help merge' for an explanation as to why.
407
 
        if len(self.parents) > 1 and self.specific_files is not None:
408
 
            raise CannotCommitSelectedFileMerge(self.specific_files)
409
 
        # Excludes are a form of selected file commit.
410
 
        if len(self.parents) > 1 and self.exclude:
411
 
            raise CannotCommitSelectedFileMerge(self.exclude)
412
 
 
413
 
        # Collect the changes
414
 
        self._set_progress_stage("Collecting changes", counter=True)
415
 
        self._lossy = lossy
416
 
        self.builder = self.branch.get_commit_builder(
417
 
            self.parents, self.config_stack, timestamp, timezone, committer,
418
 
            self.revprops, rev_id, lossy=lossy)
419
 
 
420
 
        if self.builder.updates_branch and self.bound_branch:
421
 
            self.builder.abort()
422
 
            raise AssertionError(
423
 
                "bound branches not supported for commit builders "
424
 
                "that update the branch")
425
 
 
426
 
        try:
427
 
            # 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')
 
293
 
 
294
            if working_tree is None:
 
295
                raise BzrError("working_tree must be passed into commit().")
 
296
            else:
 
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())
 
306
 
 
307
                    def message_callback(x):
 
308
                        return message
 
309
                else:
 
310
                    raise BzrError("The message or message_callback keyword"
 
311
                                   " parameter is required for commit().")
 
312
 
 
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))
 
318
            else:
 
319
                self.exclude = []
 
320
            self.local = local
 
321
            self.master_branch = None
 
322
            self.recursive = recursive
 
323
            self.rev_id = None
 
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))
 
329
            else:
 
330
                self.specific_files = None
 
331
 
 
332
            self.allow_pointless = allow_pointless
 
333
            self.message_callback = message_callback
 
334
            self.timestamp = timestamp
 
335
            self.timezone = timezone
 
336
            self.committer = committer
 
337
            self.strict = strict
 
338
            self.verbose = verbose
 
339
 
 
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
 
350
 
 
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()
 
355
 
 
356
            # Check that the working tree is up to date
 
357
            old_revno, old_revid, new_revno = self._check_out_of_date_tree()
 
358
 
 
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()
 
364
 
 
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
428
372
            if self.bound_branch:
429
 
                master_location = self.master_branch.base
430
 
            else:
431
 
                master_location = self.branch.base
432
 
 
433
 
            # report the start of the commit
434
 
            self.reporter.started(new_revno, self.rev_id, master_location)
435
 
 
436
 
            self._update_builder_with_changes()
437
 
            self._check_pointless()
438
 
 
439
 
            # TODO: Now the new inventory is known, check for conflicts.
440
 
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
441
 
            # weave lines, because nothing should be recorded until it is known
442
 
            # that commit will succeed.
443
 
            self._set_progress_stage("Saving data locally")
444
 
            self.builder.finish_inventory()
445
 
 
446
 
            # Prompt the user for a commit message if none provided
447
 
            message = message_callback(self)
448
 
            self.message = message
449
 
 
450
 
            # Add revision data to the local branch
451
 
            self.rev_id = self.builder.commit(self.message)
452
 
 
453
 
        except Exception:
454
 
            mutter("aborting commit write group because of exception:")
455
 
            trace.log_exception_quietly()
456
 
            self.builder.abort()
457
 
            raise
458
 
 
459
 
        self._update_branches(old_revno, old_revid, new_revno)
460
 
 
461
 
        # Make the working tree be up to date with the branch. This
462
 
        # includes automatic changes scheduled to be made to the tree, such
463
 
        # as updating its basis and unversioning paths that were missing.
464
 
        self.work_tree.unversion(self.deleted_paths)
465
 
        self._set_progress_stage("Updating the working tree")
466
 
        self.work_tree.update_basis_by_delta(self.rev_id,
467
 
                                             self.builder.get_basis_delta())
468
 
        self.reporter.completed(new_revno, self.rev_id)
469
 
        self._process_post_hooks(old_revno, new_revno)
470
 
        return self.rev_id
 
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
 
381
 
 
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)
 
389
 
 
390
            # Collect the changes
 
391
            self._set_progress_stage("Collecting changes", counter=True)
 
392
            self._lossy = lossy
 
393
            self.builder = self.branch.get_commit_builder(
 
394
                self.parents, self.config_stack, timestamp, timezone, committer,
 
395
                self.revprops, rev_id, lossy=lossy)
 
396
 
 
397
            if self.builder.updates_branch and self.bound_branch:
 
398
                self.builder.abort()
 
399
                raise AssertionError(
 
400
                    "bound branches not supported for commit builders "
 
401
                    "that update the branch")
 
402
 
 
403
            try:
 
404
                # find the location being committed to
 
405
                if self.bound_branch:
 
406
                    master_location = self.master_branch.base
 
407
                else:
 
408
                    master_location = self.branch.base
 
409
 
 
410
                # report the start of the commit
 
411
                self.reporter.started(new_revno, self.rev_id, master_location)
 
412
 
 
413
                self._update_builder_with_changes()
 
414
                self._check_pointless()
 
415
 
 
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()
 
422
 
 
423
                # Prompt the user for a commit message if none provided
 
424
                message = message_callback(self)
 
425
                self.message = message
 
426
 
 
427
                # Add revision data to the local branch
 
428
                self.rev_id = self.builder.commit(self.message)
 
429
 
 
430
            except Exception:
 
431
                mutter("aborting commit write group because of exception:")
 
432
                trace.log_exception_quietly()
 
433
                self.builder.abort()
 
434
                raise
 
435
 
 
436
            self._update_branches(old_revno, old_revid, new_revno)
 
437
 
 
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)
 
447
            return self.rev_id
471
448
 
472
449
    def _update_branches(self, old_revno, old_revid, new_revno):
473
450
        """Update the master and local branch to the new revision.
495
472
                    self.branch.fetch(self.master_branch, self.rev_id)
496
473
 
497
474
            # and now do the commit locally.
 
475
            if new_revno is None:
 
476
                # Keep existing behaviour around ghosts
 
477
                new_revno = 1
498
478
            self.branch.set_last_revision_info(new_revno, self.rev_id)
499
479
        else:
500
480
            try:
531
511
            return
532
512
        raise PointlessCommit()
533
513
 
534
 
    def _check_bound_branch(self, operation, possible_master_transports=None):
 
514
    def _check_bound_branch(self, stack, possible_master_transports=None):
535
515
        """Check to see if the local branch is bound.
536
516
 
537
517
        If it is bound, then most of the commit will actually be
571
551
        # Now things are ready to change the master branch
572
552
        # so grab the lock
573
553
        self.bound_branch = self.branch
574
 
        self.master_branch.lock_write()
575
 
        operation.add_cleanup(self.master_branch.unlock)
 
554
        stack.enter_context(self.master_branch.lock_write())
576
555
 
577
556
    def _check_out_of_date_tree(self):
578
557
        """Check that the working tree is up to date.
588
567
            # - in a checkout scenario the tree may have no
589
568
            # parents but the branch may do.
590
569
            first_tree_parent = breezy.revision.NULL_REVISION
591
 
        try:
592
 
            old_revno, master_last = self.master_branch.last_revision_info()
593
 
        except errors.UnsupportedOperation:
 
570
        if (self.master_branch._format.stores_revno() or
 
571
                self.config_stack.get('calculate_revnos')):
 
572
            try:
 
573
                old_revno, master_last = self.master_branch.last_revision_info()
 
574
            except errors.UnsupportedOperation:
 
575
                master_last = self.master_branch.last_revision()
 
576
                old_revno = self.branch.revision_id_to_revno(master_last)
 
577
        else:
594
578
            master_last = self.master_branch.last_revision()
595
 
            old_revno = self.branch.revision_id_to_revno(master_last)
 
579
            old_revno = None
596
580
        if master_last != first_tree_parent:
597
581
            if master_last != breezy.revision.NULL_REVISION:
598
582
                raise errors.OutOfDateTree(self.work_tree)
599
 
        if self.branch.repository.has_revision(first_tree_parent):
 
583
        if (old_revno is not None and
 
584
                self.branch.repository.has_revision(first_tree_parent)):
600
585
            new_revno = old_revno + 1
601
586
        else:
602
587
            # ghost parents never appear in revision history.
603
 
            new_revno = 1
 
588
            new_revno = None
604
589
        return old_revno, master_last, new_revno
605
590
 
606
591
    def _process_pre_hooks(self, old_revno, new_revno):
700
685
        deleted_paths = []
701
686
        for change in iter_changes:
702
687
            if report_changes:
703
 
                old_path = change[1][0]
704
 
                new_path = change[1][1]
705
 
                versioned = change[3][1]
706
 
            kind = change[6][1]
707
 
            versioned = change[3][1]
 
688
                old_path = change.path[0]
 
689
                new_path = change.path[1]
 
690
                versioned = change.versioned[1]
 
691
            kind = change.kind[1]
 
692
            versioned = change.versioned[1]
708
693
            if kind is None and versioned:
709
694
                # 'missing' path
710
695
                if report_changes:
711
696
                    reporter.missing(new_path)
712
 
                deleted_paths.append(change[1][1])
 
697
                if change.kind[0] == 'symlink' and not self.work_tree.supports_symlinks():
 
698
                    trace.warning('Ignoring "%s" as symlinks are not '
 
699
                                  'supported on this filesystem.' % (change.path[0],))
 
700
                    continue
 
701
                deleted_paths.append(change.path[1])
713
702
                # Reset the new path (None) and new versioned flag (False)
714
 
                change = (change[0], (change[1][0], None), change[2],
715
 
                          (change[3][0], False)) + change[4:]
716
 
                new_path = change[1][1]
 
703
                change = change.discard_new()
 
704
                new_path = change.path[1]
717
705
                versioned = False
718
706
            elif kind == 'tree-reference':
719
707
                if self.recursive == 'down':
720
 
                    self._commit_nested_tree(change[1][1])
721
 
            if change[3][0] or change[3][1]:
 
708
                    self._commit_nested_tree(change.path[1])
 
709
            if change.versioned[0] or change.versioned[1]:
722
710
                yield change
723
711
                if report_changes:
724
712
                    if new_path is None: