~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-27 05:35:00 UTC
  • mfrom: (4570 +trunk)
  • mto: (4634.6.29 2.0)
  • mto: This revision was merged to the branch mainline in revision 4680.
  • Revision ID: andrew.bennetts@canonical.com-20090727053500-q76zsn2dx33jhmj5
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""builtin bzr commands"""
18
18
 
35
35
    config,
36
36
    errors,
37
37
    globbing,
 
38
    hooks,
38
39
    log,
39
40
    merge as _mod_merge,
40
41
    merge_directive,
41
42
    osutils,
42
43
    reconfigure,
 
44
    rename_map,
43
45
    revision as _mod_revision,
44
46
    symbol_versioning,
45
47
    transport,
46
 
    tree as _mod_tree,
47
48
    ui,
48
49
    urlutils,
49
50
    views,
77
78
 
78
79
 
79
80
def tree_files_for_add(file_list):
80
 
    """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
81
91
    if file_list:
82
 
        tree = WorkingTree.open_containing(file_list[0])[0]
 
92
        tree, relpath = WorkingTree.open_containing(file_list[0])
83
93
        if tree.supports_views():
84
94
            view_files = tree.views.lookup_view()
85
 
            for filename in file_list:
86
 
                if not osutils.is_inside_any(view_files, filename):
87
 
                    raise errors.FileOutsideView(filename, view_files)
 
95
            if view_files:
 
96
                for filename in file_list:
 
97
                    if not osutils.is_inside_any(view_files, filename):
 
98
                        raise errors.FileOutsideView(filename, view_files)
 
99
        file_list = file_list[:]
 
100
        file_list[0] = tree.abspath(relpath)
88
101
    else:
89
102
        tree = WorkingTree.open_containing(u'.')[0]
90
103
        if tree.supports_views():
92
105
            if view_files:
93
106
                file_list = view_files
94
107
                view_str = views.view_display_str(view_files)
95
 
                note("ignoring files outside view: %s" % view_str)
 
108
                note("Ignoring files outside view. View is %s" % view_str)
96
109
    return tree, file_list
97
110
 
98
111
 
148
161
            if view_files:
149
162
                file_list = view_files
150
163
                view_str = views.view_display_str(view_files)
151
 
                note("ignoring files outside view: %s" % view_str)
 
164
                note("Ignoring files outside view. View is %s" % view_str)
152
165
        return tree, file_list
153
166
    tree = WorkingTree.open_containing(osutils.realpath(file_list[0]))[0]
154
167
    return tree, safe_relpath_files(tree, file_list, canonicalize,
436
449
        except errors.NoWorkingTree:
437
450
            raise errors.BzrCommandError("No working tree to remove")
438
451
        except errors.NotLocalUrl:
439
 
            raise errors.BzrCommandError("You cannot remove the working tree of a "
440
 
                                         "remote path")
 
452
            raise errors.BzrCommandError("You cannot remove the working tree"
 
453
                                         " of a remote path")
441
454
        if not force:
442
 
            changes = working.changes_from(working.basis_tree())
443
 
            if changes.has_changed():
 
455
            # XXX: What about pending merges ? -- vila 20090629
 
456
            if working.has_changes(working.basis_tree()):
444
457
                raise errors.UncommittedChanges(working)
445
458
 
446
459
        working_path = working.bzrdir.root_transport.base
447
460
        branch_path = working.branch.bzrdir.root_transport.base
448
461
        if working_path != branch_path:
449
 
            raise errors.BzrCommandError("You cannot remove the working tree from "
450
 
                                         "a lightweight checkout")
 
462
            raise errors.BzrCommandError("You cannot remove the working tree"
 
463
                                         " from a lightweight checkout")
451
464
 
452
465
        d.destroy_workingtree()
453
466
 
460
473
 
461
474
    _see_also = ['info']
462
475
    takes_args = ['location?']
 
476
    takes_options = [
 
477
        Option('tree', help='Show revno of working tree'),
 
478
        ]
463
479
 
464
480
    @display_command
465
 
    def run(self, location=u'.'):
466
 
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
467
 
        self.outf.write('\n')
 
481
    def run(self, tree=False, location=u'.'):
 
482
        if tree:
 
483
            try:
 
484
                wt = WorkingTree.open_containing(location)[0]
 
485
                wt.lock_read()
 
486
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
487
                raise errors.NoWorkingTree(location)
 
488
            try:
 
489
                revid = wt.last_revision()
 
490
                try:
 
491
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
492
                except errors.NoSuchRevision:
 
493
                    revno_t = ('???',)
 
494
                revno = ".".join(str(n) for n in revno_t)
 
495
            finally:
 
496
                wt.unlock()
 
497
        else:
 
498
            b = Branch.open_containing(location)[0]
 
499
            b.lock_read()
 
500
            try:
 
501
                revno = b.revno()
 
502
            finally:
 
503
                b.unlock()
 
504
 
 
505
        self.outf.write(str(revno) + '\n')
468
506
 
469
507
 
470
508
class cmd_revision_info(Command):
480
518
            short_name='d',
481
519
            type=unicode,
482
520
            ),
 
521
        Option('tree', help='Show revno of working tree'),
483
522
        ]
484
523
 
485
524
    @display_command
486
 
    def run(self, revision=None, directory=u'.', revision_info_list=[]):
487
 
 
488
 
        revs = []
489
 
        if revision is not None:
490
 
            revs.extend(revision)
491
 
        if revision_info_list is not None:
492
 
            for rev in revision_info_list:
493
 
                revs.append(RevisionSpec.from_string(rev))
494
 
 
495
 
        b = Branch.open_containing(directory)[0]
496
 
 
497
 
        if len(revs) == 0:
498
 
            revs.append(RevisionSpec.from_string('-1'))
499
 
 
500
 
        for rev in revs:
501
 
            revision_id = rev.as_revision_id(b)
502
 
            try:
503
 
                revno = '%4d' % (b.revision_id_to_revno(revision_id))
504
 
            except errors.NoSuchRevision:
505
 
                dotted_map = b.get_revision_id_to_revno_map()
506
 
                revno = '.'.join(str(i) for i in dotted_map[revision_id])
507
 
            print '%s %s' % (revno, revision_id)
 
525
    def run(self, revision=None, directory=u'.', tree=False,
 
526
            revision_info_list=[]):
 
527
 
 
528
        try:
 
529
            wt = WorkingTree.open_containing(directory)[0]
 
530
            b = wt.branch
 
531
            wt.lock_read()
 
532
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
533
            wt = None
 
534
            b = Branch.open_containing(directory)[0]
 
535
            b.lock_read()
 
536
        try:
 
537
            revision_ids = []
 
538
            if revision is not None:
 
539
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
540
            if revision_info_list is not None:
 
541
                for rev_str in revision_info_list:
 
542
                    rev_spec = RevisionSpec.from_string(rev_str)
 
543
                    revision_ids.append(rev_spec.as_revision_id(b))
 
544
            # No arguments supplied, default to the last revision
 
545
            if len(revision_ids) == 0:
 
546
                if tree:
 
547
                    if wt is None:
 
548
                        raise errors.NoWorkingTree(directory)
 
549
                    revision_ids.append(wt.last_revision())
 
550
                else:
 
551
                    revision_ids.append(b.last_revision())
 
552
 
 
553
            revinfos = []
 
554
            maxlen = 0
 
555
            for revision_id in revision_ids:
 
556
                try:
 
557
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
558
                    revno = '.'.join(str(i) for i in dotted_revno)
 
559
                except errors.NoSuchRevision:
 
560
                    revno = '???'
 
561
                maxlen = max(maxlen, len(revno))
 
562
                revinfos.append([revno, revision_id])
 
563
        finally:
 
564
            if wt is None:
 
565
                b.unlock()
 
566
            else:
 
567
                wt.unlock()
 
568
 
 
569
        for ri in revinfos:
 
570
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
508
571
 
509
572
 
510
573
class cmd_add(Command):
585
648
        finally:
586
649
            if base_tree is not None:
587
650
                base_tree.unlock()
588
 
        if not is_quiet() and len(added) > 0:
589
 
            self.outf.write('add completed\n')
590
651
        if len(ignored) > 0:
591
652
            if verbose:
592
653
                for glob in sorted(ignored.keys()):
598
659
                for glob, paths in ignored.items():
599
660
                    match_len += len(paths)
600
661
                self.outf.write("ignored %d file(s).\n" % match_len)
601
 
            self.outf.write("If you wish to add some of these files,"
602
 
                            " please add them by name.\n")
 
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")
603
665
 
604
666
 
605
667
class cmd_mkdir(Command):
721
783
    takes_args = ['names*']
722
784
    takes_options = [Option("after", help="Move only the bzr identifier"
723
785
        " of the file, because the file has already been moved."),
 
786
        Option('auto', help='Automatically guess renames.'),
 
787
        Option('dry-run', help='Avoid making changes when guessing renames.'),
724
788
        ]
725
789
    aliases = ['move', 'rename']
726
790
    encoding_type = 'replace'
727
791
 
728
 
    def run(self, names_list, after=False):
 
792
    def run(self, names_list, after=False, auto=False, dry_run=False):
 
793
        if auto:
 
794
            return self.run_auto(names_list, after, dry_run)
 
795
        elif dry_run:
 
796
            raise errors.BzrCommandError('--dry-run requires --auto.')
729
797
        if names_list is None:
730
798
            names_list = []
731
 
 
732
799
        if len(names_list) < 2:
733
800
            raise errors.BzrCommandError("missing file argument")
734
801
        tree, rel_names = tree_files(names_list, canonicalize=False)
735
 
        tree.lock_write()
 
802
        tree.lock_tree_write()
736
803
        try:
737
804
            self._run(tree, names_list, rel_names, after)
738
805
        finally:
739
806
            tree.unlock()
740
807
 
 
808
    def run_auto(self, names_list, after, dry_run):
 
809
        if names_list is not None and len(names_list) > 1:
 
810
            raise errors.BzrCommandError('Only one path may be specified to'
 
811
                                         ' --auto.')
 
812
        if after:
 
813
            raise errors.BzrCommandError('--after cannot be specified with'
 
814
                                         ' --auto.')
 
815
        work_tree, file_list = tree_files(names_list, default_branch='.')
 
816
        work_tree.lock_tree_write()
 
817
        try:
 
818
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
819
        finally:
 
820
            work_tree.unlock()
 
821
 
741
822
    def _run(self, tree, names_list, rel_names, after):
742
823
        into_existing = osutils.isdir(names_list[-1])
743
824
        if into_existing and len(names_list) == 2:
841
922
    with bzr send.
