~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

merge 2.0 branch rev 4647

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,
121
120
 
122
121
 
123
122
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
 
123
    """Get a revision tree. Not suitable for commands that change the tree.
 
124
    
 
125
    Specifically, the basis tree in dirstate trees is coupled to the dirstate
 
126
    and doing a commit/uncommit/pull will at best fail due to changing the
 
127
    basis revision data.
 
128
 
 
129
    If tree is passed in, it should be already locked, for lifetime management
 
130
    of the trees internal cached state.
 
131
    """
124
132
    if branch is None:
125
133
        branch = tree.branch
126
134
    if revisions is None:
450
458
        except errors.NoWorkingTree:
451
459
            raise errors.BzrCommandError("No working tree to remove")
452
460
        except errors.NotLocalUrl:
453
 
            raise errors.BzrCommandError("You cannot remove the working tree of a "
454
 
                                         "remote path")
 
461
            raise errors.BzrCommandError("You cannot remove the working tree"
 
462
                                         " of a remote path")
455
463
        if not force:
456
 
            changes = working.changes_from(working.basis_tree())
457
 
            if changes.has_changed():
 
464
            # XXX: What about pending merges ? -- vila 20090629
 
465
            if working.has_changes(working.basis_tree()):
458
466
                raise errors.UncommittedChanges(working)
459
467
 
460
468
        working_path = working.bzrdir.root_transport.base
461
469
        branch_path = working.branch.bzrdir.root_transport.base
462
470
        if working_path != branch_path:
463
 
            raise errors.BzrCommandError("You cannot remove the working tree from "
464
 
                                         "a lightweight checkout")
 
471
            raise errors.BzrCommandError("You cannot remove the working tree"
 
472
                                         " from a lightweight checkout")
465
473
 
466
474
        d.destroy_workingtree()
467
475
 
474
482
 
475
483
    _see_also = ['info']
476
484
    takes_args = ['location?']
 
485
    takes_options = [
 
486
        Option('tree', help='Show revno of working tree'),
 
487
        ]
477
488
 
478
489
    @display_command
479
 
    def run(self, location=u'.'):
480
 
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
481
 
        self.outf.write('\n')
 
490
    def run(self, tree=False, location=u'.'):
 
491
        if tree:
 
492
            try:
 
493
                wt = WorkingTree.open_containing(location)[0]
 
494
                wt.lock_read()
 
495
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
496
                raise errors.NoWorkingTree(location)
 
497
            try:
 
498
                revid = wt.last_revision()
 
499
                try:
 
500
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
501
                except errors.NoSuchRevision:
 
502
                    revno_t = ('???',)
 
503
                revno = ".".join(str(n) for n in revno_t)
 
504
            finally:
 
505
                wt.unlock()
 
506
        else:
 
507
            b = Branch.open_containing(location)[0]
 
508
            b.lock_read()
 
509
            try:
 
510
                revno = b.revno()
 
511
            finally:
 
512
                b.unlock()
 
513
 
 
514
        self.outf.write(str(revno) + '\n')
482
515
 
483
516
 
484
517
class cmd_revision_info(Command):
494
527
            short_name='d',
495
528
            type=unicode,
496
529
            ),
 
530
        Option('tree', help='Show revno of working tree'),
497
531
        ]
498
532
 
499
533
    @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)
 
534
    def run(self, revision=None, directory=u'.', tree=False,
 
535
            revision_info_list=[]):
 
536
 
 
537
        try:
 
538
            wt = WorkingTree.open_containing(directory)[0]
 
539
            b = wt.branch
 
540
            wt.lock_read()
 
541
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
542
            wt = None
 
543
            b = Branch.open_containing(directory)[0]
 
544
            b.lock_read()
 
545
        try:
 
546
            revision_ids = []
 
547
            if revision is not None:
 
548
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
549
            if revision_info_list is not None:
 
550
                for rev_str in revision_info_list:
 
551
                    rev_spec = RevisionSpec.from_string(rev_str)
 
552
                    revision_ids.append(rev_spec.as_revision_id(b))
 
553
            # No arguments supplied, default to the last revision
 
554
            if len(revision_ids) == 0:
 
555
                if tree:
 
556
                    if wt is None:
 
557
                        raise errors.NoWorkingTree(directory)
 
558
                    revision_ids.append(wt.last_revision())
 
559
                else:
 
560
                    revision_ids.append(b.last_revision())
 
561
 
 
562
            revinfos = []
 
563
            maxlen = 0
 
564
            for revision_id in revision_ids:
 
565
                try:
 
566
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
567
                    revno = '.'.join(str(i) for i in dotted_revno)
 
568
                except errors.NoSuchRevision:
 
