~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: John Arbash Meinel
  • Date: 2009-07-29 21:35:05 UTC
  • mfrom: (4576 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4577.
  • Revision ID: john@arbash-meinel.com-20090729213505-tkqsvy1zfpocu75w
Merge bzr.dev 4576 in prep for NEWS

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):
750
799
        if len(names_list) < 2:
751
800
            raise errors.BzrCommandError("missing file argument")
752
801
        tree, rel_names = tree_files(names_list, canonicalize=False)
753
 
        tree.lock_write()
 
802
        tree.lock_tree_write()
754
803
        try:
755
804
            self._run(tree, names_list, rel_names, after)
756
805
        finally:
764
813
            raise errors.BzrCommandError('--after cannot be specified with'
765
814
                                         ' --auto.')
766
815
        work_tree, file_list = tree_files(names_list, default_branch='.')
767
 
        work_tree.lock_write()
 
816
        work_tree.lock_tree_write()
768
817
        try:
769
818
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
770
819
        finally:
946
995
            if branch_to.get_parent() is None or remember:
947
996
                branch_to.set_parent(branch_from.base)
948
997
 
949
 
        if revision is not None:
950
 
            revision_id = revision.as_revision_id(branch_from)
951
 
 
952
 
        branch_to.lock_write()
 
998
        if branch_from is not branch_to:
 
999
            branch_from.lock_read()
953
1000
        try:
954
 
            if tree_to is not None:
955
 
                view_info = _get_view_info_for_change_reporter(tree_to)
956
 
                change_reporter = delta._ChangeReporter(
957
 
                    unversioned_filter=tree_to.is_ignored, view_info=view_info)
958
 
                result = tree_to.pull(branch_from, overwrite, revision_id,
959
 
                                      change_reporter,
960
 
                                      possible_transports=possible_transports,
961
 
                                      local=local)
962
 
            else:
963
 
                result = branch_to.pull(branch_from, overwrite, revision_id,
964
 
                                      local=local)
965
 
 
966
 
            result.report(self.outf)
967
 
            if verbose and result.old_revid != result.new_revid:
968
 
                log.show_branch_change(branch_to, self.outf, result.old_revno,
969
 
                                       result.old_revid)
 
1001
            if revision is not None:
 
1002
                revision_id = revision.as_revision_id(branch_from)
 
1003
 
 
1004
            branch_to.lock_write()
 
1005
            try:
 
1006
                if tree_to is not None:
 
1007
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1008
                    change_reporter = delta._ChangeReporter(
 
1009
                        unversioned_filter=tree_to.is_ignored,
 
1010
                        view_info=view_info)
 
1011
                    result = tree_to.pull(
 
1012
                        branch_from, overwrite, revision_id, change_reporter,
 
1013
                        possible_transports=possible_transports, local=local)
 
1014
                else:
 
1015
                    result = branch_to.pull(
 
1016
                        branch_from, overwrite, revision_id, local=local)
 
1017
 
 
1018
                result.report(self.outf)
 
1019
                if verbose and result.old_revid != result.new_revid:
 
1020
                    log.show_branch_change(
 
1021
                        branch_to, self.outf, result.old_revno,
 
1022
                        result.old_revid)
 
1023
            finally:
 
1024
                branch_to.unlock()
970
1025
        finally:
971
 
            branch_to.unlock()
 
1026
            if branch_from is not branch_to:
 
1027
                branch_from.unlock()
972
1028
 
973
1029
 
974
1030
class cmd_push(Command):
1021
1077
                'for the commit history. Only the work not present in the '
1022
1078
                'referenced branch is included in the branch created.',
1023
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.'),
1024
1083
        ]
1025
1084
    takes_args = ['location?']
1026
1085
    encoding_type = 'replace'
1028
1087
    def run(self, location=None, remember=False, overwrite=False,
1029
1088
        create_prefix=False, verbose=False, revision=None,
1030
1089
        use_existing_dir=False, directory=None, stacked_on=None,
1031
 
        stacked=False):
 
1090
        stacked=False, strict=None):
1032
1091
        from bzrlib.push import _show_push_branch
1033
1092
 
1034
 
        # Get the source branch and revision_id
1035
1093
        if directory is None:
