~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2009-07-17 10:38:41 UTC
  • mfrom: (4536 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4558.
  • Revision ID: mbp@sourcefrog.net-20090717103841-z35onk04bkiw7zb6
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
    revision as _mod_revision,
46
46
    symbol_versioning,
47
47
    transport,
48
 
    tree as _mod_tree,
49
48
    ui,
50
49
    urlutils,
51
50
    views,
450
449
        except errors.NoWorkingTree:
451
450
            raise errors.BzrCommandError("No working tree to remove")
452
451
        except errors.NotLocalUrl:
453
 
            raise errors.BzrCommandError("You cannot remove the working tree of a "
454
 
                                         "remote path")
 
452
            raise errors.BzrCommandError("You cannot remove the working tree"
 
453
                                         " of a remote path")
455
454
        if not force:
456
 
            changes = working.changes_from(working.basis_tree())
457
 
            if changes.has_changed():
 
455
            # XXX: What about pending merges ? -- vila 20090629
 
456
            if working.has_changes(working.basis_tree()):
458
457
                raise errors.UncommittedChanges(working)
459
458
 
460
459
        working_path = working.bzrdir.root_transport.base
461
460
        branch_path = working.branch.bzrdir.root_transport.base
462
461
        if working_path != branch_path:
463
 
            raise errors.BzrCommandError("You cannot remove the working tree from "
464
 
                                         "a lightweight checkout")
 
462
            raise errors.BzrCommandError("You cannot remove the working tree"
 
463
                                         " from a lightweight checkout")
465
464
 
466
465
        d.destroy_workingtree()
467
466
 
474
473
 
475
474
    _see_also = ['info']
476
475
    takes_args = ['location?']
 
476
    takes_options = [
 
477
        Option('tree', help='Show revno of working tree'),
 
478
        ]
477
479
 
478
480
    @display_command
479
 
    def run(self, location=u'.'):
480
 
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
481
 
        self.outf.write('\n')
 
481
    def run(self, tree=False, location=u'.'):
 
482
        if tree:
 
483
            try:
 
484
                wt = WorkingTree.open_containing(location)[0]
 
485
                wt.lock_read()
 
486
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
487
                raise errors.NoWorkingTree(location)
 
488
            try:
 
489
                revid = wt.last_revision()
 
490
                try:
 
491
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
492
                except errors.NoSuchRevision:
 
493
                    revno_t = ('???',)
 
494
                revno = ".".join(str(n) for n in revno_t)
 
495
            finally:
 
496
                wt.unlock()
 
497
        else:
 
498
            b = Branch.open_containing(location)[0]
 
499
            b.lock_read()
 
500
            try:
 
501
                revno = b.revno()
 
502
            finally:
 
503
                b.unlock()
 
504
 
 
505
        self.outf.write(str(revno) + '\n')
482
506
 
483
507
 
484
508
class cmd_revision_info(Command):
494
518
            short_name='d',
495
519
            type=unicode,
496
520
            ),
 
521
        Option('tree', help='Show revno of working tree'),
497
522
        ]
498
523
 
499
524
    @display_command
500
 
    def run(self, revision=None, directory=u'.', revision_info_list=[]):
501
 
 
502
 
        revs = []
503
 
        if revision is not None:
504
 
            revs.extend(revision)
505
 
        if revision_info_list is not None:
506
 
            for rev in revision_info_list:
507
 
                revs.append(RevisionSpec.from_string(rev))
508
 
 
509
 
        b = Branch.open_containing(directory)[0]
510
 
 
511
 
        if len(revs) == 0:
512
 
            revs.append(RevisionSpec.from_string('-1'))
513
 
 
514
 
        for rev in revs:
515
 
            revision_id = rev.as_revision_id(b)
516
 
            try:
517
 
                revno = '%4d' % (b.revision_id_to_revno(revision_id))
518
 
            except errors.NoSuchRevision:
519
 
                dotted_map = b.get_revision_id_to_revno_map()
520
 
                revno = '.'.join(str(i) for i in dotted_map[revision_id])