569
                    revno = '???'
 
570
                maxlen = max(maxlen, len(revno))
 
571
                revinfos.append([revno, revision_id])
 
572
        finally:
 
573
            if wt is None:
 
574
                b.unlock()
 
575
            else:
 
576
                wt.unlock()
 
577
 
 
578
        for ri in revinfos:
 
579
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
522
580
 
523
581
 
524
582
class cmd_add(Command):
554
612
    branches that will be merged later (without showing the two different
555
613
    adds as a conflict). It is also useful when merging another project
556
614
    into a subdirectory of this one.
 
615
    
 
616
    Any files matching patterns in the ignore list will not be added
 
617
    unless they are explicitly mentioned.
557
618
    """
558
619
    takes_args = ['file*']
559
620
    takes_options = [
567
628
               help='Lookup file ids from this tree.'),
568
629
        ]
569
630
    encoding_type = 'replace'
570
 
    _see_also = ['remove']
 
631
    _see_also = ['remove', 'ignore']
571
632
 
572
633
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
573
634
            file_ids_from=None):
605
666
                    for path in ignored[glob]:
606
667
                        self.outf.write("ignored %s matching \"%s\"\n"
607
668
                                        % (path, glob))
608
 
            else:
609
 
                match_len = 0
610
 
                for glob, paths in ignored.items():
611
 
                    match_len += len(paths)
612
 
                self.outf.write("ignored %d file(s).\n" % match_len)
613
 
            self.outf.write("If you wish to add ignored files, "
614
 
                            "please add them explicitly by name. "
615
 
                            "(\"bzr ignored\" gives a list)\n")
616
669
 
617
670
 
618
671
class cmd_mkdir(Command):
750
803
        if len(names_list) < 2:
751
804
            raise errors.BzrCommandError("missing file argument")
752
805
        tree, rel_names = tree_files(names_list, canonicalize=False)
753
 
        tree.lock_write()
 
806
        tree.lock_tree_write()
754
807
        try:
755
808
            self._run(tree, names_list, rel_names, after)
756
809
        finally:
764
817
            raise errors.BzrCommandError('--after cannot be specified with'
765
818
                                         ' --auto.')
766
819
        work_tree, file_list = tree_files(names_list, default_branch='.')
767
 
        work_tree.lock_write()
 
820
        work_tree.lock_tree_write()
768
821
        try:
769
822
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
770
823
        finally:
946
999
            if branch_to.get_parent() is None or remember:
947
1000
                branch_to.set_parent(branch_from.base)
948
1001
 
949
 
        if revision is not None:
950
 
            revision_id = revision.as_revision_id(branch_from)
951
 
 
952
 
        branch_to.lock_write()
 
1002
        if branch_from is not branch_to:
 
1003
            branch_from.lock_read()
953
1004
        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)
 
1005
            if revision is not None:
 
1006
                revision_id = revision.as_revision_id(branch_from)
 
1007
 
 
1008
            branch_to.lock_write()
 
1009
            try:
 
1010
                if tree_to is not None:
 
1011
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1012
                    change_reporter = delta._ChangeReporter(
 
1013
                        unversioned_filter=tree_to.is_ignored,
 
1014
                        view_info=view_info)
 
1015
                    result = tree_to.pull(
 
1016
                        branch_from, overwrite, revision_id, change_reporter,
 
1017
                        possible_transports=possible_transports, local=local)
 
1018
                else:
 
1019
                    result = branch_to.pull(
 
1020
                        branch_from, overwrite, revision_id, local=local)
 
1021
 
 
1022
                result.report(self.outf)
 
1023
                if verbose and result.old_revid != result.new_revid:
 
1024
                    log.show_branch_change(
 
1025
                        branch_to, self.outf, result.old_revno,
 
1026
                        result.old_revid)
 
1027
            finally:
 
1028
                branch_to.unlock()
970
1029
        finally:
971
 
            branch_to.unlock()
 
1030
            if branch_from is not branch_to:
 
1031
                branch_from.unlock()
972
1032
 
973
1033
 
974
1034
class cmd_push(Command):
1021
1081
                'for the commit history. Only the work not present in the '
1022
1082
                'referenced branch is included in the branch created.',
1023
1083
            type=unicode),
 
1084
        Option('strict',
 
1085
               help='Refuse to push if there are uncommitted changes in'
 
1086
               ' the working tree, --no-strict disables the check.'),
1024
1087
        ]
1025
1088
    takes_args = ['location?']
1026
1089
    encoding_type = 'replace'
1028
1091
    def run(self, location=None, remember=False, overwrite=False,
1029
1092
        create_prefix=False, verbose=False, revision=None,
1030
1093
        use_existing_dir=False, directory=None, stacked_on=None,
1031
 
        stacked=False):
 
1094
        stacked=False, strict=None):
1032
1095
        from bzrlib.push import _show_push_branch
1033
1096
 
1034
 
        # Get the source branch and revision_id
1035
1097
        if directory is None:
1036
1098
            directory = '.'
1037
 
        br_from = Branch.open_containing(directory)[0]
 
1099
        # Get the source branch
 
1100
        (tree, br_from,
 
1101
         _unused) = bzrdir.BzrDir.open_containing_tree_or_branch(directory)
 
1102
        if strict is None:
 
1103
            strict = br_from.get_config().get_user_option_as_bool('push_strict')
 
1104
        if strict is None: strict = True # default value
 
1105
        # Get the tip's revision_id
1038
1106
        revision = _get_one_revision('push', revision)
1039
1107
        if revision is not None:
1040
1108
            revision_id = revision.in_history(br_from).rev_id
1041
1109
        else:
1042
1110
            revision_id = None
 
1111
        if strict and tree is not None and revision_id is None:
 
1112
            if (tree.has_changes(tree.basis_tree())
 
1113
                or len(tree.get_parent_ids()) > 1):
 
1114
                raise errors.UncommittedChanges(
 
1115
                    tree, more='Use --no-strict to force the push.')
 
1116
            if tree.last_revision() != tree.branch.last_revision():
 
1117
                # The tree has lost sync with its branch, there is little
 
1118
                # chance that the user is aware of it but he can still force
 
1119
                # the push with --no-strict
 
1120
                raise errors.OutOfDateTree(
 
1121
                    tree, more='Use --no-strict to force the push.')
1043
1122
 
1044
1123
        # Get the stacked_on branch, if any
1045
1124
        if stacked_on is not None:
1097
1176
        help='Hard-link working tree files where possible.'),
1098
1177
        Option('no-tree',
1099
1178
            help="Create a branch without a working-tree."),
 
1179
        Option('switch',
 
1180
            help="Switch the checkout in the current directory "
 
1181
                 "to the new branch."),
1100
1182
        Option('stacked',
1101
1183
            help='Create a stacked branch referring to the source branch. '
1102
1184
                'The new branch will depend on the availability of the source '
1103
1185
                'branch for all operations.'),
1104
1186
        Option('standalone',
1105
1187
               help='Do not use a shared repository, even if available.'),
 
1188
        Option('use-existing-dir',
 
1189
               help='By default branch will fail if the target'
 
1190
                    ' directory exists, but does not already'
 
1191
                    ' have a control directory.  This flag will'
 
1192
                    ' allow branch to proceed.'),
1106
1193
        ]
1107
1194
    aliases = ['get', 'clone']
1108
1195
 
1109
1196
    def run(self, from_location, to_location=None, revision=None,
1110
 
            hardlink=False, stacked=False, standalone=False, no_tree=False):
 
1197
            hardlink=False, stacked=False, standalone=False, no_tree=False,
 
1198
            use_existing_dir=False, switch=False):
 
1199
        from bzrlib import switch as _mod_switch
1111
1200
        from bzrlib.tag import _merge_tags_if_possible
1112
 
 
1113
1201
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1114
1202
            from_location)
1115
1203
        if (accelerator_tree is not None and
1131
1219
            try:
1132
1220
                to_transport.mkdir('.')
1133
1221
            except errors.FileExists:
1134
 
                raise errors.BzrCommandError('Target directory "%s" already'
1135
 
                                             ' exists.' % to_location)
 
1222
                if not use_existing_dir:
 
1223
                    raise errors.BzrCommandError('Target directory "%s" '
 
1224
                        'already exists.' % to_location)
 
1225
                else:
 
1226
                    try:
 
1227
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1228
                    except errors.NotBranchError:
 
1229
                        pass
 
1230
                    else:
 
1231
                        raise errors.AlreadyBranchError(to_location)
1136
1232
            except errors.NoSuchFile:
1137
1233
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1138
1234
                                             % to_location)
1161
1257
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1162
1258
                errors.UnstackableRepositoryFormat), e:
1163
1259
                note('Branched %d revision(s).' % branch.revno())
 
1260
            if switch:
 
1261
                # Switch to the new branch
 
1262
                wt, _ = WorkingTree.open_containing('.')
 
1263
                _mod_switch.switch(wt.bzrdir, branch)
 
1264
                note('Switched to branch: %s',
 
1265
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
1164
1266
        finally:
1165
1267
            br_from.unlock()
1166
1268
 
1383
1485
            title='Deletion Strategy', value_switches=True, enum_switch=False,
1384
1486
            safe='Only delete files if they can be'
1385
1487
                 ' safely recovered (default).',
1386
 
            keep="Don't delete any files.",
 
1488
            keep='Delete from bzr but leave the working copy.',
1387
1489
            force='Delete all the specified files, even if they can not be '
1388
1490
                'recovered and even if they are non-empty directories.')]
1389
1491
    aliases = ['rm', 'del']
1624
1726
                branch.set_append_revisions_only(True)
1625
1727
            except errors.UpgradeRequired:
1626
1728
                raise errors.BzrCommandError('This branch format cannot be set'
1627
 
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1729
                    ' to append-revisions-only.  Try --default.')
1628
1730
        if not is_quiet():
1629
1731
            from bzrlib.info import describe_layout, describe_format
1630
1732
            try:
2369
2471
 
2370
2472
        if path is None:
2371
2473
            fs_path = '.'
2372
 
            prefix = ''
2373
2474
        else:
2374
2475
            if from_root:
2375
2476
                raise errors.BzrCommandError('cannot specify both --from-root'
2376
2477
                                             ' and PATH')
2377
2478
            fs_path = path
2378
 
            prefix = path
2379
2479
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2380
2480
            fs_path)
 
2481
 
 
2482
        # Calculate the prefix to use
 
2483
        prefix = None
2381
2484
        if from_root:
2382
 
            relpath = u''
2383
 
        elif relpath:
2384
 
            relpath += '/'
 
2485
            if relpath:
 
2486
                prefix = relpath + '/'
 
2487
        elif fs_path != '.':
 
2488
            prefix = fs_path + '/'
 
2489
 
2385
2490
        if revision is not None or tree is None:
2386
2491
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2387
2492
 
2395
2500
 
2396
2501
        tree.lock_read()
2397
2502
        try:
2398
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2399
 
                if fp.startswith(relpath):
2400
 
                    rp = fp[len(relpath):]
2401
 
                    fp = osutils.pathjoin(prefix, rp)
2402
 
                    if not recursive and '/' in rp:
2403
 
                        continue
2404
 
                    if not all and not selection[fc]:
2405
 
                        continue
2406
 
                    if kind is not None and fkind != kind:
2407
 
                        continue
2408
 
                    if apply_view:
2409
 
                        try:
2410
 
                            views.check_path_in_view(tree, fp)
2411
 
                        except errors.FileOutsideView:
2412
 
                            continue
2413
 
                    kindch = entry.kind_character()
2414
 
                    outstring = fp + kindch
2415
 
                    ui.ui_factory.clear_term()
2416
 
                    if verbose:
2417
 
                        outstring = '%-8s %s' % (fc, outstring)
2418
 
                        if show_ids and fid is not None:
2419
 
                            outstring = "%-50s %s" % (outstring, fid)
2420
 
                        self.outf.write(outstring + '\n')
2421
 
                    elif null:
2422
 
                        self.outf.write(fp + '\0')
2423
 
                        if show_ids:
2424
 
                            if fid is not None:
2425
 
                                self.outf.write(fid)
2426
 
                            self.outf.write('\0')
2427
 
                        self.outf.flush()
2428
 
                    else:
 
2503
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2504
                from_dir=relpath, recursive=recursive):
 
2505
                # Apply additional masking
 
2506
                if not all and not selection[fc]:
 
2507
                    continue
 
2508
                if kind is not None and fkind != kind:
 
2509
                    continue
 
2510
                if apply_view:
 
2511
                    try:
 
2512
                        if relpath:
 
2513
                            fullpath = osutils.pathjoin(relpath, fp)
 
2514
                        else:
 
2515
                            fullpath = fp
 
2516
                        views.check_path_in_view(tree, fullpath)
 
2517
                    except errors.FileOutsideView:
 
2518
                        continue
 
2519
 
 
2520
                # Output the entry
 
2521
                if prefix:
 
2522
                    fp = osutils.pathjoin(prefix, fp)
 
2523
                kindch = entry.kind_character()
 
2524
                outstring = fp + kindch
 
2525
                ui.ui_factory.clear_term()
 
2526
                if verbose:
 
2527
                    outstring = '%-8s %s' % (fc, outstring)
 
2528
                    if show_ids and fid is not None:
 
2529
                        outstring = "%-50s %s" % (outstring, fid)
 
2530
                    self.outf.write(outstring + '\n')
 
2531
                elif null:
 
2532
                    self.outf.write(fp + '\0')
 
2533
                    if show_ids:
 
2534
                        if fid is not None:
 
2535
                            self.outf.write(fid)
 
2536
                        self.outf.write('\0')
 
2537
                    self.outf.flush()
 
2538
                else:
 
2539
                    if show_ids:
2429
2540
                        if fid is not None:
2430
2541
                            my_id = fid
2431
2542
                        else:
2432
2543
                            my_id = ''
2433
 
                        if show_ids:
2434
 
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
2435
 
                        else:
2436
 
                            self.outf.write(outstring + '\n')
 
2544
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2545
                    else:
 
2546
                        self.outf.write(outstring + '\n')
2437
2547
        finally:
2438
2548
            tree.unlock()
2439
2549
 
2928
3038
                raise errors.BzrCommandError("empty commit message specified")
2929
3039
            return my_message
2930
3040
 
 
3041
        # The API permits a commit with a filter of [] to mean 'select nothing'
 
3042
        # but the command line should not do that.
 
3043
        if not selected_list:
 
3044
            selected_list = None
2931
3045
        try:
2932
3046
            tree.commit(message_callback=get_message,
2933
3047
                        specific_files=selected_list,
2963
3077
    The working tree and branch checks will only give output if a problem is
2964
3078
    detected. The output fields of the repository check are:
2965
3079
 
2966
 
        revisions: This is just the number of revisions checked.  It doesn't
2967
 
            indicate a problem.
2968
 
        versionedfiles: This is just the number of versionedfiles checked.  It
2969
 
            doesn't indicate a problem.
2970
 
        unreferenced ancestors: Texts that are ancestors of other texts, but
2971
 
            are not properly referenced by the revision ancestry.  This is a
2972
 
            subtle problem that Bazaar can work around.
2973
 
        unique file texts: This is the total number of unique file contents
2974
 
            seen in the checked revisions.  It does not indicate a problem.
2975
 
        repeated file texts: This is the total number of repeated texts seen
2976
 
            in the checked revisions.  Texts can be repeated when their file
2977
 
            entries are modified, but the file contents are not.  It does not
2978
 
            indicate a problem.
 
3080
    revisions
 
3081
        This is just the number of revisions checked.  It doesn't
 
3082
        indicate a problem.
 
3083
 
 
3084
    versionedfiles
 
3085
        This is just the number of versionedfiles checked.  It
 
3086
        doesn't indicate a problem.
 
3087
 
 
3088
    unreferenced ancestors
 
3089
        Texts that are ancestors of other texts, but
 
3090
        are not properly referenced by the revision ancestry.  This is a
 
3091
        subtle problem that Bazaar can work around.
 
3092
 
 
3093
    unique file texts
 
3094
        This is the total number of unique file contents
 
3095
        seen in the checked revisions.  It does not indicate a problem.
 
3096
 
 
3097
    repeated file texts
 
3098
        This is the total number of repeated texts seen
 
3099
        in the checked revisions.  Texts can be repeated when their file
 
3100
        entries are modified, but the file contents are not.  It does not
 
3101
        indicate a problem.
2979
3102
 
2980
3103
    If no restrictions are specified, all Bazaar data that is found at the given
2981
3104
    location will be checked.
3458
3581
    merge refuses to run if there are any uncommitted changes, unless
3459
3582
    --force is given.
3460
3583
 
 
3584
    To select only some changes to merge, use "merge -i", which will prompt
 
3585
    you to apply each diff hunk and file change, similar to "shelve".
 
3586
 
3461
3587
    :Examples:
3462
3588
        To merge the latest revision from bzr.dev::
3463
3589
 
3501
3627
               short_name='d',
3502
3628
               type=unicode,
3503
3629
               ),
3504
 
        Option('preview', help='Instead of merging, show a diff of the merge.')
 
3630
        Option('preview', help='Instead of merging, show a diff of the'
 
3631
               ' merge.'),
 
3632
        Option('interactive', help='Select changes interactively.',
 
3633
            short_name='i')
3505
3634
    ]
3506
3635
 
3507
3636
    def run(self, location=None, revision=None, force=False,
3509
3638
            uncommitted=False, pull=False,
3510
3639
            directory=None,
3511
3640
            preview=False,
 
3641
            interactive=False,
3512
3642
            ):
3513
3643
        if merge_type is None:
3514
3644
            merge_type = _mod_merge.Merge3Merger
3526
3656
        except errors.NoSuchRevision:
3527
3657
            basis_tree = tree.basis_tree()
3528
3658
        if not force:
3529
 
            changes = tree.changes_from(basis_tree)
3530
 
            if changes.has_changed():
 
3659
            if tree.has_changes(basis_tree):
3531
3660
                raise errors.UncommittedChanges(tree)
3532
3661
 
3533
3662
        view_info = _get_view_info_for_change_reporter(tree)
3560
3689
                if revision is not None and len(revision) > 0:
3561
3690
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3562
3691
                        ' --revision at the same time.')
3563
 
                location = self._select_branch_location(tree, location)[0]
3564
 
                other_tree, other_path = WorkingTree.open_containing(location)
3565
 
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
3566
 
                    pb)
 
3692
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3693
                                                          cleanups)
3567
3694
                allow_pending = False
3568
 
                if other_path != '':
3569
 
                    merger.interesting_files = [other_path]
3570
3695
 
3571
3696
            if merger is None:
3572
3697
                merger, allow_pending = self._get_merger_from_branch(tree,
3590
3715
                    return 0
3591
3716
            merger.check_basis(False)
3592
3717
            if preview:
3593
 
                return self._do_preview(merger)
 
3718
                return self._do_preview(merger, cleanups)
 
3719
            elif interactive:
 
3720
                return self._do_interactive(merger, cleanups)
3594
3721
            else:
3595
3722
                return self._do_merge(merger, change_reporter, allow_pending,
3596
3723
                                      verified)
3598
3725
            for cleanup in reversed(cleanups):
3599
3726
                cleanup()
3600
3727
 
3601
 
    def _do_preview(self, merger):
3602
 
        from bzrlib.diff import show_diff_trees
 
3728
    def _get_preview(self, merger, cleanups):
3603
3729
        tree_merger = merger.make_merger()
3604
3730
        tt = tree_merger.make_preview_transform()
3605
 
        try:
3606
 
            result_tree = tt.get_preview_tree()
3607
 
            show_diff_trees(merger.this_tree, result_tree, self.outf,
3608
 
                            old_label='', new_label='')
3609
 
        finally:
3610
 
            tt.finalize()
 
3731
        cleanups.append(tt.finalize)
 
3732
        result_tree = tt.get_preview_tree()
 
3733
        return result_tree
 
3734
 
 
3735
    def _do_preview(self, merger, cleanups):
 
3736
        from bzrlib.diff import show_diff_trees
 
3737
        result_tree = self._get_preview(merger, cleanups)
 
3738
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3739
                        old_label='', new_label='')
3611
3740
 
3612
3741
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3613
3742
        merger.change_reporter = change_reporter
3621
3750
        else:
3622
3751
            return 0
3623
3752
 
 
3753
    def _do_interactive(self, merger, cleanups):
 
3754
        """Perform an interactive merge.
 