1036
1094
            directory = '.'
1037
 
        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_as_bool('push_strict')
 
1100
        if strict is None: strict = True # default value
 
1101
        # Get the tip's revision_id
1038
1102
        revision = _get_one_revision('push', revision)
1039
1103
        if revision is not None:
1040
1104
            revision_id = revision.in_history(br_from).rev_id
1041
1105
        else:
1042
1106
            revision_id = None
 
1107
        if strict and tree is not None and revision_id is None:
 
1108
            if (tree.has_changes(tree.basis_tree())
 
1109
                or len(tree.get_parent_ids()) > 1):
 
1110
                raise errors.UncommittedChanges(
 
1111
                    tree, more='Use --no-strict to force the push.')
 
1112
            if tree.last_revision() != tree.branch.last_revision():
 
1113
                # The tree has lost sync with its branch, there is little
 
1114
                # chance that the user is aware of it but he can still force
 
1115
                # the push with --no-strict
 
1116
                raise errors.OutOfDateTree(
 
1117
                    tree, more='Use --no-strict to force the push.')
1043
1118
 
1044
1119
        # Get the stacked_on branch, if any
1045
1120
        if stacked_on is not None:
1078
1153
 
1079
1154
 
1080
1155
class cmd_branch(Command):
1081
 
    """Create a new copy of a branch.
 
1156
    """Create a new branch that is a copy of an existing branch.
1082
1157
 
1083
1158
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
1084
1159
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
1103
1178
                'branch for all operations.'),
1104
1179
        Option('standalone',
1105
1180
               help='Do not use a shared repository, even if available.'),
 
1181
        Option('use-existing-dir',
 
1182
               help='By default branch will fail if the target'
 
1183
                    ' directory exists, but does not already'
 
1184
                    ' have a control directory.  This flag will'
 
1185
                    ' allow branch to proceed.'),
1106
1186
        ]
1107
1187
    aliases = ['get', 'clone']
1108
1188
 
1109
1189
    def run(self, from_location, to_location=None, revision=None,
1110
 
            hardlink=False, stacked=False, standalone=False, no_tree=False):
 
1190
            hardlink=False, stacked=False, standalone=False, no_tree=False,
 
1191
            use_existing_dir=False):
1111
1192
        from bzrlib.tag import _merge_tags_if_possible
1112
1193
 
1113
1194
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1114
1195
            from_location)
 
1196
        if (accelerator_tree is not None and
 
1197
            accelerator_tree.supports_content_filtering()):
 
1198
            accelerator_tree = None
1115
1199
        revision = _get_one_revision('branch', revision)
1116
1200
        br_from.lock_read()
1117
1201
        try:
1128
1212
            try:
1129
1213
                to_transport.mkdir('.')
1130
1214
            except errors.FileExists:
1131
 
                raise errors.BzrCommandError('Target directory "%s" already'
1132
 
                                             ' exists.' % to_location)
 
1215
                if not use_existing_dir:
 
1216
                    raise errors.BzrCommandError('Target directory "%s" '
 
1217
                        'already exists.' % to_location)
 
1218
                else:
 
1219
                    try:
 
1220
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1221
                    except errors.NotBranchError:
 
1222
                        pass
 
1223
                    else:
 
1224
                        raise errors.AlreadyBranchError(to_location)
1133
1225
            except errors.NoSuchFile:
1134
1226
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1135
1227
                                             % to_location)
1380
1472
            title='Deletion Strategy', value_switches=True, enum_switch=False,
1381
1473
            safe='Only delete files if they can be'
1382
1474
                 ' safely recovered (default).',
1383
 
            keep="Don't delete any files.",
 
1475
            keep='Delete from bzr but leave the working copy.',
1384
1476
            force='Delete all the specified files, even if they can not be '
1385
1477
                'recovered and even if they are non-empty directories.')]
1386
1478
    aliases = ['rm', 'del']
1621
1713
                branch.set_append_revisions_only(True)
1622
1714
            except errors.UpgradeRequired:
1623
1715
                raise errors.BzrCommandError('This branch format cannot be set'
1624
 
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1716
                    ' to append-revisions-only.  Try --default.')
1625
1717
        if not is_quiet():
1626
1718
            from bzrlib.info import describe_layout, describe_format
