~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Vincent Ladeuil
  • Date: 2009-05-05 15:31:34 UTC
  • mto: (4343.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4344.
  • Revision ID: v.ladeuil+lp@free.fr-20090505153134-q4bp4is9gywsmzrv
Clean up test for log formats.

* bzrlib/tests/blackbox/test_logformats.py:
Update tests to actual style.

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,
48
49
    ui,
49
50
    urlutils,
50
51
    views,
449
450
        except errors.NoWorkingTree:
450
451
            raise errors.BzrCommandError("No working tree to remove")
451
452
        except errors.NotLocalUrl:
452
 
            raise errors.BzrCommandError("You cannot remove the working tree"
453
 
                                         " of a remote path")
 
453
            raise errors.BzrCommandError("You cannot remove the working tree of a "
 
454
                                         "remote path")
454
455
        if not force:
455
 
            # XXX: What about pending merges ? -- vila 20090629
456
 
            if working.has_changes(working.basis_tree()):
 
456
            changes = working.changes_from(working.basis_tree())
 
457
            if changes.has_changed():
457
458
                raise errors.UncommittedChanges(working)
458
459
 
459
460
        working_path = working.bzrdir.root_transport.base
460
461
        branch_path = working.branch.bzrdir.root_transport.base
461
462
        if working_path != branch_path:
462
 
            raise errors.BzrCommandError("You cannot remove the working tree"
463
 
                                         " from a lightweight checkout")
 
463
            raise errors.BzrCommandError("You cannot remove the working tree from "
 
464
                                         "a lightweight checkout")
464
465
 
465
466
        d.destroy_workingtree()
466
467
 
473
474
 
474
475
    _see_also = ['info']
475
476
    takes_args = ['location?']
476
 
    takes_options = [
477
 
        Option('tree', help='Show revno of working tree'),
478
 
        ]
479
477
 
480
478
    @display_command
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')
 
479
    def run(self, location=u'.'):
 
480
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
 
481
        self.outf.write('\n')
506
482
 
507
483
 
508
484
class cmd_revision_info(Command):
518
494
            short_name='d',
519
495
            type=unicode,
520
496
            ),
521
 
        Option('tree', help='Show revno of working tree'),
522
497
        ]
523
498
 
524
499
    @display_command
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]))
 
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)
571
522
 
572
523
 
573
524
class cmd_add(Command):
659
610
                for glob, paths in ignored.items():
660
611
                    match_len += len(paths)
661
612
                self.outf.write("ignored %d file(s).\n" % match_len)
662
 
            self.outf.write("If you wish to add ignored files, "
663
 
                            "please add them explicitly by name. "
664
 
                            "(\"bzr ignored\" gives a list)\n")
 
613
            self.outf.write("If you wish to add some of these files,"
 
614
                            " please add them by name.\n")
665
615
 
666
616
 
667
617
class cmd_mkdir(Command):
799
749
        if len(names_list) < 2:
800
750
            raise errors.BzrCommandError("missing file argument")
801
751
        tree, rel_names = tree_files(names_list, canonicalize=False)
802
 
        tree.lock_tree_write()
 
752
        tree.lock_write()
803
753
        try:
804
754
            self._run(tree, names_list, rel_names, after)
805
755
        finally:
813
763
            raise errors.BzrCommandError('--after cannot be specified with'
814
764
                                         ' --auto.')
815
765
        work_tree, file_list = tree_files(names_list, default_branch='.')
816
 
        work_tree.lock_tree_write()
 
766
        work_tree.lock_write()
817
767
        try:
818
768
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
819
769
        finally:
932
882
            short_name='d',
933
883
            type=unicode,
934
884
            ),
935
 
        Option('local',
936
 
            help="Perform a local pull in a bound "
937
 
                 "branch.  Local pulls are not applied to "
938
 
                 "the master branch."
939
 
            ),
940
885
        ]
941
886
    takes_args = ['location?']
942
887
    encoding_type = 'replace'
943
888
 
944
889
    def run(self, location=None, remember=False, overwrite=False,
945
890
            revision=None, verbose=False,
946
 
            directory=None, local=False):
 
891
            directory=None):
947
892
        # FIXME: too much stuff is in the command class
948
893
        revision_id = None
949
894
        mergeable = None
955
900
        except errors.NoWorkingTree:
956
901
            tree_to = None
957
902
            branch_to = Branch.open_containing(directory)[0]
958
 
        
959
 
        if local and not branch_to.get_bound_location():
960
 
            raise errors.LocalRequiresBoundBranch()
961
903
 
962
904
        possible_transports = []
963
905
        if location is not None:
995
937
            if branch_to.get_parent() is None or remember:
996
938
                branch_to.set_parent(branch_from.base)
997
939
 
998
 
        if branch_from is not branch_to:
999
 
            branch_from.lock_read()
 
940
        if revision is not None:
 
941
            revision_id = revision.as_revision_id(branch_from)
 
942
 
 
943
        branch_to.lock_write()
1000
944
        try:
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()
 
945
            if tree_to is not None:
 
946
                view_info = _get_view_info_for_change_reporter(tree_to)
 
947
                change_reporter = delta._ChangeReporter(
 
948
                    unversioned_filter=tree_to.is_ignored, view_info=view_info)
 
949
                result = tree_to.pull(branch_from, overwrite, revision_id,
 
950
                                      change_reporter,
 
951
                                      possible_transports=possible_transports)
 
952
            else:
 
953
                result = branch_to.pull(branch_from, overwrite, revision_id)
 
954
 
 
955
            result.report(self.outf)
 