521
 
            print '%s %s' % (revno, revision_id)
 
525
    def run(self, revision=None, directory=u'.', tree=False,
 
526
            revision_info_list=[]):
 
527
 
 
528
        try:
 
529
            wt = WorkingTree.open_containing(directory)[0]
 
530
            b = wt.branch
 
531
            wt.lock_read()
 
532
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
533
            wt = None
 
534
            b = Branch.open_containing(directory)[0]
 
535
            b.lock_read()
 
536
        try:
 
537
            revision_ids = []
 
538
            if revision is not None:
 
539
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
540
            if revision_info_list is not None:
 
541
                for rev_str in revision_info_list:
 
542
                    rev_spec = RevisionSpec.from_string(rev_str)
 
543
                    revision_ids.append(rev_spec.as_revision_id(b))
 
544
            # No arguments supplied, default to the last revision
 
545
            if len(revision_ids) == 0:
 
546
                if tree:
 
547
                    if wt is None:
 
548
                        raise errors.NoWorkingTree(directory)
 
549
                    revision_ids.append(wt.last_revision())
 
550
                else:
 
551
                    revision_ids.append(b.last_revision())
 
552
 
 
553
            revinfos = []
 
554
            maxlen = 0
 
555
            for revision_id in revision_ids:
 
556
                try:
 
557
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
558
                    revno = '.'.join(str(i) for i in dotted_revno)
 
559
                except errors.NoSuchRevision:
 
560
                    revno = '???'
 
561
                maxlen = max(maxlen, len(revno))
 
562
                revinfos.append([revno, revision_id])
 
563
        finally:
 
564
            if wt is None:
 
565
                b.unlock()
 
566
            else:
 
567
                wt.unlock()
 
568
 
 
569
        for ri in revinfos:
 
570
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
522
571
 
523
572
 
524
573
class cmd_add(Command):
1028
1077
                'for the commit history. Only the work not present in the '
1029
1078
                'referenced branch is included in the branch created.',
1030
1079
            type=unicode),
 
1080
        Option('strict',
 
1081
               help='Refuse to push if there are uncommitted changes in'
 
1082
               ' the working tree, --no-strict disables the check.'),
1031
1083
        ]
1032
1084
    takes_args = ['location?']
1033
1085
    encoding_type = 'replace'
1035
1087
    def run(self, location=None, remember=False, overwrite=False,
1036
1088
        create_prefix=False, verbose=False, revision=None,
1037
1089
        use_existing_dir=False, directory=None, stacked_on=None,
1038
 
        stacked=False):
 
1090
        stacked=False, strict=None):
1039
1091
        from bzrlib.push import _show_push_branch
1040
1092
 
1041
 
        # Get the source branch and revision_id
1042
1093
        if directory is None:
1043
1094
            directory = '.'
1044
 
        br_from = Branch.open_containing(directory)[0]
 
1095
        # Get the source branch
 
1096
        (tree, br_from,
 
1097
         _unused) = bzrdir.BzrDir.open_containing_tree_or_branch(directory)
 
1098
        if strict is None:
 
1099
            strict = br_from.get_config().get_user_option('push_strict')
 
1100
            if strict is not None:
 
1101
                # FIXME: This should be better supported by config
 
1102
                # -- vila 20090611
 
1103
                bools = dict(yes=True, no=False, on=True, off=False,
 
1104
                             true=True, false=False)
 
1105
                try:
 
1106
                    strict = bools[strict.lower()]
 
1107
                except KeyError:
 
1108
                    strict = None
 
1109
        # Get the tip's revision_id
1045
1110
        revision = _get_one_revision('push', revision)
1046
1111
        if revision is not None:
1047
1112
            revision_id = revision.in_history(br_from).rev_id
1048
1113
        else:
1049
1114
            revision_id = None
 
1115
        if (tree is not None and revision_id is None
 
1116
            and (strict is None or strict)): # Default to True:
 
1117
            if (tree.has_changes(tree.basis_tree())
 
1118
                 or len(tree.get_parent_ids()) > 1):
 