842
923
    """
843
924
 
844
 
    _see_also = ['push', 'update', 'status-flags']
 
925
    _see_also = ['push', 'update', 'status-flags', 'send']
845
926
    takes_options = ['remember', 'overwrite', 'revision',
846
927
        custom_help('verbose',
847
928
            help='Show logs of pulled revisions.'),
851
932
            short_name='d',
852
933
            type=unicode,
853
934
            ),
 
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
            ),
854
940
        ]
855
941
    takes_args = ['location?']
856
942
    encoding_type = 'replace'
857
943
 
858
944
    def run(self, location=None, remember=False, overwrite=False,
859
945
            revision=None, verbose=False,
860
 
            directory=None):
 
946
            directory=None, local=False):
861
947
        # FIXME: too much stuff is in the command class
862
948
        revision_id = None
863
949
        mergeable = None
869
955
        except errors.NoWorkingTree:
870
956
            tree_to = None
871
957
            branch_to = Branch.open_containing(directory)[0]
 
958
        
 
959
        if local and not branch_to.get_bound_location():
 
960
            raise errors.LocalRequiresBoundBranch()
872
961
 
873
962
        possible_transports = []
874
963
        if location is not None:
906
995
            if branch_to.get_parent() is None or remember:
907
996
                branch_to.set_parent(branch_from.base)
908
997
 
909
 
        if revision is not None:
910
 
            revision_id = revision.as_revision_id(branch_from)
911
 
 
912
 
        branch_to.lock_write()
 
998
        if branch_from is not branch_to:
 
999
            branch_from.lock_read()
913
1000
        try:
914
 
            if tree_to is not None:
915
 
                view_info = _get_view_info_for_change_reporter(tree_to)
916
 
                change_reporter = delta._ChangeReporter(
917
 
                    unversioned_filter=tree_to.is_ignored, view_info=view_info)
918
 
                result = tree_to.pull(branch_from, overwrite, revision_id,
919
 
                                      change_reporter,
920
 
                                      possible_transports=possible_transports)
921
 
            else:
922
 
                result = branch_to.pull(branch_from, overwrite, revision_id)
923
 
 
924
 
            result.report(self.outf)
925
 
            if verbose and result.old_revid != result.new_revid:
926
 
                log.show_branch_change(branch_to, self.outf, result.old_revno,
927
 
                                       result.old_revid)
 
1001
            if revision is not None:
 
1002
                revision_id = revision.as_revision_id(branch_from)
 
1003
 
 
1004
            branch_to.lock_write()
 
1005
            try:
 
1006
                if tree_to is not None:
 
1007
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1008
                    change_reporter = delta._ChangeReporter(
 
1009
                        unversioned_filter=tree_to.is_ignored,
 
1010
                        view_info=view_info)
 
1011
                    result = tree_to.pull(
 
1012
                        branch_from, overwrite, revision_id, change_reporter,
 
1013
                        possible_transports=possible_transports, local=local)
 
1014
                else:
 
1015
                    result = branch_to.pull(
 
1016
                        branch_from, overwrite, revision_id, local=local)
 
1017
 
 
1018
                result.report(self.outf)
 
1019
                if verbose and result.old_revid != result.new_revid:
 
1020
                    log.show_branch_change(
 
1021
                        branch_to, self.outf, result.old_revno,
 
1022
                        result.old_revid)
 
1023
            finally:
 
1024
                branch_to.unlock()
928
1025
        finally:
929
 
            branch_to.unlock()
 
1026
            if branch_from is not branch_to:
 
1027
                branch_from.unlock()
930
1028
 
931
1029
 
932
1030
class cmd_push(Command):
979
1077
                'for the commit history. Only the work not present in the '
980
1078
                'referenced branch is included in the branch created.',
981
1079
            type=unicode),
 
1080
        Option('strict',
 
1081
               help='Refuse to push if there are uncommitted changes in'
 
1082
               ' the working tree, --no-strict disables the check.'),
982
1083
        ]
983
1084
    takes_args = ['location?']
984
1085
    encoding_type = 'replace'
986
1087
    def run(self, location=None, remember=False, overwrite=False,
987
1088
        create_prefix=False, verbose=False, revision=None,
988
1089
        use_existing_dir=False, directory=None, stacked_on=None,
989
 
        stacked=False):
 
1090
        stacked=False, strict=None):
990
1091
        from bzrlib.push import _show_push_branch
991
1092
 
992
 
        # Get the source branch and revision_id
993
1093
        if directory is None:
994
1094
            directory = '.'
995
 
        br_from = Branch.open_containing(directory)[0]
 
1095
        # Get the source branch
 
1096
        (tree, br_from,
 
1097
         _unused) = bzrdir.BzrDir.open_containing_tree_or_branch(directory)
 
1098
        if strict is None:
 
1099
            strict = br_from.get_config().get_user_option_as_bool('push_strict')
 
1100
        if strict is None: strict = True # default value
 
1101
        # Get the tip's revision_id
996
1102
        revision = _get_one_revision('push', revision)
997
1103
        if revision is not None:
998
1104
            revision_id = revision.in_history(br_from).rev_id
999
1105
        else:
1000
 
            revision_id = br_from.last_revision()
 
1106
            revision_id = None
 
1107
        if strict and tree is not None and revision_id is None:
 
1108
            if (tree.has_changes(tree.basis_tree())
 
1109
                or len(tree.get_parent_ids()) > 1):
 
1110
                raise errors.UncommittedChanges(
 
1111
                    tree, more='Use --no-strict to force the push.')
 
1112
            if tree.last_revision() != tree.branch.last_revision():
 
1113
                # The tree has lost sync with its branch, there is little
 
1114
                # chance that the user is aware of it but he can still force
 
1115
                # the push with --no-strict
 
1116
                raise errors.OutOfDateTree(
 
1117
                    tree, more='Use --no-strict to force the push.')
1001
1118
 
1002
1119
        # Get the stacked_on branch, if any
1003
1120
        if stacked_on is not None:
1036
1153
 
1037
1154
 
1038
1155
class cmd_branch(Command):
1039
 
    """Create a new copy of a branch.
 
1156
    """Create a new branch that is a copy of an existing branch.
1040
1157
 
1041
1158
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
1042
1159
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
1061
1178
                'branch for all operations.'),
1062
1179
        Option('standalone',
1063
1180
               help='Do not use a shared repository, even if available.'),
 
1181
        Option('use-existing-dir',
 
1182
               help='By default branch will fail if the target'
 
1183
                    ' directory exists, but does not already'
 
1184
                    ' have a control directory.  This flag will'
 
1185
                    ' allow branch to proceed.'),
1064
1186
        ]
1065
1187
    aliases = ['get', 'clone']
1066
1188
 
1067
1189
    def run(self, from_location, to_location=None, revision=None,
1068
 
            hardlink=False, stacked=False, standalone=False, no_tree=False):
 
1190
            hardlink=False, stacked=False, standalone=False, no_tree=False,
 
1191
            use_existing_dir=False):
1069
1192
        from bzrlib.tag import _merge_tags_if_possible
1070
1193
 
1071
1194
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1072
1195
            from_location)
 
1196
        if (accelerator_tree is not None and
 
1197
            accelerator_tree.supports_content_filtering()):
 
1198
            accelerator_tree = None
1073
1199
        revision = _get_one_revision('branch', revision)
1074
1200
        br_from.lock_read()
1075
1201
        try:
1086
1212
            try:
1087
1213
                to_transport.mkdir('.')
1088
1214
            except errors.FileExists:
1089
 
                raise errors.BzrCommandError('Target directory "%s" already'
1090
 
                                             ' exists.' % to_location)
 
1215
                if not use_existing_dir:
 
1216
                    raise errors.BzrCommandError('Target directory "%s" '
 
1217
                        'already exists.' % to_location)
 
1218
                else:
 
1219
                    try:
 
1220
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1221
                    except errors.NotBranchError:
 
1222
                        pass
 
1223
                    else:
 
1224
                        raise errors.AlreadyBranchError(to_location)
1091
1225
            except errors.NoSuchFile:
1092
1226
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1093
1227
                                             % to_location)
1300
1434
      basic statistics (like the number of files in the working tree and
1301
1435
      number of revisions in the branch and repository):
1302
1436
 
1303
 
        bzr -v info
 
1437
        bzr info -v
1304
1438
 
1305
1439
      Display the above together with number of committers to the branch:
1306
1440
 
1307
 
        bzr -vv info
 
1441
        bzr info -vv
1308
1442
    """
1309
1443
    _see_also = ['revno', 'working-trees', 'repositories']
1310
1444
    takes_args = ['location?']
1555
1689
                    "\nYou may supply --create-prefix to create all"
1556
1690
                    " leading parent directories."
1557
1691
                    % location)
1558
 
            _create_prefix(to_transport)
 
1692
            to_transport.create_prefix()
1559
1693
 
1560
1694
        try:
1561
1695
            a_bzrdir = bzrdir.BzrDir.open_from_transport(to_transport)
1579
1713
                branch.set_append_revisions_only(True)
1580
1714
            except errors.UpgradeRequired:
1581
1715
                raise errors.BzrCommandError('This branch format cannot be set'
1582
 
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1716
                    ' to append-revisions-only.  Try --default.')
1583
1717
        if not is_quiet():
1584
1718
            from bzrlib.info import describe_layout, describe_format
1585
1719
            try:
1932
2066
        --show-ids  display revision-ids (and file-ids), not just revnos
1933
2067
 
1934
2068
      Note that the default number of levels to display is a function of the
1935
 
      log format. If the -n option is not used, ``short`` and ``line`` show
1936
 
      just the top level (mainline) while ``long`` shows all levels of merged
1937
 
      revisions.
 
2069
      log format. If the -n option is not used, the standard log formats show
 
2070
      just the top level (mainline).
1938
2071
 
1939
2072
      Status summaries are shown using status flags like A, M, etc. To see
1940
2073
      the changes explained using words like ``added`` and ``modified``
1976
2109
 
1977
2110
    :Path filtering:
1978
2111
 
1979
 
      If a parameter is given and it's not a branch, the log will be filtered
1980
 
      to show only those revisions that changed the nominated file or
1981
 
      directory.
 
2112
      If parameters are given and the first one is not a branch, the log
 
2113
      will be filtered to show only those revisions that changed the
 
2114
      nominated files or directories.
1982
2115
 
1983
2116
      Filenames are interpreted within their historical context. To log a
1984
2117
      deleted file, specify a revision range so that the file existed at
2007
2140
      explicitly ask for this (and no way to stop logging a file back
2008
2141
      until it was last renamed).
2009
2142
 
2010
 
      Note: If the path is a directory, only revisions that directly changed
2011
 
      that directory object are currently shown. This is considered a bug.
2012
 
      (Support for filtering against multiple files and for files within a
2013
 
      directory is under development.)
2014
 
 
2015
2143
    :Other filtering:
2016
2144
 
2017
2145
      The --message option can be used for finding revisions that match a
2031
2159
      You may find it useful to add the aliases below to ``bazaar.conf``::
2032
2160
 
2033
2161
        [ALIASES]
2034
 
        tip = log -r-1 -n1
2035
 
        top = log -r-10.. --short --forward
2036
 
        show = log -v -p -n1 --long
 
2162
        tip = log -r-1
 
2163
        top = log -l10 --line
 
2164
        show = log -v -p
2037
2165
 
2038
2166
      ``bzr tip`` will then show the latest revision while ``bzr top``
2039
2167
      will show the last 10 mainline revisions. To see the details of a