3755
 
 
3756
        This works by generating a preview tree of the merge, then using
 
3757
        Shelver to selectively remove the differences between the working tree
 
3758
        and the preview tree.
 
3759
        """
 
3760
        from bzrlib import shelf_ui
 
3761
        result_tree = self._get_preview(merger, cleanups)
 
3762
        writer = bzrlib.option.diff_writer_registry.get()
 
3763
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
3764
                                   reporter=shelf_ui.ApplyReporter(),
 
3765
                                   diff_writer=writer(sys.stdout))
 
3766
        shelver.run()
 
3767
 
3624
3768
    def sanity_check_merger(self, merger):
3625
3769
        if (merger.show_base and
3626
3770
            not merger.merge_type is _mod_merge.Merge3Merger):
3661
3805
            base_branch, base_path = Branch.open_containing(base_loc,
3662
3806
                possible_transports)
3663
3807
        # Find the revision ids
3664
 
        if revision is None or len(revision) < 1 or revision[-1] is None:
 
3808
        other_revision_id = None
 
3809
        base_revision_id = None
 
3810
        if revision is not None:
 
3811
            if len(revision) >= 1:
 
3812
                other_revision_id = revision[-1].as_revision_id(other_branch)
 
3813
            if len(revision) == 2:
 
3814
                base_revision_id = revision[0].as_revision_id(base_branch)
 
3815
        if other_revision_id is None:
3665
3816
            other_revision_id = _mod_revision.ensure_null(
3666
3817
                other_branch.last_revision())
3667
 
        else:
3668
 
            other_revision_id = revision[-1].as_revision_id(other_branch)
3669
 
        if (revision is not None and len(revision) == 2
3670
 
            and revision[0] is not None):
3671
 
            base_revision_id = revision[0].as_revision_id(base_branch)
3672
 
        else:
3673
 
            base_revision_id = None
3674
3818
        # Remember where we merge from
3675
3819
        if ((remember or tree.branch.get_submit_branch() is None) and
3676
3820
             user_location is not None):
3685
3829
            allow_pending = True
3686
3830
        return merger, allow_pending
3687
3831
 
 
3832
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3833
        """Get a merger for uncommitted changes.
 