1119
                raise errors.UncommittedChanges(
 
1120
                    tree, more='Use --no-strict to force the push.')
 
1121
            if tree.last_revision() != tree.branch.last_revision():
 
1122
                # The tree has lost sync with its branch, there is little
 
1123
                # chance that the user is aware of it but he can still force
 
1124
                # the push with --no-strict
 
1125
                raise errors.OutOfDateTree(
 
1126
                    tree, more='Use --no-strict to force the push.')
1050
1127
 
1051
1128
        # Get the stacked_on branch, if any
1052
1129
        if stacked_on is not None:
1110
1187
                'branch for all operations.'),
1111
1188
        Option('standalone',
1112
1189
               help='Do not use a shared repository, even if available.'),
 
1190
        Option('use-existing-dir',
 
1191
               help='By default branch will fail if the target'
 
1192
                    ' directory exists, but does not already'
 
1193
                    ' have a control directory.  This flag will'
 
1194
                    ' allow branch to proceed.'),
1113
1195
        ]
1114
1196
    aliases = ['get', 'clone']
1115
1197
 
1116
1198
    def run(self, from_location, to_location=None, revision=None,
1117
 
            hardlink=False, stacked=False, standalone=False, no_tree=False):
 
1199
            hardlink=False, stacked=False, standalone=False, no_tree=False,
 
1200
            use_existing_dir=False):
1118
1201
        from bzrlib.tag import _merge_tags_if_possible
1119
1202
 
1120
1203
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1138
1221
            try:
1139
1222
                to_transport.mkdir('.')
1140
1223
            except errors.FileExists:
1141
 
                raise errors.BzrCommandError('Target directory "%s" already'
1142
 
                                             ' exists.' % to_location)
 
1224
                if not use_existing_dir:
 
1225
                    raise errors.BzrCommandError('Target directory "%s" '
 
1226
                        'already exists.' % to_location)
 
1227
                else:
 
1228
                    try:
 
1229
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1230
                    except errors.NotBranchError:
 
1231
                        pass
 
1232
                    else:
 
1233
                        raise errors.AlreadyBranchError(to_location)
1143
1234
            except errors.NoSuchFile:
1144
1235
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1145
1236
                                             % to_location)
1631
1722
                branch.set_append_revisions_only(True)
1632
1723
            except errors.UpgradeRequired:
1633
1724
                raise errors.BzrCommandError('This branch format cannot be set'
1634
 
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1725
                    ' to append-revisions-only.  Try --default.')
1635
1726
        if not is_quiet():
1636
1727
            from bzrlib.info import describe_layout, describe_format
1637
1728
            try:
2376
2467
 
2377
2468
        if path is None:
2378
2469
            fs_path = '.'
2379
 
            prefix = ''
2380
2470
        else:
2381
2471
            if from_root:
2382
2472
                raise errors.BzrCommandError('cannot specify both --from-root'
2383
2473
                                             ' and PATH')
2384
2474
            fs_path = path
2385
 
            prefix = path
2386
2475
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2387
2476
            fs_path)
 
2477
 
 
2478
        # Calculate the prefix to use
 
2479
        prefix = None
2388
2480
        if from_root:
2389
 
            relpath = u''
2390
 
        elif relpath:
2391
 
            relpath += '/'
 
2481
            if relpath:
 
2482
                prefix = relpath + '/'
 
2483
        elif fs_path != '.':
 
2484
            prefix = fs_path + '/'
 
2485
 
2392
2486
        if revision is not None or tree is None:
2393
2487
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2394
2488
 
2402
2496
 
2403
2497
        tree.lock_read()
2404
2498
        try:
2405
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2406
 
                if fp.startswith(relpath):
2407
 
                    rp = fp[len(relpath):]
2408
 
                    fp = osutils.pathjoin(prefix, rp)
2409
 
                    if not recursive and '/' in rp:
2410
 
                        continue
2411
 
                    if not all and not selection[fc]:
2412
 
                        continue
2413
 
                    if kind is not None and fkind != kind:
2414
 
                        continue
2415
 
                    if apply_view:
2416
 
                        try:
2417
 
                            views.check_path_in_view(tree, fp)
2418
 
                        except errors.FileOutsideView:
2419
 
                            continue
2420
 
                    kindch = entry.kind_character()
2421
 
                    outstring = fp + kindch
2422
 
                    ui.ui_factory.clear_term()
2423
 
                    if verbose:
2424
 
                        outstring = '%-8s %s' % (fc, outstring)
2425
 
                        if show_ids and fid is not None:
2426
 
                            outstring = "%-50s %s" % (outstring, fid)
2427
 
                        self.outf.write(outstring + '\n')
2428
 
                    elif null:
2429
 
                        self.outf.write(fp + '\0')
2430
 
                        if show_ids:
2431
 
                            if fid is not None:
2432
 
                                self.outf.write(fid)
2433
 
                            self.outf.write('\0')
2434
 
                        self.outf.flush()
2435
 
                    else:
 
2499
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2500
                from_dir=relpath, recursive=recursive):
 
2501
                # Apply additional masking
 
2502
                if not all and not selection[fc]:
 
2503
                    continue
 
2504
                if kind is not None and fkind != kind:
 
2505
                    continue
 
2506
                if apply_view:
 
2507
                    try:
 
2508
                        if relpath:
 
2509
                            fullpath = osutils.pathjoin(relpath, fp)
 
2510
                        else:
 
2511
                            fullpath = fp
 
2512
                        views.check_path_in_view(tree, fullpath)
 
2513
                    except errors.FileOutsideView:
 
2514
                        continue
 
2515
 
 
2516
                # Output the entry
 
2517
                if prefix:
 
2518
                    fp = osutils.pathjoin(prefix, fp)
 
2519
                kindch = entry.kind_character()
 
2520
                outstring = fp + kindch
 
2521
                ui.ui_factory.clear_term()
 
2522
                if verbose:
 
2523
                    outstring = '%-8s %s' % (fc, outstring)
 
2524
                    if show_ids and fid is not None:
 
2525
                        outstring = "%-50s %s" % (outstring, fid)
 
2526
                    self.outf.write(outstring + '\n')
 
2527
                elif null:
 
2528
                    self.outf.write(fp + '\0')
 
2529
                    if show_ids:
 
2530
                        if fid is not None:
 
2531
                            self.outf.write(fid)
 
2532
                        self.outf.write('\0')
 
2533
                    self.outf.flush()
 
2534
                else:
 
2535
                    if show_ids:
2436
2536
                        if fid is not None:
2437
2537
                            my_id = fid
2438
2538
                        else:
2439
2539
                            my_id = ''
2440
 
                        if show_ids:
2441
 
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
2442
 
                        else:
2443
 
                            self.outf.write(outstring + '\n')
 
2540
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2541
                    else:
 
2542
                        self.outf.write(outstring + '\n')
2444
2543
        finally:
2445
2544
            tree.unlock()
2446
2545
 
2970
3069
    The working tree and branch checks will only give output if a problem is
2971
3070
    detected. The output fields of the repository check are:
2972
3071
 
2973
 
        revisions: This is just the number of revisions checked.  It doesn't
2974
 
            indicate a problem.
2975
 
        versionedfiles: This is just the number of versionedfiles checked.  It
2976
 
            doesn't indicate a problem.
2977
 
        unreferenced ancestors: Texts that are ancestors of other texts, but
2978
 
            are not properly referenced by the revision ancestry.  This is a
2979
 
            subtle problem that Bazaar can work around.
2980
 
        unique file texts: This is the total number of unique file contents
2981
 
            seen in the checked revisions.  It does not indicate a problem.
2982
 
        repeated file texts: This is the total number of repeated texts seen
2983
 
            in the checked revisions.  Texts can be repeated when their file
2984
 
            entries are modified, but the file contents are not.  It does not
2985
 
            indicate a problem.
 
3072
    revisions
 
3073
        This is just the number of revisions checked.  It doesn't
 
3074
        indicate a problem.
 
3075
 
 
3076
    versionedfiles
 
3077
        This is just the number of versionedfiles checked.  It
 