1627
1719
            try:
2366
2458
 
2367
2459
        if path is None:
2368
2460
            fs_path = '.'
2369
 
            prefix = ''
2370
2461
        else:
2371
2462
            if from_root:
2372
2463
                raise errors.BzrCommandError('cannot specify both --from-root'
2373
2464
                                             ' and PATH')
2374
2465
            fs_path = path
2375
 
            prefix = path
2376
2466
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2377
2467
            fs_path)
 
2468
 
 
2469
        # Calculate the prefix to use
 
2470
        prefix = None
2378
2471
        if from_root:
2379
 
            relpath = u''
2380
 
        elif relpath:
2381
 
            relpath += '/'
 
2472
            if relpath:
 
2473
                prefix = relpath + '/'
 
2474
        elif fs_path != '.':
 
2475
            prefix = fs_path + '/'
 
2476
 
2382
2477
        if revision is not None or tree is None:
2383
2478
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2384
2479
 
2392
2487
 
2393
2488
        tree.lock_read()
2394
2489
        try:
2395
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2396
 
                if fp.startswith(relpath):
2397
 
                    rp = fp[len(relpath):]
2398
 
                    fp = osutils.pathjoin(prefix, rp)
2399
 
                    if not recursive and '/' in rp:
2400
 
                        continue
2401
 
                    if not all and not selection[fc]:
2402
 
                        continue
2403
 
                    if kind is not None and fkind != kind:
2404
 
                        continue
2405
 
                    if apply_view:
2406
 
                        try:
2407
 
                            views.check_path_in_view(tree, fp)
2408
 
                        except errors.FileOutsideView:
2409
 
                            continue
2410
 
                    kindch = entry.kind_character()
2411
 
                    outstring = fp + kindch
2412
 
                    ui.ui_factory.clear_term()
2413
 
                    if verbose:
2414
 
                        outstring = '%-8s %s' % (fc, outstring)
2415
 
                        if show_ids and fid is not None:
2416
 
                            outstring = "%-50s %s" % (outstring, fid)
2417
 
                        self.outf.write(outstring + '\n')
2418
 
                    elif null:
2419
 
                        self.outf.write(fp + '\0')
2420
 
                        if show_ids:
2421
 
                            if fid is not None:
2422
 
                                self.outf.write(fid)
2423
 
                            self.outf.write('\0')
2424
 
                        self.outf.flush()
2425
 
                    else:
 
2490
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2491
                from_dir=relpath, recursive=recursive):
 
2492
                # Apply additional masking
 
2493
                if not all and not selection[fc]:
 
2494
                    continue
 
2495
                if kind is not None and fkind != kind:
 
2496
                    continue
 
2497
                if apply_view:
 
2498
                    try:
 
2499
                        if relpath:
 
2500
                            fullpath = osutils.pathjoin(relpath, fp)
 
2501
                        else:
 
2502
                            fullpath = fp
 
2503
                        views.check_path_in_view(tree, fullpath)
 
2504
                    except errors.FileOutsideView:
 
2505
                        continue
 
2506
 
 
2507
                # Output the entry
 
2508
                if prefix:
 
2509
                    fp = osutils.pathjoin(prefix, fp)
 
2510
                kindch = entry.kind_character()
 
2511
                outstring = fp + kindch
 
2512
                ui.ui_factory.clear_term()
 
2513
                if verbose:
 
2514
                    outstring = '%-8s %s' % (fc, outstring)
 
2515
                    if show_ids and fid is not None:
 
2516
                        outstring = "%-50s %s" % (outstring, fid)
 
2517
                    self.outf.write(outstring + '\n')
 
2518
                elif null:
 
2519
                    self.outf.write(fp + '\0')
 
2520
                    if show_ids:
 
2521
                        if fid is not None:
 
2522
                            self.outf.write(fid)
 
2523
                        self.outf.write('\0')
 
2524
                    self.outf.flush()
 
2525
                else:
 
2526
                    if show_ids:
2426
2527
                        if fid is not None:
2427
2528
                            my_id = fid
2428
2529
                        else:
2429
2530
                            my_id = ''
2430
 
                        if show_ids:
2431
 
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
2432
 
                        else:
2433
 
                            self.outf.write(outstring + '\n')
 