956
            if verbose and result.old_revid != result.new_revid:
 
957
                log.show_branch_change(branch_to, self.outf, result.old_revno,
 
958
                                       result.old_revid)
1025
959
        finally:
1026
 
            if branch_from is not branch_to:
1027
 
                branch_from.unlock()
 
960
            branch_to.unlock()
1028
961
 
1029
962
 
1030
963
class cmd_push(Command):
1077
1010
                'for the commit history. Only the work not present in the '
1078
1011
                'referenced branch is included in the branch created.',
1079
1012
            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.'),
1083
1013
        ]
1084
1014
    takes_args = ['location?']
1085
1015
    encoding_type = 'replace'
1087
1017
    def run(self, location=None, remember=False, overwrite=False,
1088
1018
        create_prefix=False, verbose=False, revision=None,
1089
1019
        use_existing_dir=False, directory=None, stacked_on=None,
1090
 
        stacked=False, strict=None):
 
1020
        stacked=False):
1091
1021
        from bzrlib.push import _show_push_branch
1092
1022
 
 
1023
        # Get the source branch and revision_id
1093
1024
        if directory is None:
1094
1025
            directory = '.'
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
 
1026
        br_from = Branch.open_containing(directory)[0]
1102
1027
        revision = _get_one_revision('push', revision)
1103
1028
        if revision is not None:
1104
1029
            revision_id = revision.in_history(br_from).rev_id
1105
1030
        else:
1106
1031
            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.')
1118
1032
 
1119
1033
        # Get the stacked_on branch, if any
1120
1034
        if stacked_on is not None:
1153
1067
 
1154
1068
 
1155
1069
class cmd_branch(Command):
1156
 
    """Create a new branch that is a copy of an existing branch.
 
1070
    """Create a new copy of a branch.
1157
1071
 
1158
1072
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
1159
1073
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
1178
1092
                'branch for all operations.'),
1179
1093
        Option('standalone',
1180
1094
               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.'),
1186
1095
        ]
1187
1096
    aliases = ['get', 'clone']
1188
1097
 
1189
1098
    def run(self, from_location, to_location=None, revision=None,
1190
 
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1191
 
            use_existing_dir=False):
 
1099
            hardlink=False, stacked=False, standalone=False, no_tree=False):
1192
1100
        from bzrlib.tag import _merge_tags_if_possible
1193
1101
 
1194
1102
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1195
1103
            from_location)
1196
 
        if (accelerator_tree is not None and
1197
 
            accelerator_tree.supports_content_filtering()):
1198
 
            accelerator_tree = None
1199
1104
        revision = _get_one_revision('branch', revision)
1200
1105
        br_from.lock_read()
1201
1106
        try:
1212
1117
            try:
1213
1118
                to_transport.mkdir('.')
1214
1119
            except errors.FileExists:
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)
 
1120
                raise errors.BzrCommandError('Target directory "%s" already'
 
1121
                                             ' exists.' % to_location)
1225
1122
            except errors.NoSuchFile:
1226
1123
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1227
1124
                                             % to_location)
1472
1369
            title='Deletion Strategy', value_switches=True, enum_switch=False,
1473
1370
            safe='Only delete files if they can be'
1474
1371
                 ' safely recovered (default).',
1475
 
            keep='Delete from bzr but leave the working copy.',
 
1372
            keep="Don't delete any files.",
1476
1373
            force='Delete all the specified files, even if they can not be '
1477
1374
                'recovered and even if they are non-empty directories.')]
1478
1375
    aliases = ['rm', 'del']
1713
1610
                branch.set_append_revisions_only(True)
1714
1611
            except errors.UpgradeRequired:
1715
1612
                raise errors.BzrCommandError('This branch format cannot be set'
1716
 
                    ' to append-revisions-only.  Try --default.')
 
1613
                    ' to append-revisions-only.  Try --experimental-branch6')
1717
1614
        if not is_quiet():
1718
1615
            from bzrlib.info import describe_layout, describe_format
1719
1616
            try:
2182
2079
 
2183
2080
      When exploring non-mainline history on large projects with deep
2184
2081
      history, the performance of log can be greatly improved by installing
2185
 
      the historycache plugin. This plugin buffers historical information
 
2082
      the revnocache plugin. This plugin buffers historical information
2186
2083
      trading disk space for faster speed.