3078
        doesn't indicate a problem.
 
3079
 
 
3080
    unreferenced ancestors
 
3081
        Texts that are ancestors of other texts, but
 
3082
        are not properly referenced by the revision ancestry.  This is a
 
3083
        subtle problem that Bazaar can work around.
 
3084
 
 
3085
    unique file texts
 
3086
        This is the total number of unique file contents
 
3087
        seen in the checked revisions.  It does not indicate a problem.
 
3088
 
 
3089
    repeated file texts
 
3090
        This is the total number of repeated texts seen
 
3091
        in the checked revisions.  Texts can be repeated when their file
 
3092
        entries are modified, but the file contents are not.  It does not
 
3093
        indicate a problem.
2986
3094
 
2987
3095
    If no restrictions are specified, all Bazaar data that is found at the given
2988
3096
    location will be checked.
3465
3573
    merge refuses to run if there are any uncommitted changes, unless
3466
3574
    --force is given.
3467
3575
 
 
3576
    To select only some changes to merge, use "merge -i", which will prompt
 
3577
    you to apply each diff hunk and file change, similar to "shelve".
 
3578
 
3468
3579
    :Examples:
3469
3580
        To merge the latest revision from bzr.dev::
3470
3581
 
3508
3619
               short_name='d',
3509
3620
               type=unicode,
3510
3621
               ),
3511
 
        Option('preview', help='Instead of merging, show a diff of the merge.')
 
3622
        Option('preview', help='Instead of merging, show a diff of the'
 
3623
               ' merge.'),
 
3624
        Option('interactive', help='Select changes interactively.',
 
3625
            short_name='i')
3512
3626
    ]
3513
3627
 
3514
3628
    def run(self, location=None, revision=None, force=False,
3516
3630
            uncommitted=False, pull=False,
3517
3631
            directory=None,
3518
3632
            preview=False,
 
3633
            interactive=False,
3519
3634
            ):
3520
3635
        if merge_type is None:
3521
3636
            merge_type = _mod_merge.Merge3Merger
3533
3648
        except errors.NoSuchRevision:
3534
3649
            basis_tree = tree.basis_tree()
3535
3650
        if not force:
3536
 
            changes = tree.changes_from(basis_tree)
3537
 
            if changes.has_changed():
 
3651
            if tree.has_changes(basis_tree):
3538
3652
                raise errors.UncommittedChanges(tree)
3539
3653
 
3540
3654
        view_info = _get_view_info_for_change_reporter(tree)
3567
3681
                if revision is not None and len(revision) > 0:
3568
3682
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3569
3683
                        ' --revision at the same time.')
3570
 
                location = self._select_branch_location(tree, location)[0]
3571
 
                other_tree, other_path = WorkingTree.open_containing(location)
3572
 
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
3573
 
                    pb)
 
3684
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3685
                                                          cleanups)
3574
3686
                allow_pending = False
3575
 
                if other_path != '':
3576
 
                    merger.interesting_files = [other_path]
3577
3687
 
3578
3688
            if merger is None:
3579
3689
                merger, allow_pending = self._get_merger_from_branch(tree,
3597
3707
                    return 0
3598
3708
            merger.check_basis(False)
3599
3709
            if preview:
3600
 
                return self._do_preview(merger)
 
3710
                return self._do_preview(merger, cleanups)
 
3711
            elif interactive:
 
3712
                return self._do_interactive(merger, cleanups)
3601
3713
            else:
3602
3714
                return self._do_merge(merger, change_reporter, allow_pending,
3603
3715
                                      verified)
3605
3717
            for cleanup in reversed(cleanups):
3606
3718
                cleanup()
3607
3719
 
3608
 
    def _do_preview(self, merger):
3609
 
        from bzrlib.diff import show_diff_trees
 
3720
    def _get_preview(self, merger, cleanups):
3610
3721
        tree_merger = merger.make_merger()
3611
3722
        tt = tree_merger.make_preview_transform()
3612
 
        try:
3613
 
            result_tree = tt.get_preview_tree()
3614
 
            show_diff_trees(merger.this_tree, result_tree, self.outf,
3615
 
                            old_label='', new_label='')
3616
 
        finally:
3617
 
            tt.finalize()
 
3723
        cleanups.append(tt.finalize)
 
3724
        result_tree = tt.get_preview_tree()
 
3725
        return result_tree
 
3726
 
 
3727
    def _do_preview(self, merger, cleanups):
 
3728
        from bzrlib.diff import show_diff_trees
 
3729
        result_tree = self._get_preview(merger, cleanups)
 
3730
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3731
                        old_label='', new_label='')
