~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Joe Julian
  • Date: 2010-01-10 02:25:31 UTC
  • mto: (4634.119.7 2.0)
  • mto: This revision was merged to the branch mainline in revision 4959.
  • Revision ID: joe@julianfamily.org-20100110022531-wqk61rsagz8xsiga
Added MANIFEST.in to allow bdist_rpm to have all the required include files and tools. bdist_rpm will still fail to build correctly on some distributions due to a disttools bug http://bugs.python.org/issue644744

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,
79
78
 
80
79
 
81
80
def tree_files_for_add(file_list):
82
 
    """Add handles files a bit differently so it a custom implementation."""
 
81
    """
 
82
    Return a tree and list of absolute paths from a file list.
 
83
 
 
84
    Similar to tree_files, but add handles files a bit differently, so it a
 
85
    custom implementation.  In particular, MutableTreeTree.smart_add expects
 
86
    absolute paths, which it immediately converts to relative paths.
 
87
    """
 
88
    # FIXME Would be nice to just return the relative paths like
 
89
    # internal_tree_files does, but there are a large number of unit tests
 
90
    # that assume the current interface to mutabletree.smart_add
83
91
    if file_list:
84
 
        tree = WorkingTree.open_containing(file_list[0])[0]
 
92
        tree, relpath = WorkingTree.open_containing(file_list[0])
85
93
        if tree.supports_views():
86
94
            view_files = tree.views.lookup_view()
87
95
            if view_files:
88
96
                for filename in file_list:
89
97
                    if not osutils.is_inside_any(view_files, filename):
90
98
                        raise errors.FileOutsideView(filename, view_files)
 
99
        file_list = file_list[:]
 
100
        file_list[0] = tree.abspath(relpath)
91
101
    else:
92
102
        tree = WorkingTree.open_containing(u'.')[0]
93
103
        if tree.supports_views():
110
120
 
111
121
 
112
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
    """
113
132
    if branch is None:
114
133
        branch = tree.branch
115
134
    if revisions is None:
439
458
        except errors.NoWorkingTree:
440
459
            raise errors.BzrCommandError("No working tree to remove")
441
460
        except errors.NotLocalUrl:
442
 
            raise errors.BzrCommandError("You cannot remove the working tree of a "
443
 
                                         "remote path")
 
461
            raise errors.BzrCommandError("You cannot remove the working tree"
 
462
                                         " of a remote path")
444
463
        if not force:
445
 
            changes = working.changes_from(working.basis_tree())
446
 
            if changes.has_changed():
 
464
            # XXX: What about pending merges ? -- vila 20090629
 
465
            if working.has_changes(working.basis_tree()):
447
466
                raise errors.UncommittedChanges(working)
448
467
 
449
468
        working_path = working.bzrdir.root_transport.base
450
469
        branch_path = working.branch.bzrdir.root_transport.base
451
470
        if working_path != branch_path:
452
 
            raise errors.BzrCommandError("You cannot remove the working tree from "
453
 
                                         "a lightweight checkout")
 
471
            raise errors.BzrCommandError("You cannot remove the working tree"
 
472
                                         " from a lightweight checkout")
454
473
 
455
474
        d.destroy_workingtree()
456
475
 
463
482
 
464
483
    _see_also = ['info']
465
484
    takes_args = ['location?']
 
485
    takes_options = [
 
486
        Option('tree', help='Show revno of working tree'),
 
487
        ]
466
488
 
467
489
    @display_command
468
 
    def run(self, location=u'.'):
469
 
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
470
 
        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')
471
515
 
472
516
 
473
517
class cmd_revision_info(Command):
483
527
            short_name='d',
484
528
            type=unicode,
485
529
            ),
 
530
        Option('tree', help='Show revno of working tree'),
486
531
        ]
487
532
 
488
533
    @display_command
489
 
    def run(self, revision=None, directory=u'.', revision_info_list=[]):
490
 
 
491
 
        revs = []
492
 
        if revision is not None:
493
 
            revs.extend(revision)
494
 
        if revision_info_list is not None:
495
 
            for rev in revision_info_list:
496
 
                revs.append(RevisionSpec.from_string(rev))
497
 
 
498
 
        b = Branch.open_containing(directory)[0]
499
 
 
500
 
        if len(revs) == 0:
501
 
            revs.append(RevisionSpec.from_string('-1'))
502
 
 
503
 
        for rev in revs:
504
 
            revision_id = rev.as_revision_id(b)
505
 
            try:
506
 
                revno = '%4d' % (b.revision_id_to_revno(revision_id))
507
 
            except errors.NoSuchRevision:
508
 
                dotted_map = b.get_revision_id_to_revno_map()
509
 
                revno = '.'.join(str(i) for i in dotted_map[revision_id])
510
 
            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]))
511
580
 
512
581
 
513
582
class cmd_add(Command):
543
612
    branches that will be merged later (without showing the two different
544
613
    adds as a conflict). It is also useful when merging another project
545
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.
546
618
    """
547
619
    takes_args = ['file*']
548
620
    takes_options = [
556
628
               help='Lookup file ids from this tree.'),
557
629
        ]
558
630
    encoding_type = 'replace'
559
 
    _see_also = ['remove']
 
631
    _see_also = ['remove', 'ignore']
560
632
 
561
633
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
562
634
            file_ids_from=None):
594
666
                    for path in ignored[glob]:
595
667
                        self.outf.write("ignored %s matching \"%s\"\n"
596
668
                                        % (path, glob))
597
 
            else:
598
 
                match_len = 0
599
 
                for glob, paths in ignored.items():
600
 
                    match_len += len(paths)
601
 
                self.outf.write("ignored %d file(s).\n" % match_len)
602
 
            self.outf.write("If you wish to add some of these files,"
603
 
                            " please add them by name.\n")
604
669
 
605
670
 
606
671
class cmd_mkdir(Command):
738
803
        if len(names_list) < 2:
739
804
            raise errors.BzrCommandError("missing file argument")
740
805
        tree, rel_names = tree_files(names_list, canonicalize=False)
741
 
        tree.lock_write()
 
806
        tree.lock_tree_write()
742
807
        try:
743
808
            self._run(tree, names_list, rel_names, after)
744
809
        finally:
752
817
            raise errors.BzrCommandError('--after cannot be specified with'
753
818
                                         ' --auto.')
754
819
        work_tree, file_list = tree_files(names_list, default_branch='.')
755
 
        work_tree.lock_write()
 
820
        work_tree.lock_tree_write()
756
821
        try:
757
822
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
758
823
        finally:
871
936
            short_name='d',
872
937
            type=unicode,
873
938
            ),
 
939
        Option('local',
 
940
            help="Perform a local pull in a bound "
 
941
                 "branch.  Local pulls are not applied to "
 
942
                 "the master branch."
 
943
            ),
874
944
        ]
875
945
    takes_args = ['location?']
876
946
    encoding_type = 'replace'
877
947
 
878
948
    def run(self, location=None, remember=False, overwrite=False,
879
949
            revision=None, verbose=False,
880
 
            directory=None):
 
950
            directory=None, local=False):
881
951
        # FIXME: too much stuff is in the command class
882
952
        revision_id = None
883
953
        mergeable = None
889
959
        except errors.NoWorkingTree:
890
960
            tree_to = None
891
961
            branch_to = Branch.open_containing(directory)[0]
 
962
        
 
963
        if local and not branch_to.get_bound_location():
 
964
            raise errors.LocalRequiresBoundBranch()
892
965
 
893
966
        possible_transports = []
894
967
        if location is not None:
926
999
            if branch_to.get_parent() is None or remember:
927
1000
                branch_to.set_parent(branch_from.base)
928
1001
 
929
 
        if revision is not None:
930
 
            revision_id = revision.as_revision_id(branch_from)
931
 
 
932
 
        branch_to.lock_write()
 
1002
        if branch_from is not branch_to:
 
1003
            branch_from.lock_read()
933
1004
        try:
934
 
            if tree_to is not None:
935
 
                view_info = _get_view_info_for_change_reporter(tree_to)
936
 
                change_reporter = delta._ChangeReporter(
937
 
                    unversioned_filter=tree_to.is_ignored, view_info=view_info)
938
 
                result = tree_to.pull(branch_from, overwrite, revision_id,
939
 
                                      change_reporter,
940
 
                                      possible_transports=possible_transports)