2187
2084
    """
2188
2085
    takes_args = ['file*']
2458
2355
 
2459
2356
        if path is None:
2460
2357
            fs_path = '.'
 
2358
            prefix = ''
2461
2359
        else:
2462
2360
            if from_root:
2463
2361
                raise errors.BzrCommandError('cannot specify both --from-root'
2464
2362
                                             ' and PATH')
2465
2363
            fs_path = path
 
2364
            prefix = path
2466
2365
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2467
2366
            fs_path)
2468
 
 
2469
 
        # Calculate the prefix to use
2470
 
        prefix = None
2471
2367
        if from_root:
2472
 
            if relpath:
2473
 
                prefix = relpath + '/'
2474
 
        elif fs_path != '.':
2475
 
            prefix = fs_path + '/'
2476
 
 
 
2368
            relpath = u''
 
2369
        elif relpath:
 
2370
            relpath += '/'
2477
2371
        if revision is not None or tree is None:
2478
2372
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2479
2373
 
2487
2381
 
2488
2382
        tree.lock_read()
2489
2383
        try:
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:
 
2384
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
 
2385
                if fp.startswith(relpath):
 
2386
                    rp = fp[len(relpath):]
 
2387
                    fp = osutils.pathjoin(prefix, rp)
 
2388
                    if not recursive and '/' in rp:
 
2389
                        continue
 
2390
                    if not all and not selection[fc]:
 
2391
                        continue
 
2392
                    if kind is not None and fkind != kind:
 
2393
                        continue
 
2394
                    if apply_view:
 
2395
                        try:
 
2396
                            views.check_path_in_view(tree, fp)
 
2397
                        except errors.FileOutsideView:
 
2398
                            continue
 
2399
                    kindch = entry.kind_character()
 
2400
                    outstring = fp + kindch
 
2401
                    if verbose:
 
2402
                        outstring = '%-8s %s' % (fc, outstring)
 
2403
                        if show_ids and fid is not None:
 
2404
                            outstring = "%-50s %s" % (outstring, fid)
 
2405
                        self.outf.write(outstring + '\n')
 
2406
                    elif null:
 
2407
                        self.outf.write(fp + '\0')
 
2408
                        if show_ids:
 
2409
                            if fid is not None:
 
2410
                                self.outf.write(fid)
 
2411
                            self.outf.write('\0')
 
2412
                        self.outf.flush()
 
2413
                    else:
2527
2414
                        if fid is not None:
2528
2415
                            my_id = fid
2529
2416
                        else:
2530
2417
                            my_id = ''
2531
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2532
 
                    else:
2533
 
                        self.outf.write(outstring + '\n')
 
2418
                        if show_ids:
 
2419
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2420
                        else:
 
2421
                            self.outf.write(outstring + '\n')
2534
2422
        finally:
2535
2423
            tree.unlock()
2536
2424
 
2829
2717
class cmd_commit(Command):
2830
2718
    """Commit changes into a new revision.
2831
2719
 
2832
 
    An explanatory message needs to be given for each commit. This is
2833
 
    often done by using the --message option (getting the message from the
2834
 
    command line) or by using the --file option (getting the message from
2835
 
    a file). If neither of these options is given, an editor is opened for
2836
 
    the user to enter the message. To see the changed files in the
2837
 
    boilerplate text loaded into the editor, use the --show-diff option.
2838
 
 
2839
 
    By default, the entire tree is committed and the person doing the
2840
 
    commit is assumed to be the author. These defaults can be overridden
2841
 
    as explained below.
2842
 
 
2843
 
    :Selective commits:
2844
 
 
2845
 
      If selected files are specified, only changes to those files are
2846
 
      committed.  If a directory is specified then the directory and
2847
 
      everything within it is committed.
2848
 
  
2849
 
      When excludes are given, they take precedence over selected files.
2850
 
      For example, to commit only changes within foo, but not changes
2851
 
      within foo/bar::
2852
 
  
2853
 
        bzr commit foo -x foo/bar
2854
 
  
2855
 
      A selective commit after a merge is not yet supported.
2856
 
 
2857
 
    :Custom authors:
2858
 
 
2859
 
      If the author of the change is not the same person as the committer,
2860
 
      you can specify the author's name using the --author option. The
2861
 
      name should be in the same format as a committer-id, e.g.
2862
 
      "John Doe <jdoe@example.com>". If there is more than one author of
2863
 
      the change you can specify the option multiple times, once for each
2864
 
      author.
2865
 
  
2866
 
    :Checks:
2867
 
 
2868
 
      A common mistake is to forget to add a new file or directory before
2869
 
      running the commit command. The --strict option checks for unknown
2870
 
      files and aborts the commit if any are found. More advanced pre-commit
2871
 
      checks can be implemented by defining hooks. See ``bzr help hooks``
2872
 
      for details.
2873
 
 
2874
 
    :Things to note:
2875
 
 
2876
 
      If you accidentially commit the wrong changes or make a spelling
2877
 
      mistake in the commit message say, you can use the uncommit command
2878
 
      to undo it. See ``bzr help uncommit`` for details.
2879
 
 
2880
 
      Hooks can also be configured to run after a commit. This allows you
2881
 
      to trigger updates to external systems like bug trackers. The --fixes
2882
 
      option can be used to record the association between a revision and
2883
 
      one or more bugs. See ``bzr help bugs`` for details.
2884
 
 
2885
 
      A selective commit may fail in some cases where the committed
2886
 
      tree would be invalid. Consider::
2887
 
  
2888
 
        bzr init foo
2889
 
        mkdir foo/bar
2890
 
        bzr add foo/bar
2891
 
        bzr commit foo -m "committing foo"
2892
 
        bzr mv foo/bar foo/baz
2893
 
        mkdir foo/bar
2894
 
        bzr add foo/bar
2895
 
        bzr commit foo/bar -m "committing bar but not baz"
2896
 
  
2897
 
      In the example above, the last commit will fail by design. This gives
2898
 
      the user the opportunity to decide whether they want to commit the