3618
3732
 
3619
3733
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3620
3734
        merger.change_reporter = change_reporter
3628
3742
        else:
3629
3743
            return 0
3630
3744
 
 
3745
    def _do_interactive(self, merger, cleanups):
 
3746
        """Perform an interactive merge.
 
3747
 
 
3748
        This works by generating a preview tree of the merge, then using
 
3749
        Shelver to selectively remove the differences between the working tree
 
3750
        and the preview tree.
 
3751
        """
 
3752
        from bzrlib import shelf_ui
 
3753
        result_tree = self._get_preview(merger, cleanups)
 
3754
        writer = bzrlib.option.diff_writer_registry.get()
 
3755
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
3756
                                   reporter=shelf_ui.ApplyReporter(),
 
3757
                                   diff_writer=writer(sys.stdout))
 
3758
        shelver.run()
 
3759
 
3631
3760
    def sanity_check_merger(self, merger):
3632
3761
        if (merger.show_base and
3633
3762
            not merger.merge_type is _mod_merge.Merge3Merger):
3692
3821
            allow_pending = True
3693
3822
        return merger, allow_pending
3694
3823
 
 
3824
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3825
        """Get a merger for uncommitted changes.
 
3826
 
 
3827
        :param tree: The tree the merger should apply to.
 
3828
        :param location: The location containing uncommitted changes.
 
3829
        :param pb: The progress bar to use for showing progress.
 
3830
        :param cleanups: A list of operations to perform to clean up the
 
3831
            temporary directories, unfinalized objects, etc.
 
3832
        """
 
3833
        location = self._select_branch_location(tree, location)[0]
 
3834
        other_tree, other_path = WorkingTree.open_containing(location)
 
3835
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
3836
        if other_path != '':
 
3837
            merger.interesting_files = [other_path]
 
3838
        return merger
 
3839
 
3695
3840
    def _select_branch_location(self, tree, user_location, revision=None,
3696
3841
                                index=None):