2531
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2532
                    else:
 
2533
                        self.outf.write(outstring + '\n')
2434
2534
        finally:
2435
2535
            tree.unlock()
2436
2536
 
2960
3060
    The working tree and branch checks will only give output if a problem is
2961
3061
    detected. The output fields of the repository check are:
2962
3062
 
2963
 
        revisions: This is just the number of revisions checked.  It doesn't
2964
 
            indicate a problem.
2965
 
        versionedfiles: This is just the number of versionedfiles checked.  It
2966
 
            doesn't indicate a problem.
2967
 
        unreferenced ancestors: Texts that are ancestors of other texts, but
2968
 
            are not properly referenced by the revision ancestry.  This is a
2969
 
            subtle problem that Bazaar can work around.
2970
 
        unique file texts: This is the total number of unique file contents
2971
 
            seen in the checked revisions.  It does not indicate a problem.
2972
 
        repeated file texts: This is the total number of repeated texts seen
2973
 
            in the checked revisions.  Texts can be repeated when their file
2974
 
            entries are modified, but the file contents are not.  It does not
2975
 
            indicate a problem.
 
3063
    revisions
 
3064
        This is just the number of revisions checked.  It doesn't
 
3065
        indicate a problem.
 
3066
 
 
3067
    versionedfiles
 
3068
        This is just the number of versionedfiles checked.  It
 
3069
        doesn't indicate a problem.
 
3070
 
 
3071
    unreferenced ancestors
 
3072
        Texts that are ancestors of other texts, but
 
3073
        are not properly referenced by the revision ancestry.  This is a
 
3074
        subtle problem that Bazaar can work around.
 
3075
 
 
3076
    unique file texts
 
3077
        This is the total number of unique file contents
 
3078
        seen in the checked revisions.  It does not indicate a problem.
 
3079
 
 
3080
    repeated file texts
 
3081
        This is the total number of repeated texts seen
 
3082
        in the checked revisions.  Texts can be repeated when their file
 
3083
        entries are modified, but the file contents are not.  It does not
 
3084
        indicate a problem.
2976
3085
 
2977
3086
    If no restrictions are specified, all Bazaar data that is found at the given
2978
3087
    location will be checked.
3455
3564
    merge refuses to run if there are any uncommitted changes, unless
3456
3565
    --force is given.
3457
3566
 
 
3567
    To select only some changes to merge, use "merge -i", which will prompt
 
3568
    you to apply each diff hunk and file change, similar to "shelve".
 
3569
 
3458
3570
    :Examples:
3459
3571
        To merge the latest revision from bzr.dev::
3460
3572
 
3498
3610
               short_name='d',
3499
3611
               type=unicode,
3500
3612
               ),
3501
 
        Option('preview', help='Instead of merging, show a diff of the merge.')
 
3613
        Option('preview', help='Instead of merging, show a diff of the'
 
3614
               ' merge.'),
 
3615
        Option('interactive', help='Select changes interactively.',
 
3616
            short_name='i')
3502
3617
    ]
3503
3618
 
3504
3619
    def run(self, location=None, revision=None, force=False,
3506
3621
            uncommitted=False, pull=False,
3507
3622
            directory=None,
3508
3623
            preview=False,
 
3624
            interactive=False,
3509
3625
            ):
3510
3626
        if merge_type is None:
3511
3627
            merge_type = _mod_merge.Merge3Merger
3523
3639
        except errors.NoSuchRevision:
3524
3640
            basis_tree = tree.basis_tree()
3525
3641
        if not force:
3526
 
            changes = tree.changes_from(basis_tree)
3527
 
            if changes.has_changed():
 
3642
            if tree.has_changes(basis_tree):
3528
3643
                raise errors.UncommittedChanges(tree)
3529
3644
 
3530
3645
        view_info = _get_view_info_for_change_reporter(tree)
3557
3672
                if revision is not None and len(revision) > 0:
3558
3673
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3559
3674
                        ' --revision at the same time.')
3560
 
                location = self._select_branch_location(tree, location)[0]
3561
 
                other_tree, other_path = WorkingTree.open_containing(location)
3562
 
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
3563
 
                    pb)
 
3675
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3676
                                                          cleanups)