2899
 
      rename at the same time, separately first, or not at all. (As a general
2900
 
      rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
 
2720
    If no arguments are given, the entire tree is committed.
 
2721
 
 
2722
    If selected files are specified, only changes to those files are
 
2723
    committed.  If a directory is specified then the directory and everything
 
2724
    within it is committed.
 
2725
 
 
2726
    When excludes are given, they take precedence over selected files.
 
2727
    For example, too commit only changes within foo, but not changes within
 
2728
    foo/bar::
 
2729
 
 
2730
      bzr commit foo -x foo/bar
 
2731
 
 
2732
    If author of the change is not the same person as the committer, you can
 
2733
    specify the author's name using the --author option. The name should be
 
2734
    in the same format as a committer-id, e.g. "John Doe <jdoe@example.com>".
 
2735
    If there is more than one author of the change you can specify the option
 
2736
    multiple times, once for each author.
 
2737
 
 
2738
    A selected-file commit may fail in some cases where the committed
 
2739
    tree would be invalid. Consider::
 
2740
 
 
2741
      bzr init foo
 
2742
      mkdir foo/bar
 
2743
      bzr add foo/bar
 
2744
      bzr commit foo -m "committing foo"
 
2745
      bzr mv foo/bar foo/baz
 
2746
      mkdir foo/bar
 
2747
      bzr add foo/bar
 
2748
      bzr commit foo/bar -m "committing bar but not baz"
 
2749
 
 
2750
    In the example above, the last commit will fail by design. This gives
 
2751
    the user the opportunity to decide whether they want to commit the
 
2752
    rename at the same time, separately first, or not at all. (As a general
 
2753
    rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
 
2754
 
 
2755
    Note: A selected-file commit after a merge is not yet supported.
2901
2756
    """
2902
2757
    # TODO: Run hooks on tree to-be-committed, and after commit.
2903
2758
 
2908
2763
 
2909
2764
    # XXX: verbose currently does nothing
2910
2765
 
2911
 
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
 
2766
    _see_also = ['bugs', 'uncommit']
2912
2767
    takes_args = ['selected*']
2913
2768
    takes_options = [
2914
2769
            ListOption('exclude', type=str, short_name='x',
3035
2890
        except PointlessCommit:
3036
2891
            # FIXME: This should really happen before the file is read in;
3037
2892
            # perhaps prepare the commit; get the message; then actually commit
3038
 
            raise errors.BzrCommandError("No changes to commit."
3039
 
                              " Use --unchanged to commit anyhow.")
 
2893
            raise errors.BzrCommandError("no changes to commit."
 
2894
                              " use --unchanged to commit anyhow")
3040
2895
        except ConflictsInTree:
3041
2896
            raise errors.BzrCommandError('Conflicts detected in working '
3042
2897
                'tree.  Use "bzr conflicts" to list, "bzr resolve FILE" to'
3060
2915
    The working tree and branch checks will only give output if a problem is
3061
2916
    detected. The output fields of the repository check are:
3062
2917
 
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.
 
2918
        revisions: This is just the number of revisions checked.  It doesn't
 
2919
            indicate a problem.
 
2920
        versionedfiles: This is just the number of versionedfiles checked.  It
 
2921
            doesn't indicate a problem.
 
2922
        unreferenced ancestors: Texts that are ancestors of other texts, but
 
2923
            are not properly referenced by the revision ancestry.  This is a
 
2924
            subtle problem that Bazaar can work around.
 
2925
        unique file texts: This is the total number of unique file contents
 
2926
            seen in the checked revisions.  It does not indicate a problem.
 
2927
        repeated file texts: This is the total number of repeated texts seen
 
2928
            in the checked revisions.  Texts can be repeated when their file
 
2929
            entries are modified, but the file contents are not.  It does not
 
2930
            indicate a problem.
3085
2931
 
3086
2932
    If no restrictions are specified, all Bazaar data that is found at the given
3087
2933
    location will be checked.
3564
3410
    merge refuses to run if there are any uncommitted changes, unless
3565
3411
    --force is given.
3566
3412
 
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
 
 
3570
3413
    :Examples:
3571
3414
        To merge the latest revision from bzr.dev::
3572
3415
 
3610
3453
               short_name='d',
3611
3454
               type=unicode,
3612
3455
               ),
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')
 
3456
        Option('preview', help='Instead of merging, show a diff of the merge.')
3617
3457
    ]
3618
3458
 
3619
3459
    def run(self, location=None, revision=None, force=False,
3621
3461
            uncommitted=False, pull=False,
3622
3462
            directory=None,
3623
3463
            preview=False,
3624
 
            interactive=False,
3625
3464
            ):
3626
3465
        if merge_type is None:
3627
3466
            merge_type = _mod_merge.Merge3Merger
3639
3478
        except errors.NoSuchRevision:
3640
3479
            basis_tree = tree.basis_tree()
3641
3480
        if not force:
3642
 
            if tree.has_changes(basis_tree):
 
3481
            changes = tree.changes_from(basis_tree)
 
3482
            if changes.has_changed():
3643
3483
                raise errors.UncommittedChanges(tree)
3644
3484
 
3645
3485
        view_info = _get_view_info_for_change_reporter(tree)
3672
3512
                if revision is not None and len(revision) > 0:
3673
3513
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3674
3514
                        ' --revision at the same time.')
3675
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3676
 
                                                          cleanups)
 
3515
                location = self._select_branch_location(tree, location)[0]
 
3516
                other_tree, other_path = WorkingTree.open_containing(location)
 
3517
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
 
3518
                    pb)
3677
3519
                allow_pending = False
 
3520
                if other_path != '':
 
3521
                    merger.interesting_files = [other_path]
3678
3522
 
3679
3523
            if merger is None:
3680
3524
                merger, allow_pending = self._get_merger_from_branch(tree,
3698
3542
                    return 0
3699
3543
            merger.check_basis(False)
3700
3544
            if preview:
3701
 
                return self._do_preview(merger, cleanups)
3702
 
            elif interactive:
3703
 
                return self._do_interactive(merger, cleanups)
 
3545
                return self._do_preview(merger)
3704
3546
            else:
3705
3547
                return self._do_merge(merger, change_reporter, allow_pending,
3706
3548
                                      verified)
3708
3550
            for cleanup in reversed(cleanups):
3709
3551
                cleanup()
3710
3552
 
3711
 
    def _get_preview(self, merger, cleanups):
 
3553
    def _do_preview(self, merger):
 
3554
        from bzrlib.diff import show_diff_trees
3712
3555
        tree_merger = merger.make_merger()
3713
3556
        tt = tree_merger.make_preview_transform()
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='')
 
3557
        try:
 
3558
            result_tree = tt.get_preview_tree()
 
3559
            show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3560
                            old_label='', new_label='')
 
3561
        finally:
 
3562
            tt.finalize()
3723
3563
 
3724
3564
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3725
3565
        merger.change_reporter = change_reporter
3733
3573
        else:
3734
3574
            return 0
3735
3575
 
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
 
 
3751
3576
    def sanity_check_merger(self, merger):
3752
3577
        if (merger.show_base and
3753
3578
            not merger.merge_type is _mod_merge.Merge3Merger):
3788
3613
            base_branch, base_path = Branch.open_containing(base_loc,
3789
3614
                possible_transports)
3790
3615
        # Find the revision ids
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:
 
3616
        if revision is None or len(revision) < 1 or revision[-1] is None:
3799
3617
            other_revision_id = _mod_revision.ensure_null(
3800
3618
                other_branch.last_revision())
 
3619
        else:
 
3620
            other_revision_id = revision[-1].as_revision_id(other_branch)
 
3621
        if (revision is not None and len(revision) == 2
 
3622
            and revision[0] is not None):
 
3623
            base_revision_id = revision[0].as_revision_id(base_branch)
 
3624
        else:
 
3625
            base_revision_id = None
3801
3626
        # Remember where we merge from
3802
3627
        if ((remember or tree.branch.get_submit_branch() is None) and
3803
3628
             user_location is not None):
3812
3637
            allow_pending = True
3813
3638
        return merger, allow_pending
3814
3639
 
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
 
 
3831
3640
    def _select_branch_location(self, tree, user_location, revision=None,
3832
3641
                                index=None):
3833
3642
        """Select a branch location, according to possible inputs.
4688
4497
    takes_options = [
4689
4498
        Option('inet',
4690
4499
               help='Serve on stdin/out for use from inetd or sshd.'),
4691
 
        RegistryOption('protocol', 
4692
 
               help="Protocol to serve.", 
4693
 
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
4694
 
               value_switches=True),
4695
4500
        Option('port',
4696
4501
               help='Listen for connections on nominated port of the form '
4697
4502
                    '[hostname:]portnumber.  Passing 0 as the port number will '
4698
 
                    'result in a dynamically allocated port.  The default port '
4699
 
                    'depends on the protocol.',
 
4503
                    'result in a dynamically allocated port.  The default port is '
 
4504
                    '4155.',
4700
4505
               type=str),
4701
4506
        Option('directory',
4702
4507
               help='Serve contents of this directory.',
4708
4513
                ),
4709
4514
        ]
4710
4515
 
 
4516
    def run_smart_server(self, smart_server):
 
4517
        """Run 'smart_server' forever, with no UI output at all."""
 
4518
        # For the duration of this server, no UI output is permitted. note
 
4519
        # that this may cause problems with blackbox tests. This should be
 
4520
        # changed with care though, as we dont want to use bandwidth sending
 
4521
        # progress over stderr to smart server clients!
 
4522
        from bzrlib import lockdir
 
4523
        old_factory = ui.ui_factory
 
4524
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
 
4525
        try:
 
4526
            ui.ui_factory = ui.SilentUIFactory()
 
4527
            lockdir._DEFAULT_TIMEOUT_SECONDS = 0
 
4528
            smart_server.serve()
 
4529
        finally:
 
4530
            ui.ui_factory = old_factory
 
4531
            lockdir._DEFAULT_TIMEOUT_SECONDS = old_lockdir_timeout
 
4532
 
4711
4533
    def get_host_and_port(self, port):
4712
4534
        """Return the host and port to run the smart server on.
4713
4535
 
4714
 
        If 'port' is None, None will be returned for the host and port.
 
4536
        If 'port' is None, the default host (`medium.BZR_DEFAULT_INTERFACE`)
 
4537
        and port (`medium.BZR_DEFAULT_PORT`) will be used.
4715
4538
 
4716
4539
        If 'port' has a colon in it, the string before the colon will be
4717
4540
        interpreted as the host.
4720
4543
        :return: A tuple of (host, port), where 'host' is a host name or IP,
4721
4544
            and port is an integer TCP/IP port.
4722
4545
        """
4723
 
        host = None
4724
 
        if port is not None:
 
4546
        from bzrlib.smart import medium
 
4547
        host = medium.BZR_DEFAULT_INTERFACE
 
4548
        if port is None:
 
4549
            port = medium.BZR_DEFAULT_PORT
 
4550
        else:
4725
4551
            if ':' in port:
4726
4552
                host, port = port.split(':')
4727
4553
            port = int(port)
4728
4554
        return host, port
4729
4555
 
4730
 
    def run(self, port=None, inet=False, directory=None, allow_writes=False,
4731
 
            protocol=None):
4732
 
        from bzrlib.transport import get_transport, transport_server_registry
 
4556
    def get_smart_server(self, transport, inet, port):
 
4557
        """Construct a smart server.
 
4558
 
 
4559
        :param transport: The base transport from which branches will be
 
4560
            served.
 
4561
        :param inet: If True, serve over stdin and stdout. Used for running
 
4562
            from inet.
 
4563
        :param port: The port to listen on. By default, it's `
 
4564
            medium.BZR_DEFAULT_PORT`. See `get_host_and_port` for more
 
4565
            information.
 
4566
        :return: A smart server.
 
4567
        """
 
4568
        from bzrlib.smart import medium, server
 
4569
        if inet:
 
4570
            smart_server = medium.SmartServerPipeStreamMedium(
 
4571
                sys.stdin, sys.stdout, transport)
 
4572
        else:
 
4573
            host, port = self.get_host_and_port(port)
 
4574
            smart_server = server.SmartTCPServer(
 
4575
                transport, host=host, port=port)
 
4576
            note('listening on port: %s' % smart_server.port)
 
4577
        return smart_server
 
4578
 
 
4579
    def run(self, port=None, inet=False, directory=None, allow_writes=False):
 
4580
        from bzrlib.transport import get_transport
 
4581
        from bzrlib.transport.chroot import ChrootServer
4733
4582
        if directory is None:
4734
4583
            directory = os.getcwd()
4735
 
        if protocol is None:
4736
 
            protocol = transport_server_registry.get()
4737
 
        host, port = self.get_host_and_port(port)
4738
4584
        url = urlutils.local_path_to_url(directory)
4739
4585
        if not allow_writes:
4740
4586
            url = 'readonly+' + url
4741
 
        transport = get_transport(url)
4742
 
        protocol(transport, host, port, inet)
 
4587
        chroot_server = ChrootServer(get_transport(url))
 
4588
        chroot_server.setUp()
 
4589
        t = get_transport(chroot_server.get_url())
 
4590
        smart_server = self.get_smart_server(t, inet, port)
 
4591
        self.run_smart_server(smart_server)
4743
4592
 
4744
4593
 
4745
4594
class cmd_join(Command):
4811
4660
        try:
4812
4661
            containing_tree.extract(sub_id)
4813
4662
        except errors.RootNotRich:
4814
 
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
 
4663
            raise errors.UpgradeRequired(containing_tree.branch.base)
4815
4664
 
4816
4665
 
4817
4666
class cmd_merge_directive(Command):
4986
4835
               help='Write merge directive to this file; '
4987
4836
                    'use - for stdout.',
4988
4837
               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.'),
4992
4838
        Option('mail-to', help='Mail the request to this address.',
4993
4839
               type=unicode),
4994
4840
        'revision',
4995
4841
        'message',
4996
4842
        Option('body', help='Body for the email.', type=unicode),
4997
 
        RegistryOption('format',
4998
 
                       help='Use the specified output format.',
4999
 
                       lazy_registry=('bzrlib.send', 'format_registry')),
 
4843
        RegistryOption.from_kwargs('format',
 
4844
        'Use the specified output format.',
 
4845
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
 
4846
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
5000
4847
        ]
5001
4848
 
5002
4849
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5003
4850
            no_patch=False, revision=None, remember=False, output=None,
5004
 
            format=None, mail_to=None, message=None, body=None,
5005
 
            strict=None, **kwargs):
5006
 
        from bzrlib.send import send
5007
 
        return send(submit_branch, revision, public_branch, remember,
5008
 
                    format, no_bundle, no_patch, output,
5009
 
                    kwargs.get('from', '.'), mail_to, message, body,
5010
 
                    self.outf,
5011
 
                    strict=strict)
 
4851
            format='4', mail_to=None, message=None, body=None, **kwargs):
 