3697
3842
        """Select a branch location, according to possible inputs.
4850
4995
               help='Write merge directive to this file; '
4851
4996
                    'use - for stdout.',
4852
4997
               type=unicode),
 
4998
        Option('strict',
 
4999
               help='Refuse to send if there are uncommitted changes in'
 
5000
               ' the working tree, --no-strict disables the check.'),
4853
5001
        Option('mail-to', help='Mail the request to this address.',
4854
5002
               type=unicode),
4855
5003
        'revision',
4856
5004
        'message',
4857
5005
        Option('body', help='Body for the email.', type=unicode),
4858
5006
        RegistryOption('format',
4859
 
                       help='Use the specified output format.', 
4860
 
                       lazy_registry=('bzrlib.send', 'format_registry'))
 
5007
                       help='Use the specified output format.',
 
5008
                       lazy_registry=('bzrlib.send', 'format_registry')),
4861
5009
        ]
4862
5010
 
4863
5011
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4864
5012
            no_patch=False, revision=None, remember=False, output=None,
4865
 
            format=None, mail_to=None, message=None, body=None, **kwargs):
 
5013
            format=None, mail_to=None, message=None, body=None,
 
5014
            strict=None, **kwargs):
4866
5015
        from bzrlib.send import send
4867
5016
        return send(submit_branch, revision, public_branch, remember,
4868
 
                         format, no_bundle, no_patch, output,
4869
 
                         kwargs.get('from', '.'), mail_to, message, body,
4870
 
                         self.outf)
 
5017
                    format, no_bundle, no_patch, output,
 
5018
                    kwargs.get('from', '.'), mail_to, message, body,
 
5019
                    self.outf,
 
5020
                    strict=strict)
4871
5021
 
4872
5022
 
4873
5023
class cmd_bundle_revisions(cmd_send):
4917
5067
               type=unicode),
4918
5068
        Option('output', short_name='o', help='Write directive to this file.',
4919
5069
               type=unicode),
 
5070
        Option('strict',
 
5071
               help='Refuse to bundle revisions if there are uncommitted'
 
5072
               ' changes in the working tree, --no-strict disables the check.'),
4920
5073
        'revision',
4921
5074
        RegistryOption('format',
4922
5075
                       help='Use the specified output format.',
4930
5083
 
4931
5084
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4932
5085
            no_patch=False, revision=None, remember=False, output=None,
4933
 
            format=None, **kwargs):
 
5086
            format=None, strict=None, **kwargs):
4934
5087
        if output is None:
4935
5088
            output = '-'
4936
5089
        from bzrlib.send import send
4937
5090
        return send(submit_branch, revision, public_branch, remember,
4938
5091
                         format, no_bundle, no_patch, output,
4939
5092
                         kwargs.get('from', '.'), None, None, None,
4940
 
                         self.outf)
 
5093
                         self.outf, strict=strict)
4941
5094
 
4942
5095
 
4943
5096
class cmd_tag(Command):
5171
5324
 
5172
5325
    takes_args = ['to_location']
5173
5326
    takes_options = [Option('force',
5174
 
                        help='Switch even if local commits will be lost.')
 
5327
                        help='Switch even if local commits will be lost.'),
 
5328
                     Option('create-branch', short_name='b',
 
5329
                        help='Create the target branch from this one before'
 
5330
                             ' switching to it.'),
5175
5331
                     ]
5176
5332
 
5177
 
    def run(self, to_location, force=False):
 
5333
    def run(self, to_location, force=False, create_branch=False):
5178
5334
        from bzrlib import switch
5179
5335
        tree_location = '.'
5180
5336
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5182
5338
            branch = control_dir.open_branch()
5183
5339
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5184
5340
        except errors.NotBranchError:
 
5341
            branch = None
5185
5342
            had_explicit_nick = False
5186
 
        try:
5187
 
            to_branch = Branch.open(to_location)
5188
 
        except errors.NotBranchError:
5189
 
            this_url = self._get_branch_location(control_dir)
5190
 
            to_branch = Branch.open(
5191
 
                urlutils.join(this_url, '..', to_location))
 
5343
        if create_branch:
 
5344
            if branch is None:
 
5345
                raise errors.BzrCommandError('cannot create branch without'
 
5346
                                             ' source branch')
 
5347
            if '/' not in to_location and '\\' not in to_location:
 
5348
                # This path is meant to be relative to the existing branch
 
5349
                this_url = self._get_branch_location(control_dir)
 
5350
                to_location = urlutils.join(this_url, '..', to_location)
 
5351
            to_branch = branch.bzrdir.sprout(to_location,
 
5352
                                 possible_transports=[branch.bzrdir.root_transport],
 
5353
                                 source_branch=branch).open_branch()
 
5354
            # try:
 
5355
            #     from_branch = control_dir.open_branch()
 
5356
            # except errors.NotBranchError:
 
5357
            #     raise BzrCommandError('Cannot create a branch from this'
 
5358
            #         ' location when we cannot open this branch')
 
5359
            # from_branch.bzrdir.sprout(
 
5360
            pass
 
5361
        else:
 
5362
            try:
 
5363
                to_branch = Branch.open(to_location)
 
5364
            except errors.NotBranchError:
 
5365
                this_url = self._get_branch_location(control_dir)
 
5366
                to_branch = Branch.open(
 
5367
                    urlutils.join(this_url, '..', to_location))
5192
5368
        switch.switch(control_dir, to_branch, force)
5193
5369
        if had_explicit_nick:
5194
5370
            branch = control_dir.open_branch() #get the new branch!