941
 
            else:
942
 
                result = branch_to.pull(branch_from, overwrite, revision_id)
943
 
 
944
 
            result.report(self.outf)
945
 
            if verbose and result.old_revid != result.new_revid:
946
 
                log.show_branch_change(branch_to, self.outf, result.old_revno,
947
 
                                       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()
948
1029
        finally:
949
 
            branch_to.unlock()
 
1030
            if branch_from is not branch_to:
 
1031
                branch_from.unlock()
950
1032
 
951
1033
 
952
1034
class cmd_push(Command):
999
1081
                'for the commit history. Only the work not present in the '
1000
1082
                'referenced branch is included in the branch created.',
1001
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.'),
1002
1087
        ]
1003
1088
    takes_args = ['location?']
1004
1089
    encoding_type = 'replace'
1006
1091
    def run(self, location=None, remember=False, overwrite=False,
1007
1092
        create_prefix=False, verbose=False, revision=None,
1008
1093
        use_existing_dir=False, directory=None, stacked_on=None,
1009
 
        stacked=False):
 
1094
        stacked=False, strict=None):
1010
1095
        from bzrlib.push import _show_push_branch
1011
1096
 
1012
 
        # Get the source branch and revision_id
1013
1097
        if directory is None:
1014
1098
            directory = '.'
1015
 
        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
1016
1106
        revision = _get_one_revision('push', revision)
1017
1107
        if revision is not None:
1018
1108
            revision_id = revision.in_history(br_from).rev_id
1019
1109
        else:
1020
 
            revision_id = br_from.last_revision()
 
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.')
1021
1122
 
1022
1123
        # Get the stacked_on branch, if any
1023
1124
        if stacked_on is not None:
1056
1157
 
1057
1158
 
1058
1159
class cmd_branch(Command):
1059
 
    """Create a new copy of a branch.
 
1160
    """Create a new branch that is a copy of an existing branch.
1060
1161
 
1061
1162
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
1062
1163
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
1075
1176
        help='Hard-link working tree files where possible.'),
1076
1177
        Option('no-tree',
1077
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."),
1078
1182
        Option('stacked',
1079
1183
            help='Create a stacked branch referring to the source branch. '
1080
1184
                'The new branch will depend on the availability of the source '
1081
1185
                'branch for all operations.'),
1082
1186
        Option('standalone',
1083
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.'),
1084
1193
        ]
1085
1194
    aliases = ['get', 'clone']
1086
1195
 
1087
1196
    def run(self, from_location, to_location=None, revision=None,
1088
 
            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
1089
1200
        from bzrlib.tag import _merge_tags_if_possible
1090
 
 
1091
1201
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1092
1202
            from_location)
 
1203
        if (accelerator_tree is not None and
 
1204
            accelerator_tree.supports_content_filtering()):
 
1205
            accelerator_tree = None
1093
1206
        revision = _get_one_revision('branch', revision)
1094
1207
        br_from.lock_read()
1095
1208
        try:
1106
1219
            try:
1107
1220
                to_transport.mkdir('.')
1108
1221
            except errors.FileExists:
1109
 
                raise errors.BzrCommandError('Target directory "%s" already'
1110
 
                                             ' 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)
1111
1232
            except errors.NoSuchFile:
1112
1233
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1113
1234
                                             % to_location)
1136
1257
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1137
1258
                errors.UnstackableRepositoryFormat), e:
1138
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'))
1139
1266
        finally:
1140
1267
            br_from.unlock()
1141
1268
 
1358
1485
            title='Deletion Strategy', value_switches=True, enum_switch=False,
1359
1486
            safe='Only delete files if they can be'
1360
1487
                 ' safely recovered (default).',
1361
 
            keep="Don't delete any files.",
 
1488
            keep='Delete from bzr but leave the working copy.',
1362
1489
            force='Delete all the specified files, even if they can not be '
1363
1490
                'recovered and even if they are non-empty directories.')]
1364
1491
    aliases = ['rm', 'del']
1546
1673
                lazy_registry=('bzrlib.bzrdir', 'format_registry'),
1547
1674
                converter=lambda name: bzrdir.format_registry.make_bzrdir(name),
1548
1675
                value_switches=True,
1549
 
                title="Branch Format",
 
1676
                title="Branch format",
1550
1677
                ),
1551
1678
         Option('append-revisions-only',
1552
1679
                help='Never change revnos or the existing log.'
1575
1702
                    "\nYou may supply --create-prefix to create all"
1576
1703
                    " leading parent directories."
1577
1704
                    % location)
1578
 
            _create_prefix(to_transport)
 
1705
            to_transport.create_prefix()
1579
1706
 
1580
1707
        try:
1581
1708
            a_bzrdir = bzrdir.BzrDir.open_from_transport(to_transport)
1599
1726
                branch.set_append_revisions_only(True)
1600
1727
            except errors.UpgradeRequired:
1601
1728
                raise errors.BzrCommandError('This branch format cannot be set'
1602
 
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1729
                    ' to append-revisions-only.  Try --default.')
1603
1730
        if not is_quiet():
1604
1731
            from bzrlib.info import describe_layout, describe_format
1605
1732
            try:
2068
2195
 
2069
2196
      When exploring non-mainline history on large projects with deep
2070
2197
      history, the performance of log can be greatly improved by installing
2071
 
      the revnocache plugin. This plugin buffers historical information
 
2198
      the historycache plugin. This plugin buffers historical information
2072
2199
      trading disk space for faster speed.