4852
        return self._run(submit_branch, revision, public_branch, remember,
 
4853
                         format, no_bundle, no_patch, output,
 
4854
                         kwargs.get('from', '.'), mail_to, message, body)
 
4855
 
 
4856
    def _run(self, submit_branch, revision, public_branch, remember, format,
 
4857
             no_bundle, no_patch, output, from_, mail_to, message, body):
 
4858
        from bzrlib.revision import NULL_REVISION
 
4859
        branch = Branch.open_containing(from_)[0]
 
4860
        if output is None:
 
4861
            outfile = cStringIO.StringIO()
 
4862
        elif output == '-':
 
4863
            outfile = self.outf
 
4864
        else:
 
4865
            outfile = open(output, 'wb')
 
4866
        # we may need to write data into branch's repository to calculate
 
4867
        # the data to send.
 
4868
        branch.lock_write()
 
4869
        try:
 
4870
            if output is None:
 
4871
                config = branch.get_config()
 
4872
                if mail_to is None:
 
4873
                    mail_to = config.get_user_option('submit_to')
 
4874
                mail_client = config.get_mail_client()
 
4875
                if (not getattr(mail_client, 'supports_body', False)
 
4876
                    and body is not None):
 
4877
                    raise errors.BzrCommandError(
 
4878
                        'Mail client "%s" does not support specifying body' %
 
4879
                        mail_client.__class__.__name__)
 