3834
 
 
3835
        :param tree: The tree the merger should apply to.
 
3836
        :param location: The location containing uncommitted changes.
 
3837
        :param pb: The progress bar to use for showing progress.
 
3838
        :param cleanups: A list of operations to perform to clean up the
 
3839
            temporary directories, unfinalized objects, etc.
 
3840
        """
 
3841
        location = self._select_branch_location(tree, location)[0]
 
3842
        other_tree, other_path = WorkingTree.open_containing(location)
 
3843
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
3844
        if other_path != '':
 
3845
            merger.interesting_files = [other_path]
 
3846
        return merger
 
3847
 
3688
3848
    def _select_branch_location(self, tree, user_location, revision=None,
3689
3849
                                index=None):
3690
3850
        """Select a branch location, according to possible inputs.
4668
4828
        try:
4669
4829
            containing_tree.extract(sub_id)
4670
4830
        except errors.RootNotRich:
4671
 
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
4831
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
4672
4832
 
4673
4833
 
4674
4834
class cmd_merge_directive(Command):
4843
5003
               help='Write merge directive to this file; '
4844
5004
                    'use - for stdout.',
4845
5005
               type=unicode),
 
5006
        Option('strict',
 
5007
               help='Refuse to send if there are uncommitted changes in'
 
5008
               ' the working tree, --no-strict disables the check.'),