2073
2200
    """
2074
2201
    takes_args = ['file*']
2147
2274
 
2148
2275
        file_ids = []
2149
2276
        filter_by_dir = False
2150
 
        if file_list:
2151
 
            # find the file ids to log and check for directory filtering
2152
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
2153
 
                file_list)
2154
 
            for relpath, file_id, kind in file_info_list:
2155
 
                if file_id is None:
2156
 
                    raise errors.BzrCommandError(
2157
 
                        "Path unknown at end or start of revision range: %s" %
2158
 
                        relpath)
2159
 
                # If the relpath is the top of the tree, we log everything
2160
 
                if relpath == '':
2161
 
                    file_ids = []
2162
 
                    break
 
2277
        b = None
 
2278
        try:
 
2279
            if file_list:
 
2280
                # find the file ids to log and check for directory filtering
 
2281
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2282
                    revision, file_list)
 
2283
                for relpath, file_id, kind in file_info_list:
 
2284
                    if file_id is None:
 
2285
                        raise errors.BzrCommandError(
 
2286
                            "Path unknown at end or start of revision range: %s" %
 
2287
                            relpath)
 
2288
                    # If the relpath is the top of the tree, we log everything
 
2289
                    if relpath == '':
 
2290
                        file_ids = []
 
2291
                        break
 
2292
                    else:
 
2293
                        file_ids.append(file_id)
 
2294
                    filter_by_dir = filter_by_dir or (
 
2295
                        kind in ['directory', 'tree-reference'])
 
2296
            else:
 
2297
                # log everything
 
2298
                # FIXME ? log the current subdir only RBC 20060203
 
2299
                if revision is not None \
 
2300
                        and len(revision) > 0 and revision[0].get_branch():
 
2301
                    location = revision[0].get_branch()
2163
2302
                else:
2164
 
                    file_ids.append(file_id)
2165
 
                filter_by_dir = filter_by_dir or (
2166
 
                    kind in ['directory', 'tree-reference'])
2167
 
        else:
2168
 
            # log everything
2169
 
            # FIXME ? log the current subdir only RBC 20060203
2170
 
            if revision is not None \
2171
 
                    and len(revision) > 0 and revision[0].get_branch():
2172
 
                location = revision[0].get_branch()
2173
 
            else:
2174
 
                location = '.'
2175
 
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2176
 
            b = dir.open_branch()
2177
 
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2178
 
 
2179
 
        # Decide on the type of delta & diff filtering to use
2180
 
        # TODO: add an --all-files option to make this configurable & consistent
2181
 
        if not verbose:
2182
 
            delta_type = None
2183
 
        else:
2184
 
            delta_type = 'full'
2185
 
        if not show_diff:
2186
 
            diff_type = None
2187
 
        elif file_ids:
2188
 
            diff_type = 'partial'
2189
 
        else:
2190
 
            diff_type = 'full'
2191
 
 
2192
 
        b.lock_read()
2193
 
        try:
 
2303
                    location = '.'
 
2304
                dir, relpath = bzrdir.BzrDir.open_containing(location)
 
2305
                b = dir.open_branch()
 
2306
                b.lock_read()
 
2307
                rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2308
 
 
2309
            # Decide on the type of delta & diff filtering to use
 
2310
            # TODO: add an --all-files option to make this configurable & consistent
 
2311
            if not verbose:
 
2312
                delta_type = None
 
2313
            else:
 
2314
                delta_type = 'full'
 
2315
            if not show_diff:
 
2316
                diff_type = None
 
2317
            elif file_ids:
 
2318
                diff_type = 'partial'
 
2319
            else:
 
2320
                diff_type = 'full'
 
2321
 
2194
2322
            # Build the log formatter
2195
2323
            if log_format is None:
2196
2324
                log_format = log.log_formatter_registry.get_default(b)
2226
2354
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2227
2355
            Logger(b, rqst).show(lf)
2228
2356
        finally:
2229
 
            b.unlock()
 
2357
            if b is not None:
 
2358
                b.unlock()
2230
2359
 
2231
2360
 
2232
2361
def _get_revision_range(revisionspec_list, branch, command_name):
2296
2425
    @display_command
2297
2426
    def run(self, filename):
2298
2427
        tree, relpath = WorkingTree.open_containing(filename)
 
2428
        file_id = tree.path2id(relpath)
2299
2429
        b = tree.branch
2300
 
        file_id = tree.path2id(relpath)
2301
 
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
2302
 
            self.outf.write("%6d %s\n" % (revno, what))
 
2430
        b.lock_read()
 
2431
        try:
 
2432
            touching_revs = log.find_touching_revisions(b, file_id)
 
2433
            for revno, revision_id, what in touching_revs:
 
2434
                self.outf.write("%6d %s\n" % (revno, what))
 
2435
        finally:
 
2436
            b.unlock()
2303
2437
 
2304
2438
 
2305
2439
class cmd_ls(Command):
2344
2478
 
2345
2479
        if path is None:
2346
2480
            fs_path = '.'
2347
 
            prefix = ''
2348
2481
        else:
2349
2482
            if from_root:
2350
2483
                raise errors.BzrCommandError('cannot specify both --from-root'
2351
2484
                                             ' and PATH')
2352
2485
            fs_path = path
2353
 
            prefix = path
2354
2486
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2355
2487
            fs_path)
 
2488
 
 
2489
        # Calculate the prefix to use
 
2490
        prefix = None
2356
2491
        if from_root:
2357
 
            relpath = u''
2358
 
        elif relpath:
2359
 
            relpath += '/'
 
2492
            if relpath:
 
2493
                prefix = relpath + '/'
 
2494
        elif fs_path != '.':
 
2495
            prefix = fs_path + '/'
 
2496
 
2360
2497
        if revision is not None or tree is None:
2361
2498
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2362
2499
 
2370
2507
 
2371
2508
        tree.lock_read()
2372
2509
        try:
2373
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2374
 
                if fp.startswith(relpath):
2375
 
                    rp = fp[len(relpath):]
2376
 
                    fp = osutils.pathjoin(prefix, rp)
2377
 
                    if not recursive and '/' in rp:
2378
 
                        continue
2379
 
                    if not all and not selection[fc]:
2380
 
                        continue
2381
 
                    if kind is not None and fkind != kind:
2382
 
                        continue
2383
 
                    if apply_view:
2384
 
                        try:
2385
 
                            views.check_path_in_view(tree, fp)
2386
 
                        except errors.FileOutsideView:
2387
 
                            continue
2388
 
                    kindch = entry.kind_character()
2389
 
                    outstring = fp + kindch
2390
 
                    if verbose:
2391
 
                        outstring = '%-8s %s' % (fc, outstring)
2392
 
                        if show_ids and fid is not None:
2393
 
                            outstring = "%-50s %s" % (outstring, fid)
2394
 
                        self.outf.write(outstring + '\n')
2395
 
                    elif null:
2396
 
                        self.outf.write(fp + '\0')
2397
 
                        if show_ids:
2398
 
                            if fid is not None:
2399
 
                                self.outf.write(fid)
2400
 
                            self.outf.write('\0')
2401
 
                        self.outf.flush()
2402
 
                    else:
 
2510
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2511
                from_dir=relpath, recursive=recursive):
 
2512
                # Apply additional masking
 
2513
                if not all and not selection[fc]:
 
2514
                    continue
 
2515
                if kind is not None and fkind != kind:
 
2516
                    continue
 
2517
                if apply_view:
 
2518
                    try:
 
2519
                        if relpath:
 
2520
                            fullpath = osutils.pathjoin(relpath, fp)
 
2521
                        else:
 
2522
                            fullpath = fp
 
2523
                        views.check_path_in_view(tree, fullpath)
 
2524
                    except errors.FileOutsideView:
 
2525
                        continue
 
2526
 
 
2527
                # Output the entry
 
2528
                if prefix:
 
2529
                    fp = osutils.pathjoin(prefix, fp)
 
2530
                kindch = entry.kind_character()
 
2531
                outstring = fp + kindch
 
2532
                ui.ui_factory.clear_term()
 
2533
                if verbose:
 
2534
                    outstring = '%-8s %s' % (fc, outstring)
 
2535
                    if show_ids and fid is not None:
 
2536
                        outstring = "%-50s %s" % (outstring, fid)
 
2537
                    self.outf.write(outstring + '\n')
 
2538
                elif null:
 
2539
                    self.outf.write(fp + '\0')
 
2540
                    if show_ids:
 
2541
                        if fid is not None:
 
2542
                            self.outf.write(fid)
 
2543
                        self.outf.write('\0')
 
2544
                    self.outf.flush()
 
2545
                else:
 
2546
                    if show_ids:
2403
2547
                        if fid is not None:
2404
2548
                            my_id = fid
2405
2549
                        else:
2406
2550
                            my_id = ''
2407
 
                        if show_ids:
2408
 
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
2409
 
                        else:
2410
 
                            self.outf.write(outstring + '\n')
 
2551
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2552
                    else:
 
2553
                        self.outf.write(outstring + '\n')
2411
2554
        finally:
2412
2555
            tree.unlock()
2413
2556
 
2706
2849
class cmd_commit(Command):
2707
2850
    """Commit changes into a new revision.
2708
2851
 
2709
 
    If no arguments are given, the entire tree is committed.
2710
 
 
2711
 
    If selected files are specified, only changes to those files are
2712
 
    committed.  If a directory is specified then the directory and everything
2713
 
    within it is committed.
2714
 
 
2715
 
    When excludes are given, they take precedence over selected files.
2716
 
    For example, too commit only changes within foo, but not changes within
2717
 
    foo/bar::
2718
 
 
2719
 
      bzr commit foo -x foo/bar
2720
 
 
2721
 
    If author of the change is not the same person as the committer, you can
2722
 
    specify the author's name using the --author option. The name should be
2723
 
    in the same format as a committer-id, e.g. "John Doe <jdoe@example.com>".
2724
 
    If there is more than one author of the change you can specify the option
2725
 
    multiple times, once for each author.
2726
 
 
2727
 
    A selected-file commit may fail in some cases where the committed
2728
 
    tree would be invalid. Consider::
2729
 
 
2730
 
      bzr init foo
2731
 
      mkdir foo/bar
2732
 
      bzr add foo/bar
2733
 
      bzr commit foo -m "committing foo"
2734
 
      bzr mv foo/bar foo/baz
2735
 
      mkdir foo/bar
2736
 
      bzr add foo/bar
2737
 
      bzr commit foo/bar -m "committing bar but not baz"
2738
 
 
2739
 
    In the example above, the last commit will fail by design. This gives
2740
 
    the user the opportunity to decide whether they want to commit the