4880
            if remember and submit_branch is None:
 
4881
                raise errors.BzrCommandError(
 
4882
                    '--remember requires a branch to be specified.')
 
4883
            stored_submit_branch = branch.get_submit_branch()
 
4884
            remembered_submit_branch = None
 
4885
            if submit_branch is None:
 
4886
                submit_branch = stored_submit_branch
 
4887
                remembered_submit_branch = "submit"
 
4888
            else:
 
4889
                if stored_submit_branch is None or remember:
 
4890
                    branch.set_submit_branch(submit_branch)
 
4891
            if submit_branch is None:
 
4892
                submit_branch = branch.get_parent()
 
4893
                remembered_submit_branch = "parent"
 
4894
            if submit_branch is None:
 
4895
                raise errors.BzrCommandError('No submit branch known or'
 
4896
                                             ' specified')
 
4897
            if remembered_submit_branch is not None:
 
4898
                note('Using saved %s location "%s" to determine what '
 
4899
                        'changes to submit.', remembered_submit_branch,
 
4900
                        submit_branch)
 
4901
 
 
4902
            if mail_to is None:
 
4903
                submit_config = Branch.open(submit_branch).get_config()
 
4904
                mail_to = submit_config.get_user_option("child_submit_to")
 
4905
 
 
4906
            stored_public_branch = branch.get_public_branch()
 