2040
2168
      particular revision X,  ``bzr show -rX``.
2041
2169
 
2042
 
      As many GUI tools and Web interfaces do, you may prefer viewing
2043
 
      history collapsed initially. If you are interested in looking deeper
2044
 
      into a particular merge X, use ``bzr log -n0 -rX``. If you like
2045
 
      working this way, you may wish to either:
2046
 
 
2047
 
      * change your default log format to short (or line)
2048
 
      * add this alias: log = log -n1
 
2170
      If you are interested in looking deeper into a particular merge X,
 
2171
      use ``bzr log -n0 -rX``.
2049
2172
 
2050
2173
      ``bzr log -v`` on a branch with lots of history is currently
2051
2174
      very slow. A fix for this issue is currently under development.
2059
2182
 
2060
2183
      When exploring non-mainline history on large projects with deep
2061
2184
      history, the performance of log can be greatly improved by installing
2062
 
      the revnocache plugin. This plugin buffers historical information
 
2185
      the historycache plugin. This plugin buffers historical information
2063
2186
      trading disk space for faster speed.
2064
2187
    """
2065
 
    takes_args = ['location?']
 
2188
    takes_args = ['file*']
2066
2189
    _see_also = ['log-formats', 'revisionspec']
2067
2190
    takes_options = [
2068
2191
            Option('forward',
2096
2219
            Option('show-diff',
2097
2220
                   short_name='p',
2098
2221
                   help='Show changes made in each revision as a patch.'),
 
2222
            Option('include-merges',
 
2223
                   help='Show merged revisions like --levels 0 does.'),
2099
2224
            ]
2100
2225
    encoding_type = 'replace'
2101
2226
 
2102
2227
    @display_command
2103
 
    def run(self, location=None, timezone='original',
 
2228
    def run(self, file_list=None, timezone='original',
2104
2229
            verbose=False,
2105
2230
            show_ids=False,
2106
2231
            forward=False,
2110
2235
            levels=None,
2111
2236
            message=None,
2112
2237
            limit=None,
2113
 
            show_diff=False):
2114
 
        from bzrlib.log import show_log, _get_fileid_to_log
 
2238
            show_diff=False,
 
2239
            include_merges=False):
 
2240
        from bzrlib.log import (
 
2241
            Logger,
 
2242
            make_log_request_dict,
 
2243
            _get_info_for_log_files,
 
2244
            )
2115
2245
        direction = (forward and 'forward') or 'reverse'
 
2246
        if include_merges:
 
2247
            if levels is None:
 
2248
                levels = 0
 
2249
            else:
 
2250
                raise errors.BzrCommandError(
 
2251
                    '--levels and --include-merges are mutually exclusive')
2116
2252
 
2117
2253
        if change is not None:
2118
2254
            if len(change) > 1:
2123
2259
            else:
2124
2260
                revision = change
2125
2261
 
2126
 
        # log everything
2127
 
        file_id = None
2128
 
        if location:
2129
 
            # find the file id to log:
2130
 
 
2131
 
            tree, b, fp = bzrdir.BzrDir.open_containing_tree_or_branch(
2132
 
                location)
2133
 
            if fp != '':
2134
 
                file_id = _get_fileid_to_log(revision, tree, b, fp)
 
2262
        file_ids = []
 
2263
        filter_by_dir = False
 
2264
        if file_list:
 
2265
            # find the file ids to log and check for directory filtering
 
2266
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
 
2267
                file_list)
 
2268
            for relpath, file_id, kind in file_info_list:
2135
2269
                if file_id is None:
2136
2270
                    raise errors.BzrCommandError(
2137
2271
                        "Path unknown at end or start of revision range: %s" %
2138
 
                        location)
 
2272
                        relpath)
 
2273
                # If the relpath is the top of the tree, we log everything
 
2274
                if relpath == '':
 
2275
                    file_ids = []
 
2276
                    break
 
2277
                else:
 
2278
                    file_ids.append(file_id)
 
2279
                filter_by_dir = filter_by_dir or (
 
2280
                    kind in ['directory', 'tree-reference'])
2139
2281
        else:
2140
 
            # local dir only
 
2282
            # log everything
2141
2283
            # FIXME ? log the current subdir only RBC 20060203
2142
2284
            if revision is not None \
2143
2285
                    and len(revision) > 0 and revision[0].get_branch():
2146
2288
                location = '.'
2147
2289
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2148
2290
            b = dir.open_branch()
2149
 
 
2150
 
        b.lock_read()
2151
 
        try:
2152
2291
            rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2292
 
 
2293
        # Decide on the type of delta & diff filtering to use
 
2294
        # TODO: add an --all-files option to make this configurable & consistent
 
2295
        if not verbose:
 
2296
            delta_type = None
 
2297
        else:
 
2298
            delta_type = 'full'
 
2299
        if not show_diff:
 
2300
            diff_type = None
 
2301
        elif file_ids:
 
2302
            diff_type = 'partial'
 
2303
        else:
 
2304
            diff_type = 'full'
 
2305
 
 
2306
        b.lock_read()
 
2307
        try:
 
2308
            # Build the log formatter
2153
2309
            if log_format is None:
2154
2310
                log_format = log.log_formatter_registry.get_default(b)
2155
 
 
2156
2311
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2157
2312
                            show_timezone=timezone,
2158
2313
                            delta_format=get_verbosity_level(),
2159
 
                            levels=levels)
2160
 
 
2161
 
            show_log(b,
2162
 
                     lf,
2163
 
                     file_id,
2164
 
                     verbose=verbose,
2165
 
                     direction=direction,
2166
 
                     start_revision=rev1,
2167
 
                     end_revision=rev2,
2168
 
                     search=message,
2169
 
                     limit=limit,
2170
 
                     show_diff=show_diff)
 
2314
                            levels=levels,
 
2315
                            show_advice=levels is None)
 
2316
 
 
2317
            # Choose the algorithm for doing the logging. It's annoying
 
2318
            # having multiple code paths like this but necessary until
 
2319
            # the underlying repository format is faster at generating
 
2320
            # deltas or can provide everything we need from the indices.
 
2321
            # The default algorithm - match-using-deltas - works for
 
2322
            # multiple files and directories and is faster for small
 
2323
            # amounts of history (200 revisions say). However, it's too
 
2324
            # slow for logging a single file in a repository with deep
 
2325
            # history, i.e. > 10K revisions. In the spirit of "do no
 
2326
            # evil when adding features", we continue to use the
 
2327
            # original algorithm - per-file-graph - for the "single
 
2328
            # file that isn't a directory without showing a delta" case.
 
2329
            partial_history = revision and b.repository._format.supports_chks
 
2330
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2331
                or delta_type or partial_history)
 
2332
 
 
2333
            # Build the LogRequest and execute it
 
2334
            if len(file_ids) == 0:
 
2335
                file_ids = None
 
2336
            rqst = make_log_request_dict(
 
2337
                direction=direction, specific_fileids=file_ids,
 
2338
                start_revision=rev1, end_revision=rev2, limit=limit,
 
2339
                message_search=message, delta_type=delta_type,
 
2340
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2341
            Logger(b, rqst).show(lf)
2171
2342
        finally:
2172
2343
            b.unlock()
2173
2344
 
2176
2347
    """Take the input of a revision option and turn it into a revision range.
2177
2348
 
2178
2349
    It returns RevisionInfo objects which can be used to obtain the rev_id's
2179
 
    of the desired revisons. It does some user input validations.
 
2350
    of the desired revisions. It does some user input validations.