2741
 
    rename at the same time, separately first, or not at all. (As a general
2742
 
    rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
2743
 
 
2744
 
    Note: A selected-file commit after a merge is not yet supported.
 
2852
    An explanatory message needs to be given for each commit. This is
 
2853
    often done by using the --message option (getting the message from the
 
2854
    command line) or by using the --file option (getting the message from
 
2855
    a file). If neither of these options is given, an editor is opened for
 
2856
    the user to enter the message. To see the changed files in the
 
2857
    boilerplate text loaded into the editor, use the --show-diff option.
 
2858
 
 
2859
    By default, the entire tree is committed and the person doing the
 
2860
    commit is assumed to be the author. These defaults can be overridden
 
2861
    as explained below.
 
2862
 
 
2863
    :Selective commits:
 
2864
 
 
2865
      If selected files are specified, only changes to those files are
 
2866
      committed.  If a directory is specified then the directory and
 
2867
      everything within it is committed.
 
2868
  
 
2869
      When excludes are given, they take precedence over selected files.
 
2870
      For example, to commit only changes within foo, but not changes
 
2871
      within foo/bar::
 
2872
  
 
2873
        bzr commit foo -x foo/bar
 
2874
  
 
2875
      A selective commit after a merge is not yet supported.
 
2876
 
 
2877
    :Custom authors:
 
2878
 
 
2879
      If the author of the change is not the same person as the committer,
 
2880
      you can specify the author's name using the --author option. The
 
2881
      name should be in the same format as a committer-id, e.g.
 
2882
      "John Doe <jdoe@example.com>". If there is more than one author of
 
2883
      the change you can specify the option multiple times, once for each
 
2884
      author.
 
2885
  
 
2886
    :Checks:
 
2887
 
 
2888
      A common mistake is to forget to add a new file or directory before
 
2889
      running the commit command. The --strict option checks for unknown
 
2890
      files and aborts the commit if any are found. More advanced pre-commit
 
2891
      checks can be implemented by defining hooks. See ``bzr help hooks``
 
2892
      for details.
 
2893
 
 
2894
    :Things to note:
 
2895
 
 
2896
      If you accidentially commit the wrong changes or make a spelling
 
2897
      mistake in the commit message say, you can use the uncommit command
 
2898
      to undo it. See ``bzr help uncommit`` for details.
 
2899
 
 
2900
      Hooks can also be configured to run after a commit. This allows you
 
2901
      to trigger updates to external systems like bug trackers. The --fixes
 
2902
      option can be used to record the association between a revision and
 
2903
      one or more bugs. See ``bzr help bugs`` for details.
 
2904
 
 
2905
      A selective commit may fail in some cases where the committed
 
2906
      tree would be invalid. Consider::
 
2907
  
 
2908
        bzr init foo
 
2909
        mkdir foo/bar
 
2910
        bzr add foo/bar
 
2911
        bzr commit foo -m "committing foo"
 
2912
        bzr mv foo/bar foo/baz
 
2913
        mkdir foo/bar
 
2914
        bzr add foo/bar
 
2915
        bzr commit foo/bar -m "committing bar but not baz"
 
2916
  
 
2917
      In the example above, the last commit will fail by design. This gives
 
2918
      the user the opportunity to decide whether they want to commit the
 
2919
      rename at the same time, separately first, or not at all. (As a general
 
2920
      rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
2745
2921
    """
2746
2922
    # TODO: Run hooks on tree to-be-committed, and after commit.
2747
2923
 
2752
2928
 
2753
2929
    # XXX: verbose currently does nothing
2754
2930
 
2755
 
    _see_also = ['bugs', 'uncommit']
 
2931
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
2756
2932
    takes_args = ['selected*']
2757
2933
    takes_options = [
2758
2934
            ListOption('exclude', type=str, short_name='x',
2869
3045
                raise errors.BzrCommandError("empty commit message specified")
2870
3046
            return my_message
2871
3047
 
 
3048
        # The API permits a commit with a filter of [] to mean 'select nothing'
 
3049
        # but the command line should not do that.
 
3050
        if not selected_list:
 
3051
            selected_list = None
2872
3052
        try:
2873
3053
            tree.commit(message_callback=get_message,
2874
3054
                        specific_files=selected_list,
2879
3059
        except PointlessCommit:
2880
3060
            # FIXME: This should really happen before the file is read in;
2881
3061
            # perhaps prepare the commit; get the message; then actually commit
2882
 
            raise errors.BzrCommandError("no changes to commit."
2883
 
                              " use --unchanged to commit anyhow")
 
3062
            raise errors.BzrCommandError("No changes to commit."
 
3063
                              " Use --unchanged to commit anyhow.")
2884
3064
        except ConflictsInTree:
2885
3065
            raise errors.BzrCommandError('Conflicts detected in working '
2886
3066
                'tree.  Use "bzr conflicts" to list, "bzr resolve FILE" to'
2904
3084
    The working tree and branch checks will only give output if a problem is
2905
3085
    detected. The output fields of the repository check are:
2906
3086
 
2907
 
        revisions: This is just the number of revisions checked.  It doesn't
2908
 
            indicate a problem.
2909
 
        versionedfiles: This is just the number of versionedfiles checked.  It
2910
 
            doesn't indicate a problem.
2911
 
        unreferenced ancestors: Texts that are ancestors of other texts, but
2912
 
            are not properly referenced by the revision ancestry.  This is a
2913
 
            subtle problem that Bazaar can work around.
2914
 
        unique file texts: This is the total number of unique file contents
2915
 
            seen in the checked revisions.  It does not indicate a problem.
2916
 
        repeated file texts: This is the total number of repeated texts seen
2917
 
            in the checked revisions.  Texts can be repeated when their file
2918
 
            entries are modified, but the file contents are not.  It does not
2919
 
            indicate a problem.
 
3087
    revisions
 
3088
        This is just the number of revisions checked.  It doesn't
 
3089
        indicate a problem.
 
3090
 
 
3091
    versionedfiles
 
3092
        This is just the number of versionedfiles checked.  It
 
3093
        doesn't indicate a problem.
 
3094
 
 
3095
    unreferenced ancestors
 
3096
        Texts that are ancestors of other texts, but
 
3097
        are not properly referenced by the revision ancestry.  This is a
 
3098
        subtle problem that Bazaar can work around.
 
3099
 
 
3100
    unique file texts
 
3101
        This is the total number of unique file contents
 
3102
        seen in the checked revisions.  It does not indicate a problem.
 
3103
 
 
3104
    repeated file texts
 
3105
        This is the total number of repeated texts seen
 
3106
        in the checked revisions.  Texts can be repeated when their file
 
3107
        entries are modified, but the file contents are not.  It does not
 
3108
        indicate a problem.
2920
3109
 
2921
3110
    If no restrictions are specified, all Bazaar data that is found at the given
2922
3111
    location will be checked.
3399
3588
    merge refuses to run if there are any uncommitted changes, unless
3400
3589
    --force is given.
3401
3590
 
 
3591
    To select only some changes to merge, use "merge -i", which will prompt
 
3592
    you to apply each diff hunk and file change, similar to "shelve".
 
3593
 
3402
3594
    :Examples:
3403
3595
        To merge the latest revision from bzr.dev::
3404
3596
 
3442
3634
               short_name='d',
3443
3635
               type=unicode,
3444
3636
               ),
3445
 
        Option('preview', help='Instead of merging, show a diff of the merge.')
 
3637
        Option('preview', help='Instead of merging, show a diff of the'
 
3638
               ' merge.'),
 
3639
        Option('interactive', help='Select changes interactively.',
 
3640
            short_name='i')
3446
3641
    ]
3447
3642
 
3448
3643
    def run(self, location=None, revision=None, force=False,
3450
3645
            uncommitted=False, pull=False,
3451
3646
            directory=None,
3452
3647
            preview=False,
 
3648
            interactive=False,
3453
3649
            ):
3454
3650
        if merge_type is None:
3455
3651
            merge_type = _mod_merge.Merge3Merger
3467
3663
        except errors.NoSuchRevision:
3468
3664
            basis_tree = tree.basis_tree()
3469
3665
        if not force:
3470
 
            changes = tree.changes_from(basis_tree)
3471
 
            if changes.has_changed():
 
3666
            if tree.has_changes(basis_tree):
3472
3667
                raise errors.UncommittedChanges(tree)
3473
3668
 
3474
3669
        view_info = _get_view_info_for_change_reporter(tree)
3501
3696
                if revision is not None and len(revision) > 0:
3502
3697
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3503
3698
                        ' --revision at the same time.')
3504
 
                location = self._select_branch_location(tree, location)[0]
3505
 
                other_tree, other_path = WorkingTree.open_containing(location)
3506
 
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
3507
 
                    pb)
 
3699
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3700
                                                          cleanups)
3508
3701
                allow_pending = False
3509
 
                if other_path != '':
3510
 
                    merger.interesting_files = [other_path]
3511
3702
 
3512
3703
            if merger is None:
3513
3704
                merger, allow_pending = self._get_merger_from_branch(tree,
3531
3722
                    return 0
3532
3723
            merger.check_basis(False)
3533
3724
            if preview:
3534
 
                return self._do_preview(merger)
 
3725
                return self._do_preview(merger, cleanups)
 
3726
            elif interactive:
 
3727
                return self._do_interactive(merger, cleanups)
3535
3728
            else:
3536
3729
                return self._do_merge(merger, change_reporter, allow_pending,
3537
3730
                                      verified)
3539
3732
            for cleanup in reversed(cleanups):
3540
3733
                cleanup()
3541
3734
 
3542
 
    def _do_preview(self, merger):
3543
 
        from bzrlib.diff import show_diff_trees
 
3735
    def _get_preview(self, merger, cleanups):
3544
3736
        tree_merger = merger.make_merger()
3545
3737
        tt = tree_merger.make_preview_transform()
3546
 
        try:
3547
 
            result_tree = tt.get_preview_tree()
3548
 
            show_diff_trees(merger.this_tree, result_tree, self.outf,
3549
 
                            old_label='', new_label='')
3550
 
        finally:
3551
 
            tt.finalize()
 
3738
        cleanups.append(tt.finalize)
 
3739
        result_tree = tt.get_preview_tree()
 
3740
        return result_tree
 
3741
 
 
3742
    def _do_preview(self, merger, cleanups):
 
3743
        from bzrlib.diff import show_diff_trees
 
3744
        result_tree = self._get_preview(merger, cleanups)
 
3745
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3746
                        old_label='', new_label='')
3552
3747
 
3553
3748
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3554
3749
        merger.change_reporter = change_reporter
3562
3757
        else:
3563
3758
            return 0
3564
3759
 
 
3760
    def _do_interactive(self, merger, cleanups):
 
3761
        """Perform an interactive merge.
 
3762
 
 
3763
        This works by generating a preview tree of the merge, then using
 
3764
        Shelver to selectively remove the differences between the working tree
 
3765
        and the preview tree.
 
3766
        """
 
3767
        from bzrlib import shelf_ui
 
3768
        result_tree = self._get_preview(merger, cleanups)
 
3769
        writer = bzrlib.option.diff_writer_registry.get()
 
3770
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
3771
                                   reporter=shelf_ui.ApplyReporter(),
 
3772
                                   diff_writer=writer(sys.stdout))
 
3773
        shelver.run()
 
3774
 
3565
3775
    def sanity_check_merger(self, merger):
3566
3776
        if (merger.show_base and
3567
3777
            not merger.merge_type is _mod_merge.Merge3Merger):
3602
3812
            base_branch, base_path = Branch.open_containing(base_loc,
3603
3813
                possible_transports)
3604
3814
        # Find the revision ids
3605
 
        if revision is None or len(revision) < 1 or revision[-1] is None:
 
3815
        other_revision_id = None
 
3816
        base_revision_id = None
 
3817
        if revision is not None:
 
3818
            if len(revision) >= 1:
 
3819
                other_revision_id = revision[-1].as_revision_id(other_branch)
 
3820
            if len(revision) == 2:
 
3821
                base_revision_id = revision[0].as_revision_id(base_branch)
 
3822
        if other_revision_id is None:
3606
3823
            other_revision_id = _mod_revision.ensure_null(
3607
3824
                other_branch.last_revision())
3608
 
        else:
3609
 
            other_revision_id = revision[-1].as_revision_id(other_branch)
3610
 
        if (revision is not None and len(revision) == 2
3611
 
            and revision[0] is not None):
3612
 
            base_revision_id = revision[0].as_revision_id(base_branch)
3613
 
        else:
3614
 
            base_revision_id = None
3615
3825
        # Remember where we merge from
3616
3826
        if ((remember or tree.branch.get_submit_branch() is None) and
3617
3827
             user_location is not None):
3626
3836
            allow_pending = True
3627
3837
        return merger, allow_pending
3628
3838
 
 
3839
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3840
        """Get a merger for uncommitted changes.
 
3841
 
 
3842
        :param tree: The tree the merger should apply to.
 
3843
        :param location: The location containing uncommitted changes.
 
3844
        :param pb: The progress bar to use for showing progress.
 
3845
        :param cleanups: A list of operations to perform to clean up the
 
3846
            temporary directories, unfinalized objects, etc.
 
3847
        """
 
3848
        location = self._select_branch_location(tree, location)[0]
 
3849
        other_tree, other_path = WorkingTree.open_containing(location)
 
3850
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
3851
        if other_path != '':
 
3852
            merger.interesting_files = [other_path]
 
3853
        return merger
 
3854
 
3629
3855
    def _select_branch_location(self, tree, user_location, revision=None,
3630
3856
                                index=None):
3631
3857
        """Select a branch location, according to possible inputs.
4183
4409
            branch.lock_read()
4184
4410
        try:
4185
4411
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4186
 
            if wt is not None:
4187
 
                file_id = wt.path2id(relpath)
4188
 
            else:
4189
 
                file_id = tree.path2id(relpath)
4190
 
            if file_id is None:
4191
 
                raise errors.NotVersionedError(filename)
4192
 
            file_version = tree.inventory[file_id].revision
4193
 
            if wt is not None and revision is None:
4194
 
                # If there is a tree and we're not annotating historical
4195
 
                # versions, annotate the working tree's content.
4196
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4197
 
                    show_ids=show_ids)
4198
 
            else:
4199
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4200
 
                              show_ids=show_ids)
 