4846
5009
        Option('mail-to', help='Mail the request to this address.',
4847
5010
               type=unicode),
4848
5011
        'revision',
4849
5012
        'message',
4850
5013
        Option('body', help='Body for the email.', type=unicode),
4851
5014
        RegistryOption('format',
4852
 
                       help='Use the specified output format.', 
4853
 
                       lazy_registry=('bzrlib.send', 'format_registry'))
 
5015
                       help='Use the specified output format.',
 
5016
                       lazy_registry=('bzrlib.send', 'format_registry')),
4854
5017
        ]
4855
5018
 
4856
5019
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4857
5020
            no_patch=False, revision=None, remember=False, output=None,
4858
 
            format=None, mail_to=None, message=None, body=None, **kwargs):
 
5021
            format=None, mail_to=None, message=None, body=None,
 
5022
            strict=None, **kwargs):
4859
5023
        from bzrlib.send import send
4860
5024
        return send(submit_branch, revision, public_branch, remember,
4861
 
                         format, no_bundle, no_patch, output,
4862
 
                         kwargs.get('from', '.'), mail_to, message, body,
4863
 
                         self.outf)
 
5025
                    format, no_bundle, no_patch, output,
 
5026
                    kwargs.get('from', '.'), mail_to, message, body,
 
5027
                    self.outf,
 