2180
2351
    """
2181
2352
    if revisionspec_list is None:
2182
2353
        rev1 = None
2251
2422
 
2252
2423
    _see_also = ['status', 'cat']
2253
2424
    takes_args = ['path?']
2254
 
    # TODO: Take a revision or remote path and list that tree instead.
2255
2425
    takes_options = [
2256
2426
            'verbose',
2257
2427
            'revision',
2258
 
            Option('non-recursive',
2259
 
                   help='Don\'t recurse into subdirectories.'),
 
2428
            Option('recursive', short_name='R',
 
2429
                   help='Recurse into subdirectories.'),
2260
2430
            Option('from-root',
2261
2431
                   help='Print paths relative to the root of the branch.'),
2262
2432
            Option('unknown', help='Print unknown files.'),
2273
2443
            ]
2274
2444
    @display_command
2275
2445
    def run(self, revision=None, verbose=False,
2276
 
            non_recursive=False, from_root=False,
 
2446
            recursive=False, from_root=False,
2277
2447
            unknown=False, versioned=False, ignored=False,
2278
2448
            null=False, kind=None, show_ids=False, path=None):
2279
2449
 
2288
2458
 
2289
2459
        if path is None:
2290
2460
            fs_path = '.'
2291
 
            prefix = ''
2292
2461
        else:
2293
2462
            if from_root:
2294
2463
                raise errors.BzrCommandError('cannot specify both --from-root'
2295
2464
                                             ' and PATH')
2296
2465
            fs_path = path
2297
 
            prefix = path
2298
2466
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2299
2467
            fs_path)
 
2468
 
 
2469
        # Calculate the prefix to use
 
2470
        prefix = None
2300
2471
        if from_root:
2301
 
            relpath = u''
2302
 
        elif relpath:
2303
 
            relpath += '/'
 
2472
            if relpath:
 
2473
                prefix = relpath + '/'
 
2474
        elif fs_path != '.':
 
2475
            prefix = fs_path + '/'
 
2476
 
2304
2477
        if revision is not None or tree is None:
2305
2478
            tree = _get_one_revision_tree('ls', revision, branch=branch)
2306
2479
 
2310
2483
            if view_files:
2311
2484
                apply_view = True
2312
2485
                view_str = views.view_display_str(view_files)
2313
 
                note("ignoring files outside view: %s" % view_str)
 
2486
                note("Ignoring files outside view. View is %s" % view_str)
2314
2487
 
2315
2488
        tree.lock_read()
2316
2489
        try:
2317
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2318
 
                if fp.startswith(relpath):
2319
 
                    fp = osutils.pathjoin(prefix, fp[len(relpath):])
2320
 
                    if non_recursive and '/' in fp:
2321
 
                        continue
2322
 
                    if not all and not selection[fc]:
2323
 
                        continue
2324
 
                    if kind is not None and fkind != kind:
2325
 
                        continue
2326
 
                    if apply_view:
2327
 
                        try:
2328
 
                            views.check_path_in_view(tree, fp)
2329
 
                        except errors.FileOutsideView:
2330
 
                            continue
2331
 
                    kindch = entry.kind_character()
2332
 
                    outstring = fp + kindch
2333
 
                    if verbose:
2334
 
                        outstring = '%-8s %s' % (fc, outstring)
2335
 
                        if show_ids and fid is not None:
2336
 
                            outstring = "%-50s %s" % (outstring, fid)
2337
 
                        self.outf.write(outstring + '\n')
2338
 
                    elif null:
2339
 
                        self.outf.write(fp + '\0')
2340
 
                        if show_ids:
2341
 
                            if fid is not None:
2342
 
                                self.outf.write(fid)
2343
 
                            self.outf.write('\0')
2344
 
                        self.outf.flush()
2345
 
                    else:
 
2490
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2491
                from_dir=relpath, recursive=recursive):
 
2492
                # Apply additional masking
 
2493
                if not all and not selection[fc]:
 
2494
                    continue
 
2495
                if kind is not None and fkind != kind:
 
2496
                    continue
 
2497
                if apply_view:
 
2498
                    try:
 
2499
                        if relpath:
 
2500
                            fullpath = osutils.pathjoin(relpath, fp)
 
2501
                        else:
 
2502
                            fullpath = fp
 
2503
                        views.check_path_in_view(tree, fullpath)
 
2504
                    except errors.FileOutsideView:
 
2505
                        continue
 
2506
 
 
2507
                # Output the entry
 
2508
                if prefix:
 
2509
                    fp = osutils.pathjoin(prefix, fp)
 
2510
                kindch = entry.kind_character()
 
2511
                outstring = fp + kindch
 
2512
                ui.ui_factory.clear_term()
 
2513
                if verbose:
 
2514
                    outstring = '%-8s %s' % (fc, outstring)
 
2515
                    if show_ids and fid is not None:
 
2516
                        outstring = "%-50s %s" % (outstring, fid)
 
2517
                    self.outf.write(outstring + '\n')
 
2518
                elif null:
 
2519
                    self.outf.write(fp + '\0')
 
2520
                    if show_ids:
 
2521
                        if fid is not None:
 
2522
                            self.outf.write(fid)
 
2523
                        self.outf.write('\0')
 
2524
                    self.outf.flush()
 
2525
                else:
 
2526
                    if show_ids:
2346
2527
                        if fid is not None:
2347
2528
                            my_id = fid
2348
2529
                        else:
2349
2530
                            my_id = ''
2350
 
                        if show_ids:
2351
 
                            self.outf.write('%-50s %s\n' % (outstring, my_id))
2352
 
                        else:
2353
 
                            self.outf.write(outstring + '\n')
 
2531
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2532
                    else:
 
2533
                        self.outf.write(outstring + '\n')
2354
2534
        finally:
2355
2535
            tree.unlock()
2356
2536
 
2441
2621
        tree.unlock()
2442
2622
        if len(matches) > 0:
2443
2623
            print "Warning: the following files are version controlled and" \
2444
 
                  " match your ignore pattern:\n%s" % ("\n".join(matches),)
 
2624
                  " match your ignore pattern:\n%s" \
 
2625
                  "\nThese files will continue to be version controlled" \
 
2626
                  " unless you 'bzr remove' them." % ("\n".join(matches),)
2445
2627
 
2446
2628
 
2447
2629
class cmd_ignored(Command):
2526
2708
               help="Type of file to export to.",
2527
2709
               type=unicode),
2528
2710
        'revision',
 
2711
        Option('filters', help='Apply content filters to export the '
 
2712
                'convenient form.'),
2529
2713
        Option('root',
2530
2714
               type=str,
2531
2715
               help="Name of the root directory inside the exported file."),
2532
2716
        ]
2533
2717
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
2534
 
        root=None):
 
2718
        root=None, filters=False):
2535
2719
        from bzrlib.export import export
2536
2720
 
2537
2721
        if branch_or_subdir is None:
2544
2728
 
2545
2729
        rev_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
2546
2730
        try:
2547
 
            export(rev_tree, dest, format, root, subdir)
 
2731
            export(rev_tree, dest, format, root, subdir, filtered=filters)
2548
2732
        except errors.NoSuchExportFormat, e:
2549
2733
            raise errors.BzrCommandError('Unsupported export format: %s' % e.format)
2550
2734
 
2561
2745
    _see_also = ['ls']
2562
2746
    takes_options = [
2563
2747
        Option('name-from-revision', help='The path name in the old tree.'),
 
2748
        Option('filters', help='Apply content filters to display the '
 
2749
                'convenience form.'),
2564
2750
        'revision',
2565
2751
        ]
2566
2752
    takes_args = ['filename']
2567
2753
    encoding_type = 'exact'
2568
2754
 
2569
2755
    @display_command
2570
 
    def run(self, filename, revision=None, name_from_revision=False):
 
2756
    def run(self, filename, revision=None, name_from_revision=False,
 
2757
            filters=False):
2571
2758
        if revision is not None and len(revision) != 1:
2572
2759
            raise errors.BzrCommandError("bzr cat --revision takes exactly"
2573
2760
                                         " one revision specifier")
2576
2763
        branch.lock_read()
2577
2764
        try:
2578
2765
            return self._run(tree, branch, relpath, filename, revision,
2579
 
                             name_from_revision)
 
2766
                             name_from_revision, filters)
2580
2767
        finally:
2581
2768
            branch.unlock()
2582
2769
 
2583
 
    def _run(self, tree, b, relpath, filename, revision, name_from_revision):
 
2770
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
 
2771
        filtered):
2584
2772
        if tree is None:
2585
2773
            tree = b.basis_tree()
2586
2774
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2587
2775
 
2588
 
        cur_file_id = tree.path2id(relpath)
2589
2776
        old_file_id = rev_tree.path2id(relpath)
2590
2777
 
2591
2778
        if name_from_revision:
 
2779
            # Try in revision if requested
2592
2780
            if old_file_id is None:
2593
2781
                raise errors.BzrCommandError(
2594
2782
                    "%r is not present in revision %s" % (
2595
2783
                        filename, rev_tree.get_revision_id()))
2596
2784
            else:
2597
2785
                content = rev_tree.get_file_text(old_file_id)
2598
 
        elif cur_file_id is not None:
2599
 
            content = rev_tree.get_file_text(cur_file_id)
2600
 
        elif old_file_id is not None:
2601
 
            content = rev_tree.get_file_text(old_file_id)
2602
 
        else:
2603
 
            raise errors.BzrCommandError(
2604
 
                "%r is not present in revision %s" % (
2605
 
                    filename, rev_tree.get_revision_id()))
2606
 
        self.outf.write(content)
 
2786
        else:
 
2787
            cur_file_id = tree.path2id(relpath)
 
2788
            found = False
 
2789
            if cur_file_id is not None:
 
2790
                # Then try with the actual file id
 
2791
                try:
 
2792
                    content = rev_tree.get_file_text(cur_file_id)
 
2793
                    found = True
 
2794
                except errors.NoSuchId:
 
2795
                    # The actual file id didn't exist at that time
 
2796
                    pass
 
2797
            if not found and old_file_id is not None:
 
2798
                # Finally try with the old file id
 
2799
                content = rev_tree.get_file_text(old_file_id)
 
2800
                found = True
 
2801
            if not found:
 
2802
                # Can't be found anywhere
 
2803
                raise errors.BzrCommandError(
 
2804
                    "%r is not present in revision %s" % (
 
2805
                        filename, rev_tree.get_revision_id()))
 
2806
        if filtered:
 
2807
            from bzrlib.filters import (
 
2808
                ContentFilterContext,
 
2809
                filtered_output_bytes,
 
2810
                )
 
2811
            filters = rev_tree._content_filter_stack(relpath)
 
2812
            chunks = content.splitlines(True)
 
2813
            content = filtered_output_bytes(chunks, filters,
 
2814
                ContentFilterContext(relpath, rev_tree))
 
2815
            self.outf.writelines(content)
 
2816
        else:
 
2817
            self.outf.write(content)
2607
2818
 
2608
2819
 
2609
2820
class cmd_local_time_offset(Command):
2618
2829
class cmd_commit(Command):
2619
2830
    """Commit changes into a new revision.
2620
2831
 
2621
 
    If no arguments are given, the entire tree is committed.
2622
 
 
2623
 
    If selected files are specified, only changes to those files are
2624
 
    committed.  If a directory is specified then the directory and everything
2625
 
    within it is committed.
2626
 
 
2627
 
    When excludes are given, they take precedence over selected files.
2628
 
    For example, too commit only changes within foo, but not changes within
2629
 
    foo/bar::
2630
 
 
2631
 
      bzr commit foo -x foo/bar
2632
 
 
2633
 
    If author of the change is not the same person as the committer, you can
2634
 
    specify the author's name using the --author option. The name should be
2635
 
    in the same format as a committer-id, e.g. "John Doe <jdoe@example.com>".
2636
 
    If there is more than one author of the change you can specify the option
2637
 
    multiple times, once for each author.
2638
 
 
2639
 
    A selected-file commit may fail in some cases where the committed
2640
 
    tree would be invalid. Consider::
2641
 
 
2642
 
      bzr init foo
2643
 
      mkdir foo/bar
2644
 
      bzr add foo/bar
2645
 
      bzr commit foo -m "committing foo"
2646
 
      bzr mv foo/bar foo/baz
2647
 
      mkdir foo/bar
2648
 
      bzr add foo/bar
2649
 
      bzr commit foo/bar -m "committing bar but not baz"
2650
 
 
2651
 
    In the example above, the last commit will fail by design. This gives
2652
 
    the user the opportunity to decide whether they want to commit the