4907
            if public_branch is None:
 
4908
                public_branch = stored_public_branch
 
4909
            elif stored_public_branch is None or remember:
 
4910
                branch.set_public_branch(public_branch)
 
4911
            if no_bundle and public_branch is None:
 
4912
                raise errors.BzrCommandError('No public branch specified or'
 
4913
                                             ' known')
 
4914
            base_revision_id = None
 
4915
            revision_id = None
 
4916
            if revision is not None:
 
4917
                if len(revision) > 2:
 
4918
                    raise errors.BzrCommandError('bzr send takes '
 
4919
                        'at most two one revision identifiers')
 
4920
                revision_id = revision[-1].as_revision_id(branch)
 
4921
                if len(revision) == 2:
 
4922
                    base_revision_id = revision[0].as_revision_id(branch)
 
4923
            if revision_id is None:
 
4924
                revision_id = branch.last_revision()
 
4925
            if revision_id == NULL_REVISION:
 
4926
                raise errors.BzrCommandError('No revisions to submit.')
 
4927
            if format == '4':
 
4928
                directive = merge_directive.MergeDirective2.from_objects(
 
4929
                    branch.repository, revision_id, time.time(),
 
4930
                    osutils.local_time_offset(), submit_branch,
 
4931
                    public_branch=public_branch, include_patch=not no_patch,
 
4932
                    include_bundle=not no_bundle, message=message,
 
4933
                    base_revision_id=base_revision_id)
 
4934
            elif format == '0.9':
 
4935
                if not no_bundle:
 
4936
                    if not no_patch:
 
4937
                        patch_type = 'bundle'
 
4938
                    else:
 
4939
                        raise errors.BzrCommandError('Format 0.9 does not'
 
4940
                            ' permit bundle with no patch')
 
4941
                else:
 
4942
                    if not no_patch:
 
4943
                        patch_type = 'diff'
 
4944
                    else:
 
4945
                        patch_type = None
 
4946
                directive = merge_directive.MergeDirective.from_objects(
 
4947
                    branch.repository, revision_id, time.time(),
 
4948
                    osutils.local_time_offset(), submit_branch,
 
4949
                    public_branch=public_branch, patch_type=patch_type,
 
4950
                    message=message)
 
4951
 
 
4952
            outfile.writelines(directive.to_lines())
 
4953
            if output is None:
 
4954
                subject = '[MERGE] '
 
4955
                if message is not None:
 
4956
                    subject += message
 
4957
                else:
 
4958
                    revision = branch.repository.get_revision(revision_id)
 
4959
                    subject += revision.get_summary()
 
4960
                basename = directive.get_disk_name(branch)
 
4961
                mail_client.compose_merge_request(mail_to, subject,
 
4962
                                                  outfile.getvalue(),
 
4963
                                                  basename, body)
 
4964
        finally:
 
4965
            if output != '-':
 
4966
                outfile.close()
 
4967
            branch.unlock()
5012
4968
 
5013
4969
 
5014
4970
class cmd_bundle_revisions(cmd_send):
 
4971
 