5028
                    strict=strict)
4864
5029
 
4865
5030
 
4866
5031
class cmd_bundle_revisions(cmd_send):
4910
5075
               type=unicode),
4911
5076
        Option('output', short_name='o', help='Write directive to this file.',
4912
5077
               type=unicode),
 
5078
        Option('strict',
 
5079
               help='Refuse to bundle revisions if there are uncommitted'
 
5080
               ' changes in the working tree, --no-strict disables the check.'),
4913
5081
        'revision',
4914
5082
        RegistryOption('format',
4915
5083
                       help='Use the specified output format.',
4923
5091
 
4924
5092
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4925
5093
            no_patch=False, revision=None, remember=False, output=None,
4926
 
            format=None, **kwargs):
 
5094
            format=None, strict=None, **kwargs):
4927
5095
        if output is None:
4928
5096
            output = '-'
4929
5097
        from bzrlib.send import send
4930
5098
        return send(submit_branch, revision, public_branch, remember,
4931
5099
                         format, no_bundle, no_patch, output,
4932
5100
                         kwargs.get('from', '.'), None, None, None,
4933
 
                         self.outf)
 
5101
                         self.outf, strict=strict)
4934
5102
 
4935
5103
 
4936
5104
class cmd_tag(Command):
5108
5276
            ),