2653
 
    rename at the same time, separately first, or not at all. (As a general
2654
 
    rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
2655
 
 
2656
 
    Note: A selected-file commit after a merge is not yet supported.
 
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.)
2657
2901
    """
2658
2902
    # TODO: Run hooks on tree to-be-committed, and after commit.
2659
2903
 
2664
2908
 
2665
2909
    # XXX: verbose currently does nothing
2666
2910
 
2667
 
    _see_also = ['bugs', 'uncommit']
 
2911
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
2668
2912
    takes_args = ['selected*']
2669
2913
    takes_options = [
2670
2914
            ListOption('exclude', type=str, short_name='x',
2683
2927
                    help="Refuse to commit if there are unknown "
2684
2928
                    "files in the working tree."),
2685
2929
             ListOption('fixes', type=str,
2686
 
                    help="Mark a bug as being fixed by this revision."),
 
2930
                    help="Mark a bug as being fixed by this revision "
 
2931
                         "(see \"bzr help bugs\")."),
2687
2932
             ListOption('author', type=unicode,
2688
2933
                    help="Set the author's name, if it's different "
2689
2934
                         "from the committer."),
2699
2944
             ]
2700
2945
    aliases = ['ci', 'checkin']
2701
2946
 
2702
 
    def _get_bug_fix_properties(self, fixes, branch):
2703
 
        properties = []
 
2947
    def _iter_bug_fix_urls(self, fixes, branch):
2704
2948
        # Configure the properties for bug fixing attributes.
2705
2949
        for fixed_bug in fixes:
2706
2950
            tokens = fixed_bug.split(':')
2707
2951
            if len(tokens) != 2:
2708
2952
                raise errors.BzrCommandError(
2709
 
                    "Invalid bug %s. Must be in the form of 'tag:id'. "
2710
 
                    "Commit refused." % fixed_bug)
 
2953
                    "Invalid bug %s. Must be in the form of 'tracker:id'. "
 
2954
                    "See \"bzr help bugs\" for more information on this "
 
2955
                    "feature.\nCommit refused." % fixed_bug)
2711
2956
            tag, bug_id = tokens
2712
2957
            try:
2713
 
                bug_url = bugtracker.get_bug_url(tag, branch, bug_id)
 
2958
                yield bugtracker.get_bug_url(tag, branch, bug_id)
2714
2959
            except errors.UnknownBugTrackerAbbreviation:
2715
2960
                raise errors.BzrCommandError(
2716
2961
                    'Unrecognized bug %s. Commit refused.' % fixed_bug)
2717
 
            except errors.MalformedBugIdentifier:
 
2962
            except errors.MalformedBugIdentifier, e:
2718
2963
                raise errors.BzrCommandError(
2719
 
                    "Invalid bug identifier for %s. Commit refused."
2720
 
                    % fixed_bug)
2721
 
            properties.append('%s fixed' % bug_url)
2722
 
        return '\n'.join(properties)
 
2964
                    "%s\nCommit refused." % (str(e),))
2723
2965
 
2724
2966
    def run(self, message=None, file=None, verbose=False, selected_list=None,
2725
2967
            unchanged=False, strict=False, local=False, fixes=None,
2752
2994
 
2753
2995
        if fixes is None:
2754
2996
            fixes = []
2755
 
        bug_property = self._get_bug_fix_properties(fixes, tree.branch)
 
2997
        bug_property = bugtracker.encode_fixes_bug_urls(
 
2998
            self._iter_bug_fix_urls(fixes, tree.branch))
2756
2999
        if bug_property:
2757
3000
            properties['bugs'] = bug_property
2758
3001
 
2792
3035
        except PointlessCommit:
2793
3036
            # FIXME: This should really happen before the file is read in;
2794
3037
            # perhaps prepare the commit; get the message; then actually commit
2795
 
            raise errors.BzrCommandError("no changes to commit."
2796
 
                              " use --unchanged to commit anyhow")
 
3038
            raise errors.BzrCommandError("No changes to commit."
 
3039
                              " Use --unchanged to commit anyhow.")
2797
3040
        except ConflictsInTree:
2798
3041
            raise errors.BzrCommandError('Conflicts detected in working '
2799
3042
                'tree.  Use "bzr conflicts" to list, "bzr resolve FILE" to'
2817
3060
    The working tree and branch checks will only give output if a problem is
2818
3061
    detected. The output fields of the repository check are:
2819
3062
 
2820
 
        revisions: This is just the number of revisions checked.  It doesn't
2821
 
            indicate a problem.
2822
 
        versionedfiles: This is just the number of versionedfiles checked.  It
2823
 
            doesn't indicate a problem.
2824
 
        unreferenced ancestors: Texts that are ancestors of other texts, but
2825
 
            are not properly referenced by the revision ancestry.  This is a
2826
 
            subtle problem that Bazaar can work around.
2827
 
        unique file texts: This is the total number of unique file contents
2828
 
            seen in the checked revisions.  It does not indicate a problem.
2829
 
        repeated file texts: This is the total number of repeated texts seen
2830
 
            in the checked revisions.  Texts can be repeated when their file
2831
 
            entries are modified, but the file contents are not.  It does not
2832
 
            indicate a problem.
 
3063
    revisions
 
3064
        This is just the number of revisions checked.  It doesn't
 
3065
        indicate a problem.
 
3066
 
 
3067
    versionedfiles
 
3068
        This is just the number of versionedfiles checked.  It
 
3069
        doesn't indicate a problem.
 
3070
 
 
3071
    unreferenced ancestors
 
3072
        Texts that are ancestors of other texts, but
 
3073
        are not properly referenced by the revision ancestry.  This is a
 
3074
        subtle problem that Bazaar can work around.
 
3075
 
 
3076
    unique file texts
 
3077
        This is the total number of unique file contents
 
3078
        seen in the checked revisions.  It does not indicate a problem.
 
3079
 
 
3080
    repeated file texts
 
3081
        This is the total number of repeated texts seen
 
3082
        in the checked revisions.  Texts can be repeated when their file
 
3083
        entries are modified, but the file contents are not.  It does not
 
3084
        indicate a problem.
2833
3085
 
2834
3086
    If no restrictions are specified, all Bazaar data that is found at the given
2835
3087
    location will be checked.
2890
3142
 
2891
3143
    def run(self, url='.', format=None):
2892
3144
        from bzrlib.upgrade import upgrade
2893
 
        if format is None:
2894
 
            format = bzrdir.format_registry.make_bzrdir('default')
2895
3145
        upgrade(url, format)
2896
3146
 
2897
3147
 
3124
3374
                            ),
3125
3375
                     Option('list-only',
3126
3376
                            help='List the tests instead of running them.'),
 
3377
                     RegistryOption('parallel',
 
3378
                        help="Run the test suite in parallel.",
 
3379
                        lazy_registry=('bzrlib.tests', 'parallel_registry'),
 
3380
                        value_switches=False,
 
3381
                        ),
3127
3382
                     Option('randomize', type=str, argname="SEED",
3128
3383
                            help='Randomize the order of tests using the given'
3129
3384
                                 ' seed or "now" for the current time.'),
3131
3386
                            short_name='x',
3132
3387
                            help='Exclude tests that match this regular'
3133
3388
                                 ' expression.'),
 
3389
                     Option('subunit',
 
3390
                        help='Output test progress via subunit.'),
3134
3391
                     Option('strict', help='Fail on missing dependencies or '
3135
3392
                            'known failures.'),
3136
3393
                     Option('load-list', type=str, argname='TESTLISTFILE',
3153
3410
            lsprof_timed=None, cache_dir=None,
3154
3411
            first=False, list_only=False,
3155
3412
            randomize=None, exclude=None, strict=False,
3156
 
            load_list=None, debugflag=None, starting_with=None):
 
3413
            load_list=None, debugflag=None, starting_with=None, subunit=False,
 
3414
            parallel=None):
3157
3415
        from bzrlib.tests import selftest
3158
3416
        import bzrlib.benchmarks as benchmarks
3159
3417
        from bzrlib.benchmarks import tree_creator
3163
3421
 
3164
3422
        if cache_dir is not None:
3165
3423
            tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir)
3166
 
        if not list_only:
3167
 
            print 'testing: %s' % (osutils.realpath(sys.argv[0]),)
3168
 
            print '   %s (%s python%s)' % (
3169
 
                    bzrlib.__path__[0],
3170
 
                    bzrlib.version_string,
3171
 
                    bzrlib._format_version_tuple(sys.version_info),
3172
 
                    )
3173
 
        print
3174
3424
        if testspecs_list is not None:
3175
3425
            pattern = '|'.join(testspecs_list)
3176
3426
        else:
3177
3427
            pattern = ".*"
 
3428
        if subunit:
 
3429
            try:
 
3430
                from bzrlib.tests import SubUnitBzrRunner
 
3431
            except ImportError:
 
3432
                raise errors.BzrCommandError("subunit not available. subunit "
 
3433
                    "needs to be installed to use --subunit.")
 
3434
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunner
 
3435
        if parallel:
 
3436
            self.additional_selftest_args.setdefault(
 
3437
                'suite_decorators', []).append(parallel)
3178
3438
        if benchmark:
3179
3439
            test_suite_factory = benchmarks.test_suite
3180
3440
            # Unless user explicitly asks for quiet, be verbose in benchmarks
3206
3466
        finally:
3207
3467
            if benchfile is not None:
3208
3468
                benchfile.close()
3209
 
        if result:
3210
 
            note('tests passed')
3211
 
        else:
3212
 
            note('tests failed')
3213
3469
        return int(not result)
3214
3470
 
3215
3471
 
3308
3564
    merge refuses to run if there are any uncommitted changes, unless
3309
3565
    --force is given.
3310
3566
 
 
3567
    To select only some changes to merge, use "merge -i", which will prompt
 
3568
    you to apply each diff hunk and file change, similar to "shelve".
 
3569
 
3311
3570
    :Examples:
3312
3571
        To merge the latest revision from bzr.dev::
3313
3572
 
3321
3580
 
3322
3581
            bzr merge -r 81..82 ../bzr.dev
3323
3582
 
3324
 
        To apply a merge directive contained in in /tmp/merge:
 
3583
        To apply a merge directive contained in /tmp/merge:
3325
3584
 
3326
3585
            bzr merge /tmp/merge
3327
3586
    """
3328
3587
 
3329
3588
    encoding_type = 'exact'
3330
 
    _see_also = ['update', 'remerge', 'status-flags']
 
3589
    _see_also = ['update', 'remerge', 'status-flags', 'send']
3331
3590
    takes_args = ['location?']
3332
3591
    takes_options = [
3333
3592
        'change',
3351
3610
               short_name='d',
3352
3611
               type=unicode,
3353
3612
               ),
3354
 
        Option('preview', help='Instead of merging, show a diff of the merge.')
 
3613
        Option('preview', help='Instead of merging, show a diff of the'
 
3614
               ' merge.'),
 
3615
        Option('interactive', help='Select changes interactively.',
 
3616
            short_name='i')
3355
3617
    ]
3356
3618
 
3357
3619
    def run(self, location=None, revision=None, force=False,
3359
3621
            uncommitted=False, pull=False,
3360
3622
            directory=None,
3361
3623
            preview=False,
 
3624
            interactive=False,
3362
3625
            ):
3363
3626
        if merge_type is None:
3364
3627
            merge_type = _mod_merge.Merge3Merger
3369
3632
        allow_pending = True
3370
3633
        verified = 'inapplicable'
3371
3634
        tree = WorkingTree.open_containing(directory)[0]
 
3635
 
 
3636
        # die as quickly as possible if there are uncommitted changes
 
3637
        try:
 
3638
            basis_tree = tree.revision_tree(tree.last_revision())
 
3639
        except errors.NoSuchRevision:
 
3640
            basis_tree = tree.basis_tree()
 
3641
        if not force:
 
3642
            if tree.has_changes(basis_tree):
 
3643
                raise errors.UncommittedChanges(tree)
 
3644
 
3372
3645
        view_info = _get_view_info_for_change_reporter(tree)
3373
3646
        change_reporter = delta._ChangeReporter(
3374
3647
            unversioned_filter=tree.is_ignored, view_info=view_info)
3399
3672
                if revision is not None and len(revision) > 0:
3400
3673
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3401
3674
                        ' --revision at the same time.')
3402
 
                location = self._select_branch_location(tree, location)[0]
3403
 
                other_tree, other_path = WorkingTree.open_containing(location)
3404
 
                merger = _mod_merge.Merger.from_uncommitted(tree, other_tree,
3405
 
                    pb)
 
3675
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3676
                                                          cleanups)
3406
3677
                allow_pending = False
3407
 
                if other_path != '':
3408
 
                    merger.interesting_files = [other_path]
3409
3678
 
3410
3679
            if merger is None:
3411
3680
                merger, allow_pending = self._get_merger_from_branch(tree,
3427
3696
                                       merger.other_rev_id)
3428
3697
                    result.report(self.outf)
3429
3698
                    return 0
3430
 
            merger.check_basis(not force)
 
3699
            merger.check_basis(False)
3431
3700
            if preview:
3432
 
                return self._do_preview(merger)
 