5015
4972
    """Create a merge-directive for submitting changes.
5016
4973
 
5017
4974
    A merge directive provides many things needed for requesting merges:
5058
5015
               type=unicode),
5059
5016
        Option('output', short_name='o', help='Write directive to this file.',
5060
5017
               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.'),
5064
5018
        'revision',
5065
 
        RegistryOption('format',
5066
 
                       help='Use the specified output format.',
5067
 
                       lazy_registry=('bzrlib.send', 'format_registry')),
 
5019
        RegistryOption.from_kwargs('format',
 
5020
        'Use the specified output format.',
 
5021
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
 
5022
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
5068
5023
        ]
5069
5024
    aliases = ['bundle']
5070
5025
 
5074
5029
 
5075
5030
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5076
5031
            no_patch=False, revision=None, remember=False, output=None,
5077
 
            format=None, strict=None, **kwargs):
 
5032
            format='4', **kwargs):
5078
5033
        if output is None:
5079
5034
            output = '-'
5080
 
        from bzrlib.send import send
5081
 
        return send(submit_branch, revision, public_branch, remember,
 
5035
        return self._run(submit_branch, revision, public_branch, remember,
5082
5036
                         format, no_bundle, no_patch, output,
5083
 
                         kwargs.get('from', '.'), None, None, None,
5084
 
                         self.outf, strict=strict)
 
5037
                         kwargs.get('from', '.'), None, None, None)
5085
5038
 
5086
5039
 
5087
5040
class cmd_tag(Command):
5182
5135
        if not tags:
5183
5136
            return
5184
5137
 
5185
 
        branch.lock_read()
5186
 
        try:
5187
 
            if revision:
 
5138
        if revision:
 
5139
            branch.lock_read()
 
5140
            try:
5188
5141
                graph = branch.repository.get_graph()
5189
5142
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5190
5143
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5191
5144
                # only show revisions between revid1 and revid2 (inclusive)
5192
5145
                tags = [(tag, revid) for tag, revid in tags if
5193
5146
                    graph.is_between(revid, revid1, revid2)]
5194
 
            if sort == 'alpha':
5195
 
                tags.sort()
5196
 
            elif sort == 'time':
5197
 
                timestamps = {}
5198
 
                for tag, revid in tags:
5199
 
                    try:
5200
 
                        revobj = branch.repository.get_revision(revid)
5201
 
                    except errors.NoSuchRevision:
5202
 
                        timestamp = sys.maxint # place them at the end
5203
 
                    else:
5204
 
                        timestamp = revobj.timestamp
5205
 
                    timestamps[revid] = timestamp
5206
 
                tags.sort(key=lambda x: timestamps[x[1]])
5207
 
            if not show_ids:
5208
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5209
 
                for index, (tag, revid) in enumerate(tags):
5210
 
                    try:
5211
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5212
 
                        if isinstance(revno, tuple):
5213
 
                            revno = '.'.join(map(str, revno))
5214
 
                    except errors.NoSuchRevision:
5215
 
                        # Bad tag data/merges can lead to tagged revisions
5216
 
                        # which are not in this branch. Fail gracefully ...
5217
 
                        revno = '?'
5218
 
                    tags[index] = (tag, revno)
5219
 
        finally:
5220
 
            branch.unlock()
 
5147
            finally:
 
5148
                branch.unlock()
 
5149
        if sort == 'alpha':
 
5150
            tags.sort()
 
5151
        elif sort == 'time':
 
5152
            timestamps = {}
 
5153
            for tag, revid in tags:
 
5154
                try:
 
5155
                    revobj = branch.repository.get_revision(revid)
 
5156
                except errors.NoSuchRevision:
 
5157
                    timestamp = sys.maxint # place them at the end
 
5158
                else:
 
5159
                    timestamp = revobj.timestamp
 
5160
                timestamps[revid] = timestamp
 
5161
            tags.sort(key=lambda x: timestamps[x[1]])
 
5162
        if not show_ids:
 
5163
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5164
            revno_map = branch.get_revision_id_to_revno_map()
 
5165
            tags = [ (tag, '.'.join(map(str, revno_map.get(revid, ('?',)))))
 
5166
                        for tag, revid in tags ]
5221
5167
        for tag, revspec in tags:
5222
5168
            self.outf.write('%-20s %s\n' % (tag, revspec))
5223
5169
 
5259
5205
            ),
5260
5206
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5261
5207
        Option('force',
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
 
            ),
 
5208
               help='Perform reconfiguration even if local changes'
 
5209
               ' will be lost.')
5272
5210
        ]
5273
5211
 
5274
 
    def run(self, location=None, target_type=None, bind_to=None, force=False,
5275
 
            stacked_on=None,
5276
 
            unstacked=None):
 
5212
    def run(self, location=None, target_type=None, bind_to=None, force=False):
5277
5213
        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.
5287
5214
        if target_type is None:
5288
 
            if stacked_on or unstacked:
5289
 
                return
5290
 
            else:
5291
 
                raise errors.BzrCommandError('No target configuration '
5292
 
                    'specified')
 
5215
            raise errors.BzrCommandError('No target configuration specified')
5293
5216
        elif target_type == 'branch':
5294
5217
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5295
5218
        elif target_type == 'tree':
5338
5261
 
5339
5262
    takes_args = ['to_location']
5340
5263
    takes_options = [Option('force',
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.'),
 
5264
                        help='Switch even if local commits will be lost.')
5345
5265
                     ]
5346
5266
 
5347
 
    def run(self, to_location, force=False, create_branch=False):
 
5267
    def run(self, to_location, force=False):
5348
5268
        from bzrlib import switch
5349
5269
        tree_location = '.'
5350
5270
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
 
5271
        branch = control_dir.open_branch()
5351
5272
        try:
5352
 
            branch = control_dir.open_branch()
5353
 
            had_explicit_nick = branch.get_config().has_explicit_nickname()
 
5273
            to_branch = Branch.open(to_location)
5354
5274
        except errors.NotBranchError:
5355
 
            branch = None
5356
 
            had_explicit_nick = False
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))
 
5275
            this_branch = control_dir.open_branch()
 
5276
            # This may be a heavy checkout, where we want the master branch
 
5277
            this_url = this_branch.get_bound_location()
 
5278
            # If not, use a local sibling
 
5279
            if this_url is None:
 
5280
                this_url = this_branch.base
 
5281
            to_branch = Branch.open(
 
5282
                urlutils.join(this_url, '..', to_location))
5382
5283
        switch.switch(control_dir, to_branch, force)
5383
 
        if had_explicit_nick:
 
5284
        if branch.get_config().has_explicit_nickname():
5384
5285
            branch = control_dir.open_branch() #get the new branch!
5385
5286
            branch.nick = to_branch.nick
5386
5287
        note('Switched to branch: %s',
5387
5288
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
5388
5289
 
5389
 
    def _get_branch_location(self, control_dir):
5390
 
        """Return location of branch for this control dir."""
5391
 
        try:
5392
 
            this_branch = control_dir.open_branch()
5393
 
            # This may be a heavy checkout, where we want the master branch
5394
 
            master_location = this_branch.get_bound_location()
5395
 
            if master_location is not None:
5396
 
                return master_location
5397
 
            # If not, use a local sibling
5398
 
            return this_branch.base
5399
 
        except errors.NotBranchError:
5400
 
            format = control_dir.find_branch_format()
5401
 
            if getattr(format, 'get_reference', None) is not None:
5402
 
                return format.get_reference(control_dir)
5403
 
            else:
5404
 
                return control_dir.root_transport.base
5405
 
 
5406
5290
 
5407
5291
class cmd_view(Command):
5408
5292
    """Manage filtered views.