4412
            tree.lock_read()
 
4413
            try:
 
4414
                if wt is not None:
 
4415
                    file_id = wt.path2id(relpath)
 
4416
                else:
 
4417
                    file_id = tree.path2id(relpath)
 
4418
                if file_id is None:
 
4419
                    raise errors.NotVersionedError(filename)
 
4420
                file_version = tree.inventory[file_id].revision
 
4421
                if wt is not None and revision is None:
 
4422
                    # If there is a tree and we're not annotating historical
 
4423
                    # versions, annotate the working tree's content.
 
4424
                    annotate_file_tree(wt, file_id, self.outf, long, all,
 
4425
                        show_ids=show_ids)
 
4426
                else:
 
4427
                    annotate_file(branch, file_version, file_id, long, all,
 
4428
                        self.outf, show_ids=show_ids)
 
4429
            finally:
 
4430
                tree.unlock()
4201
4431
        finally:
4202
4432
            if wt is not None:
4203
4433
                wt.unlock()
4486
4716
    takes_options = [
4487
4717
        Option('inet',
4488
4718
               help='Serve on stdin/out for use from inetd or sshd.'),
 
4719
        RegistryOption('protocol', 
 
4720
               help="Protocol to serve.", 
 
4721
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
 
4722
               value_switches=True),
4489
4723
        Option('port',
4490
4724
               help='Listen for connections on nominated port of the form '
4491
4725
                    '[hostname:]portnumber.  Passing 0 as the port number will '
4492
 
                    'result in a dynamically allocated port.  The default port is '
4493
 
                    '4155.',
 
4726
                    'result in a dynamically allocated port.  The default port '
 
4727
                    'depends on the protocol.',
4494
4728
               type=str),
4495
4729
        Option('directory',
4496
4730
               help='Serve contents of this directory.',
4502
4736
                ),
4503
4737
        ]
4504
4738
 
4505
 
    def run_smart_server(self, smart_server):
4506
 
        """Run 'smart_server' forever, with no UI output at all."""
4507
 
        # For the duration of this server, no UI output is permitted. note
4508
 
        # that this may cause problems with blackbox tests. This should be
4509
 
        # changed with care though, as we dont want to use bandwidth sending
4510
 
        # progress over stderr to smart server clients!
4511
 
        from bzrlib import lockdir
4512
 
        old_factory = ui.ui_factory
4513
 
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
4514
 
        try:
4515
 
            ui.ui_factory = ui.SilentUIFactory()
4516
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 0
4517
 
            smart_server.serve()
4518
 
        finally:
4519
 
            ui.ui_factory = old_factory
4520
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = old_lockdir_timeout
4521
 
 
4522
4739
    def get_host_and_port(self, port):