3564
3677
                allow_pending = False
3565
 
                if other_path != '':
3566
 
                    merger.interesting_files = [other_path]
3567
3678
 
3568
3679
            if merger is None:
3569
3680
                merger, allow_pending = self._get_merger_from_branch(tree,
3587
3698
                    return 0
3588
3699
            merger.check_basis(False)
3589
3700
            if preview:
3590
 
                return self._do_preview(merger)
 
3701
                return self._do_preview(merger, cleanups)
 
3702
            elif interactive:
 
3703
                return self._do_interactive(merger, cleanups)
3591
3704
            else:
3592
3705
                return self._do_merge(merger, change_reporter, allow_pending,
3593
3706
                                      verified)
3595
3708
            for cleanup in reversed(cleanups):
3596
3709
                cleanup()
3597
3710
 
3598
 
    def _do_preview(self, merger):
3599
 
        from bzrlib.diff import show_diff_trees
 
3711
    def _get_preview(self, merger, cleanups):
3600
3712
        tree_merger = merger.make_merger()
3601
3713
        tt = tree_merger.make_preview_transform()
3602
 
        try:
3603
 
            result_tree = tt.get_preview_tree()
3604
 
            show_diff_trees(merger.this_tree, result_tree, self.outf,
3605
 
                            old_label='', new_label='')
3606
 
        finally:
3607
 
            tt.finalize()
 
3714
        cleanups.append(tt.finalize)
 
3715
        result_tree = tt.get_preview_tree()
 
3716
        return result_tree
 
3717
 
 
3718
    def _do_preview(self, merger, cleanups):
 
3719
        from bzrlib.diff import show_diff_trees
 
3720
        result_tree = self._get_preview(merger, cleanups)
 
3721
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3722
                        old_label='', new_label='')
3608
3723
 
3609
3724
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3610
3725
        merger.change_reporter = change_reporter
3618
3733
        else:
3619
3734
            return 0
3620
3735
 
 
3736
    def _do_interactive(self, merger, cleanups):
 
3737
        """Perform an interactive merge.
 
3738
 
 
3739
        This works by generating a preview tree of the merge, then using
 
3740
        Shelver to selectively remove the differences between the working tree
 
3741
        and the preview tree.
 
3742
        """
 
3743
        from bzrlib import shelf_ui
 
3744
        result_tree = self._get_preview(merger, cleanups)
 
3745
        writer = bzrlib.option.diff_writer_registry.get()
 
3746
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
3747
                                   reporter=shelf_ui.ApplyReporter(),
 
3748
                                   diff_writer=writer(sys.stdout))
 
3749
        shelver.run()
 
3750
 
3621
3751
    def sanity_check_merger(self, merger):
3622
3752
        if (merger.show_base and
3623
3753
            not merger.merge_type is _mod_merge.Merge3Merger):
3658
3788
            base_branch, base_path = Branch.open_containing(base_loc,
3659
3789
                possible_transports)
3660
3790
        # Find the revision ids
3661
 
        if revision is None or len(revision) < 1 or revision[-1] is None:
 
3791
        other_revision_id = None
 
3792
        base_revision_id = None
 
3793
        if revision is not None:
 
3794
            if len(revision) >= 1:
 
3795
                other_revision_id = revision[-1].as_revision_id(other_branch)
 
3796
            if len(revision) == 2:
 
3797
                base_revision_id = revision[0].as_revision_id(base_branch)
 
3798
        if other_revision_id is None:
3662
3799
            other_revision_id = _mod_revision.ensure_null(
3663
3800
                other_branch.last_revision())
3664
 
        else:
3665
 
            other_revision_id = revision[-1].as_revision_id(other_branch)
3666
 
        if (revision is not None and len(revision) == 2
3667
 
            and revision[0] is not None):
3668
 
            base_revision_id = revision[0].as_revision_id(base_branch)
3669
 
        else:
3670
 
            base_revision_id = None
3671
3801
        # Remember where we merge from
3672
3802
        if ((remember or tree.branch.get_submit_branch() is None) and
3673
3803
             user_location is not None):
3682
3812
            allow_pending = True
3683
3813
        return merger, allow_pending