3701
                return self._do_preview(merger, cleanups)
 
3702
            elif interactive:
 
3703
                return self._do_interactive(merger, cleanups)
3433
3704
            else:
3434
3705
                return self._do_merge(merger, change_reporter, allow_pending,
3435
3706
                                      verified)
3437
3708
            for cleanup in reversed(cleanups):
3438
3709
                cleanup()
3439
3710
 
3440
 
    def _do_preview(self, merger):
3441
 
        from bzrlib.diff import show_diff_trees
 
3711
    def _get_preview(self, merger, cleanups):
3442
3712
        tree_merger = merger.make_merger()
3443
3713
        tt = tree_merger.make_preview_transform()
3444
 
        try:
3445
 
            result_tree = tt.get_preview_tree()
3446
 
            show_diff_trees(merger.this_tree, result_tree, self.outf,
3447
 
                            old_label='', new_label='')
3448
 
        finally:
3449
 
            tt.finalize()
 
3714
        cleanups.append(tt.finalize)
 
3715
        result_tree = tt.get_preview_tree()
 
3716
        return result_tree
 
3717
 
 
3718
    def _do_preview(self, merger, cleanups):
 
3719
        from bzrlib.diff import show_diff_trees
 
3720
        result_tree = self._get_preview(merger, cleanups)
 
3721
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
3722
                        old_label='', new_label='')
3450
3723
 
3451
3724
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3452
3725
        merger.change_reporter = change_reporter
3460
3733
        else:
3461
3734
            return 0
3462
3735
 
 
3736
    def _do_interactive(self, merger, cleanups):
 
3737
        """Perform an interactive merge.
 
3738
 
 
3739
        This works by generating a preview tree of the merge, then using
 
3740
        Shelver to selectively remove the differences between the working tree
 
3741
        and the preview tree.
 
3742
        """
 
3743
        from bzrlib import shelf_ui
 
3744
        result_tree = self._get_preview(merger, cleanups)
 
3745
        writer = bzrlib.option.diff_writer_registry.get()
 
3746
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
3747
                                   reporter=shelf_ui.ApplyReporter(),
 
3748
                                   diff_writer=writer(sys.stdout))
 
3749
        shelver.run()
 
3750
 
3463
3751
    def sanity_check_merger(self, merger):
3464
3752
        if (merger.show_base and
3465
3753
            not merger.merge_type is _mod_merge.Merge3Merger):
3500
3788
            base_branch, base_path = Branch.open_containing(base_loc,
3501
3789
                possible_transports)
3502
3790
        # Find the revision ids
3503
 
        if revision is None or len(revision) < 1 or revision[-1] is None:
 
3791
        other_revision_id = None
 
3792
        base_revision_id = None
 
3793
        if revision is not None:
 
3794
            if len(revision) >= 1:
 
3795
                other_revision_id = revision[-1].as_revision_id(other_branch)
 
3796
            if len(revision) == 2:
 
3797
                base_revision_id = revision[0].as_revision_id(base_branch)
 
3798
        if other_revision_id is None:
3504
3799
            other_revision_id = _mod_revision.ensure_null(
3505
3800
                other_branch.last_revision())
3506
 
        else:
3507
 
            other_revision_id = revision[-1].as_revision_id(other_branch)
3508
 
        if (revision is not None and len(revision) == 2
3509
 
            and revision[0] is not None):
3510
 
            base_revision_id = revision[0].as_revision_id(base_branch)
3511
 
        else:
3512
 
            base_revision_id = None
3513
3801
        # Remember where we merge from
3514
3802
        if ((remember or tree.branch.get_submit_branch() is None) and
3515
3803
             user_location is not None):
3524
3812
            allow_pending = True
3525
3813
        return merger, allow_pending
3526
3814
 
 
3815
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3816
        """Get a merger for uncommitted changes.
 
3817
 
 
3818
        :param tree: The tree the merger should apply to.
 
3819
        :param location: The location containing uncommitted changes.
 
3820
        :param pb: The progress bar to use for showing progress.
 
3821
        :param cleanups: A list of operations to perform to clean up the
 
3822
            temporary directories, unfinalized objects, etc.
 
3823
        """
 
3824
        location = self._select_branch_location(tree, location)[0]
 
3825
        other_tree, other_path = WorkingTree.open_containing(location)
 
3826
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
3827
        if other_path != '':
 
3828
            merger.interesting_files = [other_path]
 
3829
        return merger
 
3830
 
3527
3831
    def _select_branch_location(self, tree, user_location, revision=None,
3528
3832
                                index=None):
3529
3833
        """Select a branch location, according to possible inputs.
3786
4090
 
3787
4091
    OTHER_BRANCH may be local or remote.
3788
4092
 
3789
 
    To filter on a range of revirions, you can use the command -r begin..end
 
4093
    To filter on a range of revisions, you can use the command -r begin..end
3790
4094
    -r revision requests a specific revision, -r ..end or -r begin.. are
3791
4095
    also valid.
3792
4096
 
3832
4136
            type=_parse_revision_str,
3833
4137
            help='Filter on local branch revisions (inclusive). '
3834
4138
                'See "help revisionspec" for details.'),
3835
 
        Option('include-merges', 'Show merged revisions.'),
 
4139
        Option('include-merges',
 
4140
               'Show all revisions in addition to the mainline ones.'),
3836
4141
        ]
3837
4142
    encoding_type = 'replace'
3838
4143
 
4383
4688
    takes_options = [
4384
4689
        Option('inet',
4385
4690
               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),
4386
4695
        Option('port',
4387
4696
               help='Listen for connections on nominated port of the form '
4388
4697
                    '[hostname:]portnumber.  Passing 0 as the port number will '
4389
 
                    'result in a dynamically allocated port.  The default port is '
4390
 
                    '4155.',
 
4698
                    'result in a dynamically allocated port.  The default port '
 
4699
                    'depends on the protocol.',
4391
4700
               type=str),
4392
4701
        Option('directory',
4393
4702
               help='Serve contents of this directory.',
4399
4708
                ),
4400
4709
        ]
4401
4710
 
4402
 
    def run_smart_server(self, smart_server):
4403
 
        """Run 'smart_server' forever, with no UI output at all."""
4404
 
        # For the duration of this server, no UI output is permitted. note
4405
 
        # that this may cause problems with blackbox tests. This should be
4406
 
        # changed with care though, as we dont want to use bandwidth sending
4407
 
        # progress over stderr to smart server clients!
4408
 
        from bzrlib import lockdir
4409
 
        old_factory = ui.ui_factory
4410
 
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
4411
 
        try:
4412
 
            ui.ui_factory = ui.SilentUIFactory()
4413
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 0
4414
 
            smart_server.serve()
4415
 
        finally:
4416
 
            ui.ui_factory = old_factory
4417
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = old_lockdir_timeout
4418
 
 
4419
4711
    def get_host_and_port(self, port):
4420
4712
        """Return the host and port to run the smart server on.
4421
4713
 
4422
 
        If 'port' is None, the default host (`medium.BZR_DEFAULT_INTERFACE`)
4423
 
        and port (`medium.BZR_DEFAULT_PORT`) will be used.
 
4714
        If 'port' is None, None will be returned for the host and port.
4424
4715
 
4425
4716
        If 'port' has a colon in it, the string before the colon will be
4426
4717
        interpreted as the host.
4429
4720
        :return: A tuple of (host, port), where 'host' is a host name or IP,
4430
4721
            and port is an integer TCP/IP port.
4431
4722
        """
4432
 
        from bzrlib.smart import medium
4433
 
        host = medium.BZR_DEFAULT_INTERFACE
4434
 
        if port is None:
4435
 
            port = medium.BZR_DEFAULT_PORT
4436
 
        else:
 
4723
        host = None
 
4724
        if port is not None:
4437
4725
            if ':' in port:
4438
4726
                host, port = port.split(':')
4439
4727
            port = int(port)
4440
4728
        return host, port
4441
4729
 
4442
 
    def get_smart_server(self, transport, inet, port):
4443
 
        """Construct a smart server.
4444
 
 
4445
 
        :param transport: The base transport from which branches will be
4446
 
            served.
4447
 
        :param inet: If True, serve over stdin and stdout. Used for running
4448
 
            from inet.
4449
 
        :param port: The port to listen on. By default, it's `