5109
5277
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5110
5278
        Option('force',
5111
 
               help='Perform reconfiguration even if local changes'
5112
 
               ' will be lost.')
 
5279
            help='Perform reconfiguration even if local changes'
 
5280
            ' will be lost.'),
 
5281
        Option('stacked-on',
 
5282
            help='Reconfigure a branch to be stacked on another branch.',
 
5283
            type=unicode,
 
5284
            ),
 
5285
        Option('unstacked',
 
5286
            help='Reconfigure a branch to be unstacked.  This '
 
5287
                'may require copying substantial data into it.',
 
5288
            ),
5113
5289
        ]
5114
5290
 
5115
 
    def run(self, location=None, target_type=None, bind_to=None, force=False):
 
5291
    def run(self, location=None, target_type=None, bind_to=None, force=False,
 
5292
            stacked_on=None,
 
5293
            unstacked=None):
5116
5294
        directory = bzrdir.BzrDir.open(location)
 
5295
        if stacked_on and unstacked:
 
5296
            raise BzrCommandError("Can't use both --stacked-on and --unstacked")
 
5297
        elif stacked_on is not None:
 
5298
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
5299
        elif unstacked:
 
5300
            reconfigure.ReconfigureUnstacked().apply(directory)
 
5301
        # At the moment you can use --stacked-on and a different
 