4523
4740
        """Return the host and port to run the smart server on.
4524
4741
 
4525
 
        If 'port' is None, the default host (`medium.BZR_DEFAULT_INTERFACE`)
4526
 
        and port (`medium.BZR_DEFAULT_PORT`) will be used.
 
4742
        If 'port' is None, None will be returned for the host and port.
4527
4743
 
4528
4744
        If 'port' has a colon in it, the string before the colon will be
4529
4745
        interpreted as the host.
4532
4748
        :return: A tuple of (host, port), where 'host' is a host name or IP,
4533
4749
            and port is an integer TCP/IP port.
4534
4750
        """
4535
 
        from bzrlib.smart import medium
4536
 
        host = medium.BZR_DEFAULT_INTERFACE
4537
 
        if port is None:
4538
 
            port = medium.BZR_DEFAULT_PORT
4539
 
        else:
 
4751
        host = None
 
4752
        if port is not None:
4540
4753
            if ':' in port:
4541
4754
                host, port = port.split(':')
4542
4755
            port = int(port)
4543
4756
        return host, port
4544
4757
 
4545
 
    def get_smart_server(self, transport, inet, port):
4546
 
        """Construct a smart server.
4547
 
 
4548
 
        :param transport: The base transport from which branches will be
4549
 
            served.
4550
 
        :param inet: If True, serve over stdin and stdout. Used for running
4551
 
            from inet.
4552
 
        :param port: The port to listen on. By default, it's `
4553
 
            medium.BZR_DEFAULT_PORT`. See `get_host_and_port` for more
4554
 
            information.
4555
 
        :return: A smart server.
4556
 
        """
4557
 
        from bzrlib.smart import medium, server
4558
 
        if inet:
4559
 
            smart_server = medium.SmartServerPipeStreamMedium(
4560
 
                sys.stdin, sys.stdout, transport)
4561
 
        else:
4562
 
            host, port = self.get_host_and_port(port)
4563
 
            smart_server = server.SmartTCPServer(
4564
 
                transport, host=host, port=port)
4565
 
            note('listening on port: %s' % smart_server.port)
4566
 
        return smart_server
4567
 
 
4568
 
    def run(self, port=None, inet=False, directory=None, allow_writes=False):
4569
 
        from bzrlib.transport import get_transport
4570
 
        from bzrlib.transport.chroot import ChrootServer
 
4758
    def run(self, port=None, inet=False, directory=None, allow_writes=False,
 
4759
            protocol=None):
 
4760
        from bzrlib.transport import get_transport, transport_server_registry
4571
4761
        if directory is None:
4572
4762
            directory = os.getcwd()
 
4763
        if protocol is None:
 
4764
            protocol = transport_server_registry.get()
 
4765
        host, port = self.get_host_and_port(port)
4573
4766
        url = urlutils.local_path_to_url(directory)
4574
4767
        if not allow_writes:
4575
4768
            url = 'readonly+' + url
4576
 
        chroot_server = ChrootServer(get_transport(url))
4577
 
        chroot_server.setUp()
4578
 
        t = get_transport(chroot_server.get_url())
4579
 
        smart_server = self.get_smart_server(t, inet, port)
4580
 
        self.run_smart_server(smart_server)
 
4769
        transport = get_transport(url)
 
4770
        protocol(transport, host, port, inet)
4581
4771
 
4582
4772
 
4583
4773
class cmd_join(Command):
4649
4839
        try:
4650
4840
            containing_tree.extract(sub_id)
4651
4841
        except errors.RootNotRich:
4652
 
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
4842
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
4653
4843
 
4654
4844
 
4655
4845
class cmd_merge_directive(Command):
4824
5014
               help='Write merge directive to this file; '
4825
5015
                    'use - for stdout.',
4826
5016
               type=unicode),
 
5017
        Option('strict',
 
5018
               help='Refuse to send if there are uncommitted changes in'
 
5019
               ' the working tree, --no-strict disables the check.'),
4827
5020
        Option('mail-to', help='Mail the request to this address.',
4828
5021
               type=unicode),
4829
5022
        'revision',
4830
5023
        'message',
4831
5024
        Option('body', help='Body for the email.', type=unicode),
4832
 
        RegistryOption.from_kwargs('format',
4833
 
        'Use the specified output format.',
4834
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
4835
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
5025
        RegistryOption('format',
 
5026
                       help='Use the specified output format.',
 
5027
                       lazy_registry=('bzrlib.send', 'format_registry')),
4836
5028
        ]
4837
5029
 
4838
5030
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4839
5031
            no_patch=False, revision=None, remember=False, output=None,
4840
 
            format='4', mail_to=None, message=None, body=None, **kwargs):
4841
 
        return self._run(submit_branch, revision, public_branch, remember,
4842
 
                         format, no_bundle, no_patch, output,
4843
 
                         kwargs.get('from', '.'), mail_to, message, body)
4844
 
 
4845
 
    def _run(self, submit_branch, revision, public_branch, remember, format,
4846
 
             no_bundle, no_patch, output, from_, mail_to, message, body):
4847
 
        from bzrlib.revision import NULL_REVISION
4848
 
        branch = Branch.open_containing(from_)[0]
4849
 
        if output is None:
4850
 
            outfile = cStringIO.StringIO()
4851
 
        elif output == '-':
4852
 
            outfile = self.outf
4853
 
        else:
4854
 
            outfile = open(output, 'wb')
4855
 
        # we may need to write data into branch's repository to calculate
4856
 
        # the data to send.
4857
 
        branch.lock_write()
4858
 
        try:
4859
 
            if output is None:
4860
 
                config = branch.get_config()
4861
 
                if mail_to is None:
4862
 
                    mail_to = config.get_user_option('submit_to')
4863
 
                mail_client = config.get_mail_client()
4864
 
                if (not getattr(mail_client, 'supports_body', False)
4865
 
                    and body is not None):
4866
 
                    raise errors.BzrCommandError(
4867
 
                        'Mail client "%s" does not support specifying body' %
4868
 
                        mail_client.__class__.__name__)
4869
 
            if remember and submit_branch is None:
4870
 
                raise errors.BzrCommandError(
4871
 
                    '--remember requires a branch to be specified.')
4872
 
            stored_submit_branch = branch.get_submit_branch()
4873
 
            remembered_submit_branch = None
4874
 
            if submit_branch is None:
4875
 
                submit_branch = stored_submit_branch
4876
 
                remembered_submit_branch = "submit"
4877
 
            else:
4878
 
                if stored_submit_branch is None or remember:
4879
 
                    branch.set_submit_branch(submit_branch)
4880
 
            if submit_branch is None:
4881
 
                submit_branch = branch.get_parent()
4882
 
                remembered_submit_branch = "parent"
4883
 
            if submit_branch is None:
4884
 
                raise errors.BzrCommandError('No submit branch known or'
4885
 
                                             ' specified')
4886
 
            if remembered_submit_branch is not None:
4887
 
                note('Using saved %s location "%s" to determine what '
4888
 
                        'changes to submit.', remembered_submit_branch,
4889
 
                        submit_branch)
4890
 
 
4891
 
            if mail_to is None:
4892
 
                submit_config = Branch.open(submit_branch).get_config()
4893
 
                mail_to = submit_config.get_user_option("child_submit_to")
4894
 
 
4895
 
            stored_public_branch = branch.get_public_branch()
4896
 
            if public_branch is None:
4897
 
                public_branch = stored_public_branch
4898
 
            elif stored_public_branch is None or remember:
4899
 
                branch.set_public_branch(public_branch)
4900
 
            if no_bundle and public_branch is None:
4901
 
                raise errors.BzrCommandError('No public branch specified or'
4902
 
                                             ' known')
4903
 
            base_revision_id = None
4904
 
            revision_id = None
4905
 
            if revision is not None:
4906
 
                if len(revision) > 2:
4907
 
                    raise errors.BzrCommandError('bzr send takes '
4908
 
                        'at most two one revision identifiers')
4909
 
                revision_id = revision[-1].as_revision_id(branch)
4910
 
                if len(revision) == 2:
4911
 
                    base_revision_id = revision[0].as_revision_id(branch)
4912
 
            if revision_id is None:
4913
 
                revision_id = branch.last_revision()
4914
 
            if revision_id == NULL_REVISION:
4915
 
                raise errors.BzrCommandError('No revisions to submit.')
4916
 
            if format == '4':