4450
 
            medium.BZR_DEFAULT_PORT`. See `get_host_and_port` for more
4451
 
            information.
4452
 
        :return: A smart server.
4453
 
        """
4454
 
        from bzrlib.smart import medium, server
4455
 
        if inet:
4456
 
            smart_server = medium.SmartServerPipeStreamMedium(
4457
 
                sys.stdin, sys.stdout, transport)
4458
 
        else:
4459
 
            host, port = self.get_host_and_port(port)
4460
 
            smart_server = server.SmartTCPServer(
4461
 
                transport, host=host, port=port)
4462
 
            note('listening on port: %s' % smart_server.port)
4463
 
        return smart_server
4464
 
 
4465
 
    def run(self, port=None, inet=False, directory=None, allow_writes=False):
4466
 
        from bzrlib.transport import get_transport
4467
 
        from bzrlib.transport.chroot import ChrootServer
 
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
4468
4733
        if directory is None:
4469
4734
            directory = os.getcwd()
 
4735
        if protocol is None:
 
4736
            protocol = transport_server_registry.get()
 
4737
        host, port = self.get_host_and_port(port)
4470
4738
        url = urlutils.local_path_to_url(directory)
4471
4739
        if not allow_writes:
4472
4740
            url = 'readonly+' + url
4473
 
        chroot_server = ChrootServer(get_transport(url))
4474
 
        chroot_server.setUp()
4475
 
        t = get_transport(chroot_server.get_url())
4476
 
        smart_server = self.get_smart_server(t, inet, port)
4477
 
        self.run_smart_server(smart_server)
 
4741
        transport = get_transport(url)
 
4742
        protocol(transport, host, port, inet)
4478
4743
 
4479
4744
 
4480
4745
class cmd_join(Command):
4481
 
    """Combine a subtree into its containing tree.
 
4746
    """Combine a tree into its containing tree.
4482
4747
 
4483
 
    This command is for experimental use only.  It requires the target tree
4484
 
    to be in dirstate-with-subtree format, which cannot be converted into
4485
 
    earlier formats.
 
4748
    This command requires the target tree to be in a rich-root format.
4486
4749
 
4487
4750
    The TREE argument should be an independent tree, inside another tree, but
4488
4751
    not part of it.  (Such trees can be produced by "bzr split", but also by
4491
4754
    The result is a combined tree, with the subtree no longer an independant
4492
4755
    part.  This is marked as a merge of the subtree into the containing tree,
4493
4756
    and all history is preserved.
4494
 
 
4495
 
    If --reference is specified, the subtree retains its independence.  It can
4496
 
    be branched by itself, and can be part of multiple projects at the same
4497
 
    time.  But operations performed in the containing tree, such as commit
4498
 
    and merge, will recurse into the subtree.
4499
4757
    """
4500
4758
 
4501
4759
    _see_also = ['split']
4502
4760
    takes_args = ['tree']
4503
4761
    takes_options = [
4504
 
            Option('reference', help='Join by reference.'),
 
4762
            Option('reference', help='Join by reference.', hidden=True),
4505
4763
            ]
4506
 
    hidden = True
4507
4764
 
4508
4765
    def run(self, tree, reference=False):
4509
4766
        sub_tree = WorkingTree.open(tree)
4543
4800
    branch.  Commits in the top-level tree will not apply to the new subtree.
4544
4801
    """
4545
4802
 
4546
 
    # join is not un-hidden yet
4547
 
    #_see_also = ['join']
 
4803
    _see_also = ['join']
4548
4804
    takes_args = ['tree']
4549
4805
 
4550
4806
    def run(self, tree):
4555
4811
        try:
4556
4812
            containing_tree.extract(sub_id)
4557
4813
        except errors.RootNotRich:
4558
 
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
4814
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
4559
4815
 
4560
4816
 
4561
4817
class cmd_merge_directive(Command):
4704
4960
    default.  "0.9" uses revision bundle format 0.9 and merge directive
4705
4961
    format 1.  It is compatible with Bazaar 0.12 - 0.18.
4706
4962
 
4707
 
    Merge directives are applied using the merge command or the pull command.
 
4963
    The merge directives created by bzr send may be applied using bzr merge or
 
4964
    bzr pull by specifying a file containing a merge directive as the location.
4708
4965
    """
4709
4966
 
4710
4967
    encoding_type = 'exact'
4729
4986
               help='Write merge directive to this file; '
4730
4987
                    'use - for stdout.',
4731
4988
               type=unicode),
 
4989
        Option('strict',
 
4990
               help='Refuse to send if there are uncommitted changes in'
 
4991
               ' the working tree, --no-strict disables the check.'),
4732
4992
        Option('mail-to', help='Mail the request to this address.',
4733
4993
               type=unicode),
4734
4994
        'revision',
4735
4995
        'message',
4736
 
        RegistryOption.from_kwargs('format',
4737
 
        'Use the specified output format.',
4738
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
4739
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
4996
        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')),
4740
5000
        ]
4741
5001
 
4742
5002
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4743
5003
            no_patch=False, revision=None, remember=False, output=None,
4744
 
            format='4', mail_to=None, message=None, **kwargs):
4745
 
        return self._run(submit_branch, revision, public_branch, remember,
4746
 
                         format, no_bundle, no_patch, output,
4747
 
                         kwargs.get('from', '.'), mail_to, message)
4748
 
 
4749
 
    def _run(self, submit_branch, revision, public_branch, remember, format,
4750
 
             no_bundle, no_patch, output, from_, mail_to, message):
4751
 
        from bzrlib.revision import NULL_REVISION
4752
 
        branch = Branch.open_containing(from_)[0]
4753
 
        if output is None:
4754
 
            outfile = cStringIO.StringIO()
4755
 
        elif output == '-':
4756
 
            outfile = self.outf
4757
 
        else:
4758
 
            outfile = open(output, 'wb')
4759
 
        # we may need to write data into branch's repository to calculate
4760
 
        # the data to send.
4761
 
        branch.lock_write()
4762
 
        try:
4763
 
            if output is None:
4764
 
                config = branch.get_config()
4765
 
                if mail_to is None:
4766
 
                    mail_to = config.get_user_option('submit_to')
4767
 
                mail_client = config.get_mail_client()
4768
 
            if remember and submit_branch is None:
4769
 
                raise errors.BzrCommandError(
4770
 
                    '--remember requires a branch to be specified.')
4771
 
            stored_submit_branch = branch.get_submit_branch()
4772
 
            remembered_submit_branch = None
4773
 
            if submit_branch is None:
4774
 
                submit_branch = stored_submit_branch
4775
 
                remembered_submit_branch = "submit"
4776
 
            else:
4777
 
                if stored_submit_branch is None or remember:
4778
 
                    branch.set_submit_branch(submit_branch)
4779
 
            if submit_branch is None:
4780
 
                submit_branch = branch.get_parent()
4781
 
                remembered_submit_branch = "parent"
4782
 
            if submit_branch is None:
4783
 
                raise errors.BzrCommandError('No submit branch known or'
4784
 
                                             ' specified')
4785
 
            if remembered_submit_branch is not None:
4786
 
                note('Using saved %s location "%s" to determine what '
4787
 
                        'changes to submit.', remembered_submit_branch,
4788
 
                        submit_branch)
4789
 
 
4790
 
            if mail_to is None:
4791
 
                submit_config = Branch.open(submit_branch).get_config()
4792
 
                mail_to = submit_config.get_user_option("child_submit_to")
4793
 
 
4794
 
            stored_public_branch = branch.get_public_branch()
4795
 
            if public_branch is None:
4796
 
                public_branch = stored_public_branch
4797
 
            elif stored_public_branch is None or remember:
4798
 
                branch.set_public_branch(public_branch)
4799
 
            if no_bundle and public_branch is None:
4800
 
                raise errors.BzrCommandError('No public branch specified or'
4801
 
                                             ' known')
4802
 
            base_revision_id = None
4803
 
            revision_id = None
4804
 
            if revision is not None:
4805
 
                if len(revision) > 2:
4806
 
                    raise errors.BzrCommandError('bzr send takes '
4807
 
                        'at most two one revision identifiers')
4808
 
                revision_id = revision[-1].as_revision_id(branch)
4809
 
                if len(revision) == 2:
4810
 
                    base_revision_id = revision[0].as_revision_id(branch)
4811
 
            if revision_id is None:
4812
 
                revision_id = branch.last_revision()
4813
 
            if revision_id == NULL_REVISION:
4814
 
                raise errors.BzrCommandError('No revisions to submit.')
4815
 
            if format == '4':
4816
 
                directive = merge_directive.MergeDirective2.from_objects(
4817
 
                    branch.repository, revision_id, time.time(),
4818
 
                    osutils.local_time_offset(), submit_branch,
4819
 
                    public_branch=public_branch, include_patch=not no_patch,
4820
 
                    include_bundle=not no_bundle, message=message,
4821
 
                    base_revision_id=base_revision_id)
4822
 
            elif format == '0.9':
4823
 
                if not no_bundle:
4824
 
                    if not no_patch:
4825
 
                        patch_type = 'bundle'
4826
 
                    else:
4827
 
                        raise errors.BzrCommandError('Format 0.9 does not'
4828
 
                            ' permit bundle with no patch')
4829
 
                else:
4830
 
                    if not no_patch:
4831
 
                        patch_type = 'diff'
4832
 
                    else:
4833
 
                        patch_type = None
4834
 
                directive = merge_directive.MergeDirective.from_objects(
4835
 
                    branch.repository, revision_id, time.time(),
4836
 
                    osutils.local_time_offset(), submit_branch,
4837
 
                    public_branch=public_branch, patch_type=patch_type,
4838
 
                    message=message)
4839
 
 
4840
 
            outfile.writelines(directive.to_lines())
4841
 
            if output is None:
4842
 
                subject = '[MERGE] '
4843
 
                if message is not None:
4844
 
                    subject += message
4845
 
                else:
4846
 
                    revision = branch.repository.get_revision(revision_id)
4847
 
                    subject += revision.get_summary()
4848
 
                basename = directive.get_disk_name(branch)
4849
 
                mail_client.compose_merge_request(mail_to, subject,
4850
 
                                                  outfile.getvalue(), basename)
4851
 
        finally:
4852
 
            if output != '-':
4853
 
                outfile.close()
4854
 
            branch.unlock()
 
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)
4855
5012
 
4856
5013
 
4857
5014
class cmd_bundle_revisions(cmd_send):
4858
 
 
4859
5015
    """Create a merge-directive for submitting changes.
4860
5016
 
4861
5017
    A merge directive provides many things needed for requesting merges:
4902
5058
               type=unicode),
4903
5059
        Option('output', short_name='o', help='Write directive to this file.',
4904
5060
               type=unicode),
 
5061
        Option('strict',
 
5062
               help='Refuse to bundle revisions if there are uncommitted'
 
5063
               ' changes in the working tree, --no-strict disables the check.'),
4905
5064
        'revision',
4906
 
        RegistryOption.from_kwargs('format',
4907
 
        'Use the specified output format.',
4908
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
4909
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
5065
        RegistryOption('format',
 
5066
                       help='Use the specified output format.',
 
5067
                       lazy_registry=('bzrlib.send', 'format_registry')),
4910
5068
        ]
4911
5069
    aliases = ['bundle']
4912
5070
 
4916
5074
 
4917
5075
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4918
5076
            no_patch=False, revision=None, remember=False, output=None,
4919
 
            format='4', **kwargs):
 
5077
            format=None, strict=None, **kwargs):
4920
5078
        if output is None:
4921
5079
            output = '-'
4922
 
        return self._run(submit_branch, revision, public_branch, remember,
 
5080
        from bzrlib.send import send
 
5081
        return send(submit_branch, revision, public_branch, remember,
4923
5082
                         format, no_bundle, no_patch, output,
4924
 
                         kwargs.get('from', '.'), None, None)
 
5083
                         kwargs.get('from', '.'), None, None, None,
 
5084
                         self.outf, strict=strict)
4925
5085
 
4926
5086
 
4927
5087
class cmd_tag(Command):
5022
5182
        if not tags:
5023
5183
            return
5024
5184
 
5025
 
        if revision:
5026
 
            branch.lock_read()
5027
 
            try:
 
5185
        branch.lock_read()
 
5186
        try:
 
5187
            if revision:
5028
5188
                graph = branch.repository.get_graph()
5029
5189
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5030
5190
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5031
5191
                # only show revisions between revid1 and revid2 (inclusive)
5032
5192
                tags = [(tag, revid) for tag, revid in tags if
5033
5193
                    graph.is_between(revid, revid1, revid2)]
5034
 
            finally:
5035
 
                branch.unlock()
5036
 
        if sort == 'alpha':
5037
 
            tags.sort()
5038
 
        elif sort == 'time':
5039
 
            timestamps = {}
5040
 
            for tag, revid in tags:
5041
 
                try:
5042
 
                    revobj = branch.repository.get_revision(revid)
5043
 
                except errors.NoSuchRevision:
5044
 
                    timestamp = sys.maxint # place them at the end
5045
 
                else:
5046
 
                    timestamp = revobj.timestamp
5047
 
                timestamps[revid] = timestamp
5048
 
            tags.sort(key=lambda x: timestamps[x[1]])
5049
 
        if not show_ids:
5050
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5051
 
            revno_map = branch.get_revision_id_to_revno_map()
5052
 
            tags = [ (tag, '.'.join(map(str, revno_map.get(revid, ('?',)))))
5053
 
                        for tag, revid in tags ]
 
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()
5054
5221
        for tag, revspec in tags:
5055
5222
            self.outf.write('%-20s %s\n' % (tag, revspec))
5056
5223
 
5092
5259
            ),
5093
5260
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5094
5261
        Option('force',
5095
 
               help='Perform reconfiguration even if local changes'
5096
 
               ' will be lost.')
 
5262
            help='Perform reconfiguration even if local changes'
 
5263
            ' will be lost.'),
 
5264
        Option('stacked-on',
 
5265
            help='Reconfigure a branch to be stacked on another branch.',
 
5266
            type=unicode,
 
5267
            ),
 
5268
        Option('unstacked',
 
5269
            help='Reconfigure a branch to be unstacked.  This '
 
5270
                'may require copying substantial data into it.',
 
5271
            ),
5097
5272
        ]
5098
5273
 
5099
 
    def run(self, location=None, target_type=None, bind_to=None, force=False):
 
5274
    def run(self, location=None, target_type=None, bind_to=None, force=False,
 
5275
            stacked_on=None,
 
5276
            unstacked=None):
5100
5277
        directory = bzrdir.BzrDir.open(location)
 
5278
        if stacked_on and unstacked:
 
5279
            raise BzrCommandError("Can't use both --stacked-on and --unstacked")
 
5280
        elif stacked_on is not None:
 
5281
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
5282
        elif unstacked:
 
5283
            reconfigure.ReconfigureUnstacked().apply(directory)
 
5284
        # At the moment you can use --stacked-on and a different
 
5285
        # reconfiguration shape at the same time; there seems no good reason
 
5286
        # to ban it.
5101
5287
        if target_type is None:
5102
 
            raise errors.BzrCommandError('No target configuration specified')
 
5288
            if stacked_on or unstacked:
 
5289
                return
 
5290
            else:
 
5291
                raise errors.BzrCommandError('No target configuration '
 
5292
                    'specified')
5103
5293
        elif target_type == 'branch':
5104
5294
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5105
5295
        elif target_type == 'tree':
5148
5338
 
5149
5339
    takes_args = ['to_location']
5150
5340
    takes_options = [Option('force',
5151
 
                        help='Switch even if local commits will be lost.')
 
5341
                        help='Switch even if local commits will be lost.'),
 
5342
                     Option('create-branch', short_name='b',
 
5343
                        help='Create the target branch from this one before'
 
5344
                             ' switching to it.'),
5152
5345
                     ]
5153
5346
 
5154
 
    def run(self, to_location, force=False):
 
5347
    def run(self, to_location, force=False, create_branch=False):
5155
5348
        from bzrlib import switch
5156
5349
        tree_location = '.'
5157
5350
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5158
 
        branch = control_dir.open_branch()
5159
5351
        try:
5160
 
            to_branch = Branch.open(to_location)
 
5352
            branch = control_dir.open_branch()
 
5353
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5161
5354
        except errors.NotBranchError:
5162
 
            this_branch = control_dir.open_branch()
5163
 
            # This may be a heavy checkout, where we want the master branch
5164
 
            this_url = this_branch.get_bound_location()
5165
 
            # If not, use a local sibling
5166
 
            if this_url is None:
5167
 
                this_url = this_branch.base
5168
 
            to_branch = Branch.open(
5169
 
                urlutils.join(this_url, '..', to_location))
 
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))
5170
5382
        switch.switch(control_dir, to_branch, force)
5171
 
        if branch.get_config().has_explicit_nickname():
 
5383
        if had_explicit_nick:
5172
5384
            branch = control_dir.open_branch() #get the new branch!
5173
5385
            branch.nick = to_branch.nick
5174
5386
        note('Switched to branch: %s',
5175
5387
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
5176
5388
 
 
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
 
5177
5406
 
5178
5407
class cmd_view(Command):
5179
5408
    """Manage filtered views.
5330
5559
 
5331
5560
 
5332
5561
class cmd_hooks(Command):
5333
 
    """Show a branch's currently registered hooks.
5334
 
    """
 
5562
    """Show hooks."""
5335
5563
 
5336
5564
    hidden = True
5337
 
    takes_args = ['path?']
5338
5565
 
5339
 
    def run(self, path=None):
5340
 
        if path is None:
5341
 
            path = '.'
5342
 
        branch_hooks = Branch.open(path).hooks
5343
 
        for hook_type in branch_hooks:
5344
 
            hooks = branch_hooks[hook_type]
5345
 
            self.outf.write("%s:\n" % (hook_type,))
5346
 
            if hooks:
5347
 
                for hook in hooks:
5348
 
                    self.outf.write("  %s\n" %
5349
 
                                    (branch_hooks.get_hook_name(hook),))
5350
 
            else:
5351
 
                self.outf.write("  <no hooks installed>\n")
 
5566
    def run(self):
 
5567
        for hook_key in sorted(hooks.known_hooks.keys()):
 
5568
            some_hooks = hooks.known_hooks_key_to_object(hook_key)
 
5569
            self.outf.write("%s:\n" % type(some_hooks).__name__)
 
5570
            for hook_name, hook_point in sorted(some_hooks.items()):
 
5571
                self.outf.write("  %s:\n" % (hook_name,))
 
5572
                found_hooks = list(hook_point)
 
5573
                if found_hooks:
 
5574
                    for hook in found_hooks:
 
5575
                        self.outf.write("    %s\n" %
 
5576
                                        (some_hooks.get_hook_name(hook),))
 
5577
                else:
 
5578
                    self.outf.write("    <no hooks installed>\n")
5352
5579
 
5353
5580
 
5354
5581
class cmd_shelve(Command):
5387
5614
                       value_switches=True, enum_switch=False),
5388
5615
 
5389
5616
        Option('list', help='List shelved changes.'),
 
5617
        Option('destroy',
 
5618
               help='Destroy removed changes instead of shelving them.'),
5390
5619
    ]
5391
5620
    _see_also = ['unshelve']
5392
5621
 
5393
5622
    def run(self, revision=None, all=False, file_list=None, message=None,
5394
 
            writer=None, list=False):
 
5623
            writer=None, list=False, destroy=False):
5395
5624
        if list:
5396
5625
            return self.run_for_list()
5397
5626
        from bzrlib.shelf_ui import Shelver
5399
5628
            writer = bzrlib.option.diff_writer_registry.get()
5400
5629
        try:
5401
5630
            Shelver.from_args(writer(sys.stdout), revision, all, file_list,
5402
 
                              message).run()
 
5631
                              message, destroy=destroy).run()
5403
5632
        except errors.UserAbort:
5404
5633
            return 0
5405
5634
 
5447
5676
        Unshelver.from_args(shelf_id, action).run()
5448
5677
 
5449
5678
 
5450
 
def _create_prefix(cur_transport):
5451
 
    needed = [cur_transport]
5452
 
    # Recurse upwards until we can create a directory successfully
5453
 
    while True:
5454
 
        new_transport = cur_transport.clone('..')
5455
 
        if new_transport.base == cur_transport.base:
5456
 
            raise errors.BzrCommandError(
5457
 
                "Failed to create path prefix for %s."
5458
 
                % cur_transport.base)
5459
 
        try:
5460
 
            new_transport.mkdir('.')
5461
 
        except errors.NoSuchFile:
5462
 
            needed.append(new_transport)
5463
 
            cur_transport = new_transport
 
5679
class cmd_clean_tree(Command):
 
5680
    """Remove unwanted files from working tree.
 
5681
 
 
5682
    By default, only unknown files, not ignored files, are deleted.  Versioned
 
5683
    files are never deleted.
 
5684
 
 
5685
    Another class is 'detritus', which includes files emitted by bzr during
 
5686
    normal operations and selftests.  (The value of these files decreases with
 
5687
    time.)
 
5688
 
 
5689
    If no options are specified, unknown files are deleted.  Otherwise, option
 
5690
    flags are respected, and may be combined.
 
5691
 
 
5692
    To check what clean-tree will do, use --dry-run.
 
5693
    """
 
5694
    takes_options = [Option('ignored', help='Delete all ignored files.'),
 
5695
                     Option('detritus', help='Delete conflict files, merge'
 
5696
                            ' backups, and failed selftest dirs.'),
 
5697
                     Option('unknown',
 
5698
                            help='Delete files unknown to bzr (default).'),
 
5699
                     Option('dry-run', help='Show files to delete instead of'
 
5700
                            ' deleting them.'),
 
5701
                     Option('force', help='Do not prompt before deleting.')]
 
5702
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
 
5703
            force=False):
 
5704
        from bzrlib.clean_tree import clean_tree
 
5705
        if not (unknown or ignored or detritus):
 
5706
            unknown = True
 
5707
        if dry_run:
 
5708
            force = True
 
5709
        clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
 
5710
                   dry_run=dry_run, no_prompt=force)
 
5711
 
 
5712
 
 
5713
class cmd_reference(Command):
 
5714
    """list, view and set branch locations for nested trees.
 
5715
 
 
5716
    If no arguments are provided, lists the branch locations for nested trees.
 
5717
    If one argument is provided, display the branch location for that tree.
 
5718
    If two arguments are provided, set the branch location for that tree.
 
5719
    """
 
5720
 
 
5721
    hidden = True
 
5722
 
 
5723
    takes_args = ['path?', 'location?']
 
5724
 
 
5725
    def run(self, path=None, location=None):
 
5726
        branchdir = '.'
 
5727
        if path is not None:
 
5728
            branchdir = path
 
5729
        tree, branch, relpath =(
 
5730
            bzrdir.BzrDir.open_containing_tree_or_branch(branchdir))
 
5731
        if path is not None:
 
5732
            path = relpath
 
5733
        if tree is None:
 
5734
            tree = branch.basis_tree()
 
5735
        if path is None:
 
5736
            info = branch._get_all_reference_info().iteritems()
 
5737
            self._display_reference_info(tree, branch, info)
5464
5738
        else:
5465
 
            break
5466
 
    # Now we only need to create child directories
5467
 
    while needed:
5468
 
        cur_transport = needed.pop()
5469
 
        cur_transport.ensure_base()
 
5739
            file_id = tree.path2id(path)
 
5740
            if file_id is None:
 
5741
                raise errors.NotVersionedError(path)
 
5742
            if location is None:
 
5743
                info = [(file_id, branch.get_reference_info(file_id))]
 
5744
                self._display_reference_info(tree, branch, info)
 
5745
            else:
 
5746
                branch.set_reference_info(file_id, path, location)
 
5747
 
 
5748
    def _display_reference_info(self, tree, branch, info):
 
5749
        ref_list = []
 
5750
        for file_id, (path, location) in info:
 
5751
            try:
 
5752
                path = tree.id2path(file_id)
 
5753
            except errors.NoSuchId:
 
5754
                pass
 
5755
            ref_list.append((path, location))
 
5756
        for path, location in sorted(ref_list):
 
5757
            self.outf.write('%s %s\n' % (path, location))
5470
5758
 
5471
5759
 
5472
5760
# these get imported and then picked up by the scan for cmd_*
5479
5767
from bzrlib.bundle.commands import (
5480
5768
    cmd_bundle_info,
5481
5769
    )
 
5770
from bzrlib.foreign import cmd_dpush
5482
5771
from bzrlib.sign_my_commits import cmd_sign_my_commits
5483
5772
from bzrlib.weave_commands import cmd_versionedfile_list, \
5484
5773
        cmd_weave_plan_merge, cmd_weave_merge_text