5302
        # reconfiguration shape at the same time; there seems no good reason
 
5303
        # to ban it.
5117
5304
        if target_type is None:
5118
 
            raise errors.BzrCommandError('No target configuration specified')
 
5305
            if stacked_on or unstacked:
 
5306
                return
 
5307
            else:
 
5308
                raise errors.BzrCommandError('No target configuration '
 
5309
                    'specified')
5119
5310
        elif target_type == 'branch':
5120
5311
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5121
5312
        elif target_type == 'tree':
5164
5355
 
5165
5356
    takes_args = ['to_location']
5166
5357
    takes_options = [Option('force',
5167
 
                        help='Switch even if local commits will be lost.')
 
5358
                        help='Switch even if local commits will be lost.'),
 
5359
                     Option('create-branch', short_name='b',
 
5360
                        help='Create the target branch from this one before'
 
5361
                             ' switching to it.'),
5168
5362
                     ]
5169
5363
 
5170
 
    def run(self, to_location, force=False):
 
5364
    def run(self, to_location, force=False, create_branch=False):
5171
5365
        from bzrlib import switch
5172
5366
        tree_location = '.'
5173
5367
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5175
5369
            branch = control_dir.open_branch()
5176
5370
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5177
5371
        except errors.NotBranchError:
 
5372
            branch = None
5178
5373
            had_explicit_nick = False
5179
 
        try:
5180
 
            to_branch = Branch.open(to_location)
5181
 
        except errors.NotBranchError:
5182
 
            this_url = self._get_branch_location(control_dir)
5183
 
            to_branch = Branch.open(
5184
 
                urlutils.join(this_url, '..', to_location))
 
5374
        if create_branch:
 
5375
            if branch is None:
 
5376
                raise errors.BzrCommandError('cannot create branch without'
 
5377
                                             ' source branch')
 
5378
            if '/' not in to_location and '\\' not in to_location:
 
5379
                # This path is meant to be relative to the existing branch
 
5380
                this_url = self._get_branch_location(control_dir)
 
5381
                to_location = urlutils.join(this_url, '..', to_location)
 
5382
            to_branch = branch.bzrdir.sprout(to_location,
 
5383
                                 possible_transports=[branch.bzrdir.root_transport],
 
5384
                                 source_branch=branch).open_branch()
 
5385
            # try:
 
5386
            #     from_branch = control_dir.open_branch()
 
5387
            # except errors.NotBranchError:
 
5388
            #     raise BzrCommandError('Cannot create a branch from this'
 
5389
            #         ' location when we cannot open this branch')
 
5390
            # from_branch.bzrdir.sprout(
 
5391
            pass
 
5392
        else:
 
5393
            try:
 
5394
                to_branch = Branch.open(to_location)
 
5395
            except errors.NotBranchError:
 
5396
                this_url = self._get_branch_location(control_dir)
 
5397
                to_branch = Branch.open(
 
5398
                    urlutils.join(this_url, '..', to_location))
5185
5399
        switch.switch(control_dir, to_branch, force)
5186
5400
        if had_explicit_nick:
5187
5401
            branch = control_dir.open_branch() #get the new branch!
5430
5644
        if writer is None:
5431
5645
            writer = bzrlib.option.diff_writer_registry.get()
5432
5646
        try:
5433
 
            Shelver.from_args(writer(sys.stdout), revision, all, file_list,
5434
 
                              message, destroy=destroy).run()
 
5647
            shelver = Shelver.from_args(writer(sys.stdout), revision, all,
 
5648
                file_list, message, destroy=destroy)
 
5649
            try:
 
5650
                shelver.run()
 
5651
            finally:
 
5652
                shelver.work_tree.unlock()
5435
5653
        except errors.UserAbort:
5436
5654
            return 0
5437
5655
 
5476
5694
 
5477
5695
    def run(self, shelf_id=None, action='apply'):
5478
5696
        from bzrlib.shelf_ui import Unshelver
5479
 
        Unshelver.from_args(shelf_id, action).run()
 
5697
        unshelver = Unshelver.from_args(shelf_id, action)
 
5698
        try:
 
5699
            unshelver.run()
 
5700
        finally:
 
5701
            unshelver.tree.unlock()
5480
5702
 
5481
5703
 
5482
5704
class cmd_clean_tree(Command):