4917
 
                directive = merge_directive.MergeDirective2.from_objects(
4918
 
                    branch.repository, revision_id, time.time(),
4919
 
                    osutils.local_time_offset(), submit_branch,
4920
 
                    public_branch=public_branch, include_patch=not no_patch,
4921
 
                    include_bundle=not no_bundle, message=message,
4922
 
                    base_revision_id=base_revision_id)
4923
 
            elif format == '0.9':
4924
 
                if not no_bundle:
4925
 
                    if not no_patch:
4926
 
                        patch_type = 'bundle'
4927
 
                    else:
4928
 
                        raise errors.BzrCommandError('Format 0.9 does not'
4929
 
                            ' permit bundle with no patch')
4930
 
                else:
4931
 
                    if not no_patch:
4932
 
                        patch_type = 'diff'
4933
 
                    else:
4934
 
                        patch_type = None
4935
 
                directive = merge_directive.MergeDirective.from_objects(
4936
 
                    branch.repository, revision_id, time.time(),
4937
 
                    osutils.local_time_offset(), submit_branch,
4938
 
                    public_branch=public_branch, patch_type=patch_type,
4939
 
                    message=message)
4940
 
 
4941
 
            outfile.writelines(directive.to_lines())
4942
 
            if output is None:
4943
 
                subject = '[MERGE] '
4944
 
                if message is not None:
4945
 
                    subject += message
4946
 
                else:
4947
 
                    revision = branch.repository.get_revision(revision_id)
4948
 
                    subject += revision.get_summary()
4949
 
                basename = directive.get_disk_name(branch)
4950
 
                mail_client.compose_merge_request(mail_to, subject,
4951
 
                                                  outfile.getvalue(),
4952
 
                                                  basename, body)
4953
 
        finally:
4954
 
            if output != '-':
4955
 
                outfile.close()
4956
 
            branch.unlock()
 
5032
            format=None, mail_to=None, message=None, body=None,
 
5033
            strict=None, **kwargs):
 
5034
        from bzrlib.send import send
 
5035
        return send(submit_branch, revision, public_branch, remember,
 
5036
                    format, no_bundle, no_patch, output,
 
5037
                    kwargs.get('from', '.'), mail_to, message, body,
 
5038
                    self.outf,
 
5039
                    strict=strict)
4957
5040
 
4958
5041
 
4959
5042
class cmd_bundle_revisions(cmd_send):
4960
 
 
4961
5043
    """Create a merge-directive for submitting changes.
4962
5044
 
4963
5045
    A merge directive provides many things needed for requesting merges:
5004
5086
               type=unicode),
5005
5087
        Option('output', short_name='o', help='Write directive to this file.',
5006
5088
               type=unicode),
 
5089
        Option('strict',
 
5090
               help='Refuse to bundle revisions if there are uncommitted'
 
5091
               ' changes in the working tree, --no-strict disables the check.'),
5007
5092
        'revision',
5008
 
        RegistryOption.from_kwargs('format',
5009
 
        'Use the specified output format.',
5010
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
5011
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
5093
        RegistryOption('format',
 
5094
                       help='Use the specified output format.',
 
5095
                       lazy_registry=('bzrlib.send', 'format_registry')),
5012
5096
        ]
5013
5097
    aliases = ['bundle']
5014
5098
 
5018
5102
 
5019
5103
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5020
5104
            no_patch=False, revision=None, remember=False, output=None,
5021
 
            format='4', **kwargs):
 
5105
            format=None, strict=None, **kwargs):
5022
5106
        if output is None:
5023
5107
            output = '-'
5024
 
        return self._run(submit_branch, revision, public_branch, remember,
 
5108
        from bzrlib.send import send
 
5109
        return send(submit_branch, revision, public_branch, remember,
5025
5110
                         format, no_bundle, no_patch, output,
5026
 
                         kwargs.get('from', '.'), None, None, None)
 
5111
                         kwargs.get('from', '.'), None, None, None,
 
5112
                         self.outf, strict=strict)
5027
5113
 
5028
5114
 
5029
5115
class cmd_tag(Command):
5124
5210
        if not tags:
5125
5211
            return
5126
5212
 
5127
 
        if revision:
5128
 
            branch.lock_read()
5129
 
            try:
 
5213
        branch.lock_read()
 
5214
        try:
 
5215
            if revision:
5130
5216
                graph = branch.repository.get_graph()
5131
5217
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5132
5218
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5133
5219
                # only show revisions between revid1 and revid2 (inclusive)
5134
5220
                tags = [(tag, revid) for tag, revid in tags if
5135
5221
                    graph.is_between(revid, revid1, revid2)]
5136
 
            finally:
5137
 
                branch.unlock()
5138
 
        if sort == 'alpha':
5139
 
            tags.sort()
5140
 
        elif sort == 'time':
5141
 
            timestamps = {}
5142
 
            for tag, revid in tags:
5143
 
                try:
5144
 
                    revobj = branch.repository.get_revision(revid)
5145
 
                except errors.NoSuchRevision:
5146
 
                    timestamp = sys.maxint # place them at the end
5147
 
                else:
5148
 
                    timestamp = revobj.timestamp
5149
 
                timestamps[revid] = timestamp
5150
 
            tags.sort(key=lambda x: timestamps[x[1]])
5151
 
        if not show_ids:
5152
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5153
 
            revno_map = branch.get_revision_id_to_revno_map()
5154
 
            tags = [ (tag, '.'.join(map(str, revno_map.get(revid, ('?',)))))
5155
 
                        for tag, revid in tags ]
 
5222
            if sort == 'alpha':
 
5223
                tags.sort()
 
5224
            elif sort == 'time':
 
5225
                timestamps = {}
 
5226
                for tag, revid in tags:
 
5227
                    try:
 
5228
                        revobj = branch.repository.get_revision(revid)
 
5229
                    except errors.NoSuchRevision:
 
5230
                        timestamp = sys.maxint # place them at the end
 
5231
                    else:
 
5232
                        timestamp = revobj.timestamp
 
5233
                    timestamps[revid] = timestamp
 
5234
                tags.sort(key=lambda x: timestamps[x[1]])
 
5235
            if not show_ids:
 
5236
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5237
                for index, (tag, revid) in enumerate(tags):
 
5238
                    try:
 
5239
                        revno = branch.revision_id_to_dotted_revno(revid)
 
5240
                        if isinstance(revno, tuple):
 
5241
                            revno = '.'.join(map(str, revno))
 
5242
                    except errors.NoSuchRevision:
 
5243
                        # Bad tag data/merges can lead to tagged revisions
 
5244
                        # which are not in this branch. Fail gracefully ...
 
5245
                        revno = '?'
 
5246
                    tags[index] = (tag, revno)
 
5247
        finally:
 
5248
            branch.unlock()
5156
5249
        for tag, revspec in tags:
5157
5250
            self.outf.write('%-20s %s\n' % (tag, revspec))
5158
5251
 
5194
5287
            ),
5195
5288
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5196
5289
        Option('force',
5197
 
               help='Perform reconfiguration even if local changes'
5198
 
               ' will be lost.')
 
5290
            help='Perform reconfiguration even if local changes'
 
5291
            ' will be lost.'),
 
5292
        Option('stacked-on',
 
5293
            help='Reconfigure a branch to be stacked on another branch.',
 
5294
            type=unicode,
 
5295
            ),
 
5296
        Option('unstacked',
 
5297
            help='Reconfigure a branch to be unstacked.  This '
 
5298
                'may require copying substantial data into it.',
 
5299
            ),
5199
5300
        ]
5200
5301
 
5201
 
    def run(self, location=None, target_type=None, bind_to=None, force=False):
 
5302
    def run(self, location=None, target_type=None, bind_to=None, force=False,
 
5303
            stacked_on=None,
 
5304
            unstacked=None):
5202
5305
        directory = bzrdir.BzrDir.open(location)
 
5306
        if stacked_on and unstacked:
 
5307
            raise BzrCommandError("Can't use both --stacked-on and --unstacked")
 
5308
        elif stacked_on is not None:
 
5309
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
5310
        elif unstacked:
 
5311
            reconfigure.ReconfigureUnstacked().apply(directory)
 
5312
        # At the moment you can use --stacked-on and a different
 
5313
        # reconfiguration shape at the same time; there seems no good reason
 
5314
        # to ban it.
5203
5315
        if target_type is None:
5204
 
            raise errors.BzrCommandError('No target configuration specified')
 
5316
            if stacked_on or unstacked:
 
5317
                return
 
5318
            else:
 
5319
                raise errors.BzrCommandError('No target configuration '
 
5320
                    'specified')
5205
5321
        elif target_type == 'branch':
5206
5322
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5207
5323
        elif target_type == 'tree':
5250
5366
 
5251
5367
    takes_args = ['to_location']
5252
5368
    takes_options = [Option('force',
5253
 
                        help='Switch even if local commits will be lost.')
 
5369
                        help='Switch even if local commits will be lost.'),
 
5370
                     Option('create-branch', short_name='b',
 
5371
                        help='Create the target branch from this one before'
 
5372
                             ' switching to it.'),
5254
5373
                     ]
5255
5374
 
5256
 
    def run(self, to_location, force=False):
 
5375
    def run(self, to_location, force=False, create_branch=False):
5257
5376
        from bzrlib import switch
5258
5377
        tree_location = '.'
5259
5378
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5260
 
        branch = control_dir.open_branch()
5261
5379
        try:
5262
 
            to_branch = Branch.open(to_location)
 
5380
            branch = control_dir.open_branch()
 
5381
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5263
5382
        except errors.NotBranchError:
5264
 
            this_branch = control_dir.open_branch()
5265
 
            # This may be a heavy checkout, where we want the master branch
5266
 
            this_url = this_branch.get_bound_location()
5267
 
            # If not, use a local sibling
5268
 
            if this_url is None:
5269
 
                this_url = this_branch.base
5270
 
            to_branch = Branch.open(
5271
 
                urlutils.join(this_url, '..', to_location))
 
5383
            branch = None
 
5384
            had_explicit_nick = False
 
5385
        if create_branch:
 
5386
            if branch is None:
 
5387
                raise errors.BzrCommandError('cannot create branch without'
 
5388
                                             ' source branch')
 
5389
            if '/' not in to_location and '\\' not in to_location:
 
5390
                # This path is meant to be relative to the existing branch
 
5391
                this_url = self._get_branch_location(control_dir)
 
5392
                to_location = urlutils.join(this_url, '..', to_location)
 
5393
            to_branch = branch.bzrdir.sprout(to_location,
 
5394
                                 possible_transports=[branch.bzrdir.root_transport],
 
5395
                                 source_branch=branch).open_branch()
 
5396
            # try:
 
5397
            #     from_branch = control_dir.open_branch()
 
5398
            # except errors.NotBranchError:
 
5399
            #     raise BzrCommandError('Cannot create a branch from this'
 
5400
            #         ' location when we cannot open this branch')
 
5401
            # from_branch.bzrdir.sprout(
 
5402
            pass
 
5403
        else:
 
5404
            try:
 
5405
                to_branch = Branch.open(to_location)
 
5406
            except errors.NotBranchError:
 
5407
                this_url = self._get_branch_location(control_dir)
 
5408
                to_branch = Branch.open(
 
5409
                    urlutils.join(this_url, '..', to_location))
5272
5410
        switch.switch(control_dir, to_branch, force)
5273
 
        if branch.get_config().has_explicit_nickname():
 
5411
        if had_explicit_nick:
5274
5412
            branch = control_dir.open_branch() #get the new branch!
5275
5413
            branch.nick = to_branch.nick
5276
5414
        note('Switched to branch: %s',
5277
5415
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
5278
5416
 
 
5417
    def _get_branch_location(self, control_dir):
 
5418
        """Return location of branch for this control dir."""
 
5419
        try:
 
5420
            this_branch = control_dir.open_branch()
 
5421
            # This may be a heavy checkout, where we want the master branch
 
5422
            master_location = this_branch.get_bound_location()
 
5423
            if master_location is not None:
 
5424
                return master_location
 
5425
            # If not, use a local sibling
 
5426
            return this_branch.base
 
5427
        except errors.NotBranchError:
 
5428
            format = control_dir.find_branch_format()
 
5429
            if getattr(format, 'get_reference', None) is not None:
 
5430
                return format.get_reference(control_dir)
 
5431
            else:
 
5432
                return control_dir.root_transport.base
 
5433
 
5279
5434
 
5280
5435
class cmd_view(Command):
5281
5436
    """Manage filtered views.