3684
3814
 
 
3815
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3816
        """Get a merger for uncommitted changes.
 
3817
 
 
3818
        :param tree: The tree the merger should apply to.
 
3819
        :param location: The location containing uncommitted changes.
 
3820
        :param pb: The progress bar to use for showing progress.
 
3821
        :param cleanups: A list of operations to perform to clean up the
 
3822
            temporary directories, unfinalized objects, etc.
 
3823
        """
 
3824
        location = self._select_branch_location(tree, location)[0]
 
3825
        other_tree, other_path = WorkingTree.open_containing(location)
 
3826
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
3827
        if other_path != '':
 
3828
            merger.interesting_files = [other_path]
 
3829
        return merger
 
3830
 
3685
3831
    def _select_branch_location(self, tree, user_location, revision=None,
3686
3832
                                index=None):
3687
3833
        """Select a branch location, according to possible inputs.
4665
4811
        try:
4666
4812
            containing_tree.extract(sub_id)
4667
4813
        except errors.RootNotRich:
4668
 
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
4814
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
4669
4815
 
4670
4816
 
4671
4817
class cmd_merge_directive(Command):
4840
4986
               help='Write merge directive to this file; '
4841
4987
                    'use - for stdout.',
4842
4988
               type=unicode),
 
4989
        Option('strict',
 
4990
               help='Refuse to send if there are uncommitted changes in'
 
4991
               ' the working tree, --no-strict disables the check.'),
4843
4992
        Option('mail-to', help='Mail the request to this address.',
4844
4993
               type=unicode),
4845
4994
        'revision',
4846
4995
        'message',
4847
4996
        Option('body', help='Body for the email.', type=unicode),
4848
4997
        RegistryOption('format',
4849
 
                       help='Use the specified output format.', 
4850
 
                       lazy_registry=('bzrlib.send', 'format_registry'))
 
4998
                       help='Use the specified output format.',
 
4999
                       lazy_registry=('bzrlib.send', 'format_registry')),
4851
5000
        ]
4852
5001
 
4853
5002
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4854
5003
            no_patch=False, revision=None, remember=False, output=None,
4855
 
            format=None, mail_to=None, message=None, body=None, **kwargs):
 
5004
            format=None, mail_to=None, message=None, body=None,
 
5005
            strict=None, **kwargs):
4856
5006
        from bzrlib.send import send
4857
5007
        return send(submit_branch, revision, public_branch, remember,
4858
 
                         format, no_bundle, no_patch, output,
4859
 
                         kwargs.get('from', '.'), mail_to, message, body,
4860
 
                         self.outf)
 
5008
                    format, no_bundle, no_patch, output,
 
5009
                    kwargs.get('from', '.'), mail_to, message, body,
 
5010
                    self.outf,
 
5011
                    strict=strict)
4861
5012
 
4862
5013
 
4863
5014
class cmd_bundle_revisions(cmd_send):
4907
5058
               type=unicode),
4908
5059
        Option('output', short_name='o', help='Write directive to this file.',
4909
5060
               type=unicode),
 
5061
        Option('strict',
 
5062
               help='Refuse to bundle revisions if there are uncommitted'
 
5063
               ' changes in the working tree, --no-strict disables the check.'),
4910
5064
        'revision',
4911
5065
        RegistryOption('format',
4912
5066
                       help='Use the specified output format.',
4920
5074
 
4921
5075
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4922
5076
            no_patch=False, revision=None, remember=False, output=None,
4923
 
            format=None, **kwargs):
 
5077
            format=None, strict=None, **kwargs):
4924
5078
        if output is None:
4925
5079
            output = '-'
4926
5080
        from bzrlib.send import send
4927
5081
        return send(submit_branch, revision, public_branch, remember,
4928
5082
                         format, no_bundle, no_patch, output,
4929
5083
                         kwargs.get('from', '.'), None, None, None,
4930
 
                         self.outf)
 
5084
                         self.outf, strict=strict)
4931
5085
 
4932
5086
 
4933
5087
class cmd_tag(Command):
5105
5259
            ),
5106
5260
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5107
5261
        Option('force',
5108
 
               help='Perform reconfiguration even if local changes'
5109
 
               ' will be lost.')
 
5262
            help='Perform reconfiguration even if local changes'
 
5263
            ' will be lost.'),
 
5264
        Option('stacked-on',
 
5265
            help='Reconfigure a branch to be stacked on another branch.',
 
5266
            type=unicode,
 
5267
            ),
 
5268
        Option('unstacked',
 
5269
            help='Reconfigure a branch to be unstacked.  This '
 
5270
                'may require copying substantial data into it.',
 
5271
            ),
5110
5272
        ]
5111
5273
 
5112
 
    def run(self, location=None, target_type=None, bind_to=None, force=False):
 
5274
    def run(self, location=None, target_type=None, bind_to=None, force=False,
 
5275
            stacked_on=None,
 
5276
            unstacked=None):
5113
5277
        directory = bzrdir.BzrDir.open(location)
 
5278
        if stacked_on and unstacked:
 
5279
            raise BzrCommandError("Can't use both --stacked-on and --unstacked")
 
5280
        elif stacked_on is not None:
 
5281
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
5282
        elif unstacked:
 
5283
            reconfigure.ReconfigureUnstacked().apply(directory)
 
5284
        # At the moment you can use --stacked-on and a different
 
5285
        # reconfiguration shape at the same time; there seems no good reason
 
5286
        # to ban it.
5114
5287
        if target_type is None:
5115
 
            raise errors.BzrCommandError('No target configuration specified')
 
5288
            if stacked_on or unstacked:
 
5289
                return
 
5290
            else:
 
5291
                raise errors.BzrCommandError('No target configuration '
 
5292
                    'specified')
5116
5293
        elif target_type == 'branch':
5117
5294
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5118
5295
        elif target_type == 'tree':
5161
5338
 
5162
5339
    takes_args = ['to_location']
5163
5340
    takes_options = [Option('force',
5164
 
                        help='Switch even if local commits will be lost.')
 
5341
                        help='Switch even if local commits will be lost.'),
 
5342
                     Option('create-branch', short_name='b',
 
5343
                        help='Create the target branch from this one before'
 
5344
                             ' switching to it.'),
5165
5345
                     ]
5166
5346
 
5167
 
    def run(self, to_location, force=False):
 
5347
    def run(self, to_location, force=False, create_branch=False):
5168
5348
        from bzrlib import switch
5169
5349
        tree_location = '.'
5170
5350
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5172
5352
            branch = control_dir.open_branch()
5173
5353
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5174
5354
        except errors.NotBranchError:
 
5355
            branch = None
5175
5356
            had_explicit_nick = False
5176
 
        try:
5177
 
            to_branch = Branch.open(to_location)
5178
 
        except errors.NotBranchError:
5179
 
            this_url = self._get_branch_location(control_dir)
5180
 
            to_branch = Branch.open(
5181
 
                urlutils.join(this_url, '..', to_location))
 
5357
        if create_branch:
 
5358
            if branch is None:
 
5359
                raise errors.BzrCommandError('cannot create branch without'
 
5360
                                             ' source branch')
 
5361
            if '/' not in to_location and '\\' not in to_location:
 
5362
                # This path is meant to be relative to the existing branch
 
5363
                this_url = self._get_branch_location(control_dir)
 
5364
                to_location = urlutils.join(this_url, '..', to_location)
 
5365
            to_branch = branch.bzrdir.sprout(to_location,
 
5366
                                 possible_transports=[branch.bzrdir.root_transport],
 
5367
                                 source_branch=branch).open_branch()
 
5368
            # try:
 
5369
            #     from_branch = control_dir.open_branch()
 
5370
            # except errors.NotBranchError:
 
5371
            #     raise BzrCommandError('Cannot create a branch from this'
 
5372
            #         ' location when we cannot open this branch')
 
5373
            # from_branch.bzrdir.sprout(
 
5374
            pass
 
5375
        else:
 
5376
            try:
 
5377
                to_branch = Branch.open(to_location)
 
5378
            except errors.NotBranchError:
 
5379
                this_url = self._get_branch_location(control_dir)
 
5380
                to_branch = Branch.open(
 
5381
                    urlutils.join(this_url, '..', to_location))
5182
5382
        switch.switch(control_dir, to_branch, force)
5183
5383
        if had_explicit_nick:
5184
5384
            branch = control_dir.open_branch() #get the new branch!