5500
5655
        if writer is None:
5501
5656
            writer = bzrlib.option.diff_writer_registry.get()
5502
5657
        try:
5503
 
            Shelver.from_args(writer(sys.stdout), revision, all, file_list,
5504
 
                              message, destroy=destroy).run()
 
5658
            shelver = Shelver.from_args(writer(sys.stdout), revision, all,
 
5659
                file_list, message, destroy=destroy)
 
5660
            try:
 
5661
                shelver.run()
 
5662
            finally:
 
5663
                shelver.work_tree.unlock()
5505
5664
        except errors.UserAbort:
5506
5665
            return 0
5507
5666
 
5546
5705
 
5547
5706
    def run(self, shelf_id=None, action='apply'):
5548
5707
        from bzrlib.shelf_ui import Unshelver
5549
 
        Unshelver.from_args(shelf_id, action).run()
 
5708
        unshelver = Unshelver.from_args(shelf_id, action)
 
5709
        try:
 
5710
            unshelver.run()
 
5711
        finally:
 
5712
            unshelver.tree.unlock()
5550
5713
 
5551
5714
 
5552
5715
class cmd_clean_tree(Command):
5583
5746
                   dry_run=dry_run, no_prompt=force)
5584
5747
 
5585
5748
 
5586
 
def _create_prefix(cur_transport):
5587
 
    needed = [cur_transport]
5588
 
    # Recurse upwards until we can create a directory successfully
5589
 
    while True:
5590
 
        new_transport = cur_transport.clone('..')
5591
 
        if new_transport.base == cur_transport.base:
5592
 
            raise errors.BzrCommandError(
5593
 
                "Failed to create path prefix for %s."
5594
 
                % cur_transport.base)
5595
 
        try:
5596
 
            new_transport.mkdir('.')
5597
 
        except errors.NoSuchFile:
5598
 
            needed.append(new_transport)
5599
 
            cur_transport = new_transport
 
5749
class cmd_reference(Command):
 
5750
    """list, view and set branch locations for nested trees.
 
5751
 
 
5752
    If no arguments are provided, lists the branch locations for nested trees.
 
5753
    If one argument is provided, display the branch location for that tree.
 
5754
    If two arguments are provided, set the branch location for that tree.
 
5755
    """
 
5756
 
 
5757
    hidden = True
 
5758
 
 
5759
    takes_args = ['path?', 'location?']
 
5760
 
 
5761
    def run(self, path=None, location=None):
 
5762
        branchdir = '.'
 
5763
        if path is not None:
 
5764
            branchdir = path
 
5765
        tree, branch, relpath =(
 
5766
            bzrdir.BzrDir.open_containing_tree_or_branch(branchdir))
 
5767
        if path is not None:
 
5768
            path = relpath
 
5769
        if tree is None:
 
5770
            tree = branch.basis_tree()
 
5771
        if path is None:
 
5772
            info = branch._get_all_reference_info().iteritems()
 
5773
            self._display_reference_info(tree, branch, info)
5600
5774
        else:
5601
 
            break
5602
 
    # Now we only need to create child directories
5603
 
    while needed:
5604
 
        cur_transport = needed.pop()
5605
 
        cur_transport.ensure_base()
 
5775
            file_id = tree.path2id(path)
 
5776
            if file_id is None:
 
5777
                raise errors.NotVersionedError(path)
 
5778
            if location is None:
 
5779
                info = [(file_id, branch.get_reference_info(file_id))]
 
5780
                self._display_reference_info(tree, branch, info)
 
5781
            else:
 
5782
                branch.set_reference_info(file_id, path, location)
 
5783
 
 
5784
    def _display_reference_info(self, tree, branch, info):
 
5785
        ref_list = []
 
5786
        for file_id, (path, location) in info:
 
5787
            try:
 
5788
                path = tree.id2path(file_id)
 
5789
            except errors.NoSuchId:
 
5790
                pass
 
5791
            ref_list.append((path, location))
 
5792
        for path, location in sorted(ref_list):
 
5793
            self.outf.write('%s %s\n' % (path, location))
5606
5794
 
5607
5795
 
5608
5796
# these get imported and then picked up by the scan for cmd_*