/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2009-06-10 02:51:23 UTC
  • mfrom: (4423 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4464.
  • Revision ID: mbp@sourcefrog.net-20090610025123-2u0c0ng5jcezjzqh
merge trunk

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
 
41
41
    merge_directive,
42
42
    osutils,
43
43
    reconfigure,
 
44
    rename_map,
44
45
    revision as _mod_revision,
45
46
    symbol_versioning,
46
47
    transport,
78
79
 
79
80
 
80
81
def tree_files_for_add(file_list):
81
 
    """Add handles files a bit differently so it a custom implementation."""
 
82
    """
 
83
    Return a tree and list of absolute paths from a file list.
 
84
 
 
85
    Similar to tree_files, but add handles files a bit differently, so it a
 
86
    custom implementation.  In particular, MutableTreeTree.smart_add expects
 
87
    absolute paths, which it immediately converts to relative paths.
 
88
    """
 
89
    # FIXME Would be nice to just return the relative paths like
 
90
    # internal_tree_files does, but there are a large number of unit tests
 
91
    # that assume the current interface to mutabletree.smart_add
82
92
    if file_list:
83
 
        tree = WorkingTree.open_containing(file_list[0])[0]
 
93
        tree, relpath = WorkingTree.open_containing(file_list[0])
84
94
        if tree.supports_views():
85
95
            view_files = tree.views.lookup_view()
86
 
            for filename in file_list:
87
 
                if not osutils.is_inside_any(view_files, filename):
88
 
                    raise errors.FileOutsideView(filename, view_files)
 
96
            if view_files:
 
97
                for filename in file_list:
 
98
                    if not osutils.is_inside_any(view_files, filename):
 
99
                        raise errors.FileOutsideView(filename, view_files)
 
100
        file_list = file_list[:]
 
101
        file_list[0] = tree.abspath(relpath)
89
102
    else:
90
103
        tree = WorkingTree.open_containing(u'.')[0]
91
104
        if tree.supports_views():
93
106
            if view_files:
94
107
                file_list = view_files
95
108
                view_str = views.view_display_str(view_files)
96
 
                note("ignoring files outside view: %s" % view_str)
 
109
                note("Ignoring files outside view. View is %s" % view_str)
97
110
    return tree, file_list
98
111
 
99
112
 
149
162
            if view_files:
150
163
                file_list = view_files
151
164
                view_str = views.view_display_str(view_files)
152
 
                note("ignoring files outside view: %s" % view_str)
 
165
                note("Ignoring files outside view. View is %s" % view_str)
153
166
        return tree, file_list
154
167
    tree = WorkingTree.open_containing(osutils.realpath(file_list[0]))[0]
155
168
    return tree, safe_relpath_files(tree, file_list, canonicalize,
597
610
                for glob, paths in ignored.items():
598
611
                    match_len += len(paths)
599
612
                self.outf.write("ignored %d file(s).\n" % match_len)
600
 
            self.outf.write("If you wish to add some of these files,"
601
 
                            " please add them by name.\n")
 
613
            self.outf.write("If you wish to add ignored files, "
 
614
                            "please add them explicitly by name. "
 
615
                            "(\"bzr ignored\" gives a list)\n")
602
616
 
603
617
 
604
618
class cmd_mkdir(Command):
720
734
    takes_args = ['names*']
721
735
    takes_options = [Option("after", help="Move only the bzr identifier"
722
736
        " of the file, because the file has already been moved."),
 
737
        Option('auto', help='Automatically guess renames.'),
 
738
        Option('dry-run', help='Avoid making changes when guessing renames.'),
723
739
        ]
724
740
    aliases = ['move', 'rename']
725
741
    encoding_type = 'replace'
726
742
 
727
 
    def run(self, names_list, after=False):
 
743
    def run(self, names_list, after=False, auto=False, dry_run=False):
 
744
        if auto:
 
745
            return self.run_auto(names_list, after, dry_run)
 
746
        elif dry_run:
 
747
            raise errors.BzrCommandError('--dry-run requires --auto.')
728
748
        if names_list is None:
729
749
            names_list = []
730
 
 
731
750
        if len(names_list) < 2:
732
751
            raise errors.BzrCommandError("missing file argument")
733
752
        tree, rel_names = tree_files(names_list, canonicalize=False)
737
756
        finally:
738
757
            tree.unlock()
739
758
 
 
759
    def run_auto(self, names_list, after, dry_run):
 
760
        if names_list is not None and len(names_list) > 1:
 
761
            raise errors.BzrCommandError('Only one path may be specified to'
 
762
                                         ' --auto.')
 
763
        if after:
 
764
            raise errors.BzrCommandError('--after cannot be specified with'
 
765
                                         ' --auto.')
 
766
        work_tree, file_list = tree_files(names_list, default_branch='.')
 
767
        work_tree.lock_write()
 
768
        try:
 
769
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
770
        finally:
 
771
            work_tree.unlock()
 
772
 
740
773
    def _run(self, tree, names_list, rel_names, after):
741
774
        into_existing = osutils.isdir(names_list[-1])
742
775
        if into_existing and len(names_list) == 2:
850
883
            short_name='d',
851
884
            type=unicode,
852
885
            ),
 
886
        Option('local',
 
887
            help="Perform a local pull in a bound "
 
888
                 "branch.  Local pulls are not applied to "
 
889
                 "the master branch."
 
890
            ),
853
891
        ]
854
892
    takes_args = ['location?']
855
893
    encoding_type = 'replace'
856
894
 
857
895
    def run(self, location=None, remember=False, overwrite=False,
858
896
            revision=None, verbose=False,
859
 
            directory=None):
 
897
            directory=None, local=False):
860
898
        # FIXME: too much stuff is in the command class
861
899
        revision_id = None
862
900
        mergeable = None
868
906
        except errors.NoWorkingTree:
869
907
            tree_to = None
870
908
            branch_to = Branch.open_containing(directory)[0]
 
909
        
 
910
        if local and not branch_to.get_bound_location():
 
911
            raise errors.LocalRequiresBoundBranch()
871
912
 
872
913
        possible_transports = []
873
914
        if location is not None:
916
957
                    unversioned_filter=tree_to.is_ignored, view_info=view_info)
917
958
                result = tree_to.pull(branch_from, overwrite, revision_id,
918
959
                                      change_reporter,
919
 
                                      possible_transports=possible_transports)
 
960
                                      possible_transports=possible_transports,
 
961
                                      local=local)
920
962
            else:
921
 
                result = branch_to.pull(branch_from, overwrite, revision_id)
 
963
                result = branch_to.pull(branch_from, overwrite, revision_id,
 
964
                                      local=local)
922
965
 
923
966
            result.report(self.outf)
924
967
            if verbose and result.old_revid != result.new_revid:
996
1039
        if revision is not None:
997
1040
            revision_id = revision.in_history(br_from).rev_id
998
1041
        else:
999
 
            revision_id = br_from.last_revision()
 
1042
            revision_id = None
1000
1043
 
1001
1044
        # Get the stacked_on branch, if any
1002
1045
        if stacked_on is not None:
1299
1342
      basic statistics (like the number of files in the working tree and
1300
1343
      number of revisions in the branch and repository):
1301
1344
 
1302
 
        bzr -v info
 
1345
        bzr info -v
1303
1346
 
1304
1347
      Display the above together with number of committers to the branch:
1305
1348
 
1306
 
        bzr -vv info
 
1349
        bzr info -vv
1307
1350
    """
1308
1351
    _see_also = ['revno', 'working-trees', 'repositories']
1309
1352
    takes_args = ['location?']
1554
1597
                    "\nYou may supply --create-prefix to create all"
1555
1598
                    " leading parent directories."
1556
1599
                    % location)
1557
 
            _create_prefix(to_transport)
 
1600
            to_transport.create_prefix()
1558
1601
 
1559
1602
        try:
1560
1603
            a_bzrdir = bzrdir.BzrDir.open_from_transport(to_transport)
1931
1974
        --show-ids  display revision-ids (and file-ids), not just revnos
1932
1975
 
1933
1976
      Note that the default number of levels to display is a function of the
1934
 
      log format. If the -n option is not used, ``short`` and ``line`` show
1935
 
      just the top level (mainline) while ``long`` shows all levels of merged
1936
 
      revisions.
 
1977
      log format. If the -n option is not used, the standard log formats show
 
1978
      just the top level (mainline).
1937
1979
 
1938
1980
      Status summaries are shown using status flags like A, M, etc. To see
1939
1981
      the changes explained using words like ``added`` and ``modified``
1975
2017
 
1976
2018
    :Path filtering:
1977
2019
 
1978
 
      If a parameter is given and it's not a branch, the log will be filtered
1979
 
      to show only those revisions that changed the nominated file or
1980
 
      directory.
 
2020
      If parameters are given and the first one is not a branch, the log
 
2021
      will be filtered to show only those revisions that changed the
 
2022
      nominated files or directories.
1981
2023
 
1982
2024
      Filenames are interpreted within their historical context. To log a
1983
2025
      deleted file, specify a revision range so that the file existed at
2006
2048
      explicitly ask for this (and no way to stop logging a file back
2007
2049
      until it was last renamed).
2008
2050
 
2009
 
      Note: If the path is a directory, only revisions that directly changed
2010
 
      that directory object are currently shown. This is considered a bug.
2011
 
      (Support for filtering against multiple files and for files within a
2012
 
      directory is under development.)
2013
 
 
2014
2051
    :Other filtering:
2015
2052
 
2016
2053
      The --message option can be used for finding revisions that match a
2030
2067
      You may find it useful to add the aliases below to ``bazaar.conf``::
2031
2068
 
2032
2069
        [ALIASES]
2033
 
        tip = log -r-1 -n1
2034
 
        top = log -r-10.. --short --forward
2035
 
        show = log -v -p -n1 --long
 
2070
        tip = log -r-1
 
2071
        top = log -l10 --line
 
2072
        show = log -v -p
2036
2073
 
2037
2074
      ``bzr tip`` will then show the latest revision while ``bzr top``
2038
2075
      will show the last 10 mainline revisions. To see the details of a
2039
2076
      particular revision X,  ``bzr show -rX``.
2040
2077
 
2041
 
      As many GUI tools and Web interfaces do, you may prefer viewing
2042
 
      history collapsed initially. If you are interested in looking deeper
2043
 
      into a particular merge X, use ``bzr log -n0 -rX``. If you like
2044
 
      working this way, you may wish to either:
2045
 
 
2046
 
      * change your default log format to short (or line)
2047
 
      * add this alias: log = log -n1
 
2078
      If you are interested in looking deeper into a particular merge X,
 
2079
      use ``bzr log -n0 -rX``.
2048
2080
 
2049
2081
      ``bzr log -v`` on a branch with lots of history is currently
2050
2082
      very slow. A fix for this issue is currently under development.
2058
2090
 
2059
2091
      When exploring non-mainline history on large projects with deep
2060
2092
      history, the performance of log can be greatly improved by installing
2061
 
      the revnocache plugin. This plugin buffers historical information
 
2093
      the historycache plugin. This plugin buffers historical information
2062
2094
      trading disk space for faster speed.
2063
2095
    """
2064
 
    takes_args = ['location?']
 
2096
    takes_args = ['file*']
2065
2097
    _see_also = ['log-formats', 'revisionspec']
2066
2098
    takes_options = [
2067
2099
            Option('forward',
2095
2127
            Option('show-diff',
2096
2128
                   short_name='p',
2097
2129
                   help='Show changes made in each revision as a patch.'),
 
2130
            Option('include-merges',
 
2131
                   help='Show merged revisions like --levels 0 does.'),
2098
2132
            ]
2099
2133
    encoding_type = 'replace'
2100
2134
 
2101
2135
    @display_command
2102
 
    def run(self, location=None, timezone='original',
 
2136
    def run(self, file_list=None, timezone='original',
2103
2137
            verbose=False,
2104
2138
            show_ids=False,
2105
2139
            forward=False,
2109
2143
            levels=None,
2110
2144
            message=None,
2111
2145
            limit=None,
2112
 
            show_diff=False):
2113
 
        from bzrlib.log import show_log, _get_fileid_to_log
 
2146
            show_diff=False,
 
2147
            include_merges=False):
 
2148
        from bzrlib.log import (
 
2149
            Logger,
 
2150
            make_log_request_dict,
 
2151
            _get_info_for_log_files,
 
2152
            )
2114
2153
        direction = (forward and 'forward') or 'reverse'
 
2154
        if include_merges:
 
2155
            if levels is None:
 
2156
                levels = 0
 
2157
            else:
 
2158
                raise errors.BzrCommandError(
 
2159
                    '--levels and --include-merges are mutually exclusive')
2115
2160
 
2116
2161
        if change is not None:
2117
2162
            if len(change) > 1:
2122
2167
            else:
2123
2168
                revision = change
2124
2169
 
2125
 
        # log everything
2126
 
        file_id = None
2127
 
        if location:
2128
 
            # find the file id to log:
2129
 
 
2130
 
            tree, b, fp = bzrdir.BzrDir.open_containing_tree_or_branch(
2131
 
                location)
2132
 
            if fp != '':
2133
 
                file_id = _get_fileid_to_log(revision, tree, b, fp)
 
2170
        file_ids = []
 
2171
        filter_by_dir = False
 
2172
        if file_list:
 
2173
            # find the file ids to log and check for directory filtering
 
2174
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
 
2175
                file_list)
 
2176
            for relpath, file_id, kind in file_info_list:
2134
2177
                if file_id is None:
2135
2178
                    raise errors.BzrCommandError(
2136
2179
                        "Path unknown at end or start of revision range: %s" %
2137
 
                        location)
 
2180
                        relpath)
 
2181
                # If the relpath is the top of the tree, we log everything
 
2182
                if relpath == '':
 
2183
                    file_ids = []
 
2184
                    break
 
2185
                else:
 
2186
                    file_ids.append(file_id)
 
2187
                filter_by_dir = filter_by_dir or (
 
2188
                    kind in ['directory', 'tree-reference'])
2138
2189
        else:
2139
 
            # local dir only
 
2190
            # log everything
2140
2191
            # FIXME ? log the current subdir only RBC 20060203
2141
2192
            if revision is not None \
2142
2193
                    and len(revision) > 0 and revision[0].get_branch():
2145
2196
                location = '.'
2146
2197
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2147
2198
            b = dir.open_branch()
2148
 
 
2149
 
        b.lock_read()
2150
 
        try:
2151
2199
            rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2200
 
 
2201
        # Decide on the type of delta & diff filtering to use
 
2202
        # TODO: add an --all-files option to make this configurable & consistent
 
2203
        if not verbose:
 
2204
            delta_type = None
 
2205
        else:
 
2206
            delta_type = 'full'
 
2207
        if not show_diff:
 
2208
            diff_type = None
 
2209
        elif file_ids:
 
2210
            diff_type = 'partial'
 
2211
        else:
 
2212
            diff_type = 'full'
 
2213
 
 
2214
        b.lock_read()
 
2215
        try:
 
2216
            # Build the log formatter
2152
2217
            if log_format is None:
2153
2218
                log_format = log.log_formatter_registry.get_default(b)
2154
 
 
2155
2219
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2156
2220
                            show_timezone=timezone,
2157
2221
                            delta_format=get_verbosity_level(),
2158
 
                            levels=levels)
2159
 
 
2160
 
            show_log(b,
2161
 
                     lf,
2162
 
                     file_id,
2163
 
                     verbose=verbose,
2164
 
                     direction=direction,
2165
 
                     start_revision=rev1,
2166
 
                     end_revision=rev2,
2167
 
                     search=message,
2168
 
                     limit=limit,
2169
 
                     show_diff=show_diff)
 
2222
                            levels=levels,
 
2223
                            show_advice=levels is None)
 
2224
 
 
2225
            # Choose the algorithm for doing the logging. It's annoying
 
2226
            # having multiple code paths like this but necessary until
 
2227
            # the underlying repository format is faster at generating
 
2228
            # deltas or can provide everything we need from the indices.
 
2229
            # The default algorithm - match-using-deltas - works for
 
2230
            # multiple files and directories and is faster for small
 
2231
            # amounts of history (200 revisions say). However, it's too
 
2232
            # slow for logging a single file in a repository with deep
 
2233
            # history, i.e. > 10K revisions. In the spirit of "do no
 
2234
            # evil when adding features", we continue to use the
 
2235
            # original algorithm - per-file-graph - for the "single
 
2236
            # file that isn't a directory without showing a delta" case.
 
2237
            partial_history = revision and b.repository._format.supports_chks
 
2238
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2239
                or delta_type or partial_history)
 
2240
 
 
2241
            # Build the LogRequest and execute it
 
2242
            if len(file_ids) == 0:
 
2243
                file_ids = None
 
2244
            rqst = make_log_request_dict(
 
2245
                direction=direction, specific_fileids=file_ids,
 
2246
                start_revision=rev1, end_revision=rev2, limit=limit,
 
2247
                message_search=message, delta_type=delta_type,
 
2248
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2249
            Logger(b, rqst).show(lf)
2170
2250
        finally:
2171
2251
            b.unlock()
2172
2252
 
2175
2255
    """Take the input of a revision option and turn it into a revision range.
2176
2256
 
2177
2257
    It returns RevisionInfo objects which can be used to obtain the rev_id's
2178
 
    of the desired revisons. It does some user input validations.
 
2258
    of the desired revisions. It does some user input validations.
2179
2259
    """
2180
2260
    if revisionspec_list is None:
2181
2261
        rev1 = None
2250
2330
 
2251
2331
    _see_also = ['status', 'cat']
2252
2332
    takes_args = ['path?']
2253
 
    # TODO: Take a revision or remote path and list that tree instead.
2254
2333
    takes_options = [
2255
2334
            'verbose',
2256
2335
            'revision',
2257
 
            Option('non-recursive',
2258
 
                   help='Don\'t recurse into subdirectories.'),
 
2336
            Option('recursive', short_name='R',
 
2337
                   help='Recurse into subdirectories.'),
2259
2338
            Option('from-root',
2260
2339
                   help='Print paths relative to the root of the branch.'),
2261
2340
            Option('unknown', help='Print unknown files.'),
2272
2351
            ]
2273
2352
    @display_command
2274
2353
    def run(self, revision=None, verbose=False,
2275
 
            non_recursive=False, from_root=False,
 
2354
            recursive=False, from_root=False,
2276
2355
            unknown=False, versioned=False, ignored=False,
2277
2356
            null=False, kind=None, show_ids=False, path=None):
2278
2357
 
2309
2388
            if view_files:
2310
2389
                apply_view = True
2311
2390
                view_str = views.view_display_str(view_files)
2312
 
                note("ignoring files outside view: %s" % view_str)
 
2391
                note("Ignoring files outside view. View is %s" % view_str)
2313
2392
 
2314
2393
        tree.lock_read()
2315
2394
        try:
2316
2395
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
2317
2396
                if fp.startswith(relpath):
2318
 
                    fp = osutils.pathjoin(prefix, fp[len(relpath):])
2319
 
                    if non_recursive and '/' in fp:
 
2397
                    rp = fp[len(relpath):]
 
2398
                    fp = osutils.pathjoin(prefix, rp)
 
2399
                    if not recursive and '/' in rp:
2320
2400
                        continue
2321
2401
                    if not all and not selection[fc]:
2322
2402
                        continue
2329
2409
                            continue
2330
2410
                    kindch = entry.kind_character()
2331
2411
                    outstring = fp + kindch
 
2412
                    ui.ui_factory.clear_term()
2332
2413
                    if verbose:
2333
2414
                        outstring = '%-8s %s' % (fc, outstring)
2334
2415
                        if show_ids and fid is not None:
2527
2608
               help="Type of file to export to.",
2528
2609
               type=unicode),
2529
2610
        'revision',
 
2611
        Option('filters', help='Apply content filters to export the '
 
2612
                'convenient form.'),
2530
2613
        Option('root',
2531
2614
               type=str,
2532
2615
               help="Name of the root directory inside the exported file."),
2533
2616
        ]
2534
2617
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
2535
 
        root=None):
 
2618
        root=None, filters=False):
2536
2619
        from bzrlib.export import export
2537
2620
 
2538
2621
        if branch_or_subdir is None:
2545
2628
 
2546
2629
        rev_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
2547
2630
        try:
2548
 
            export(rev_tree, dest, format, root, subdir)
 
2631
            export(rev_tree, dest, format, root, subdir, filtered=filters)
2549
2632
        except errors.NoSuchExportFormat, e:
2550
2633
            raise errors.BzrCommandError('Unsupported export format: %s' % e.format)
2551
2634
 
2562
2645
    _see_also = ['ls']
2563
2646
    takes_options = [
2564
2647
        Option('name-from-revision', help='The path name in the old tree.'),
 
2648
        Option('filters', help='Apply content filters to display the '
 
2649
                'convenience form.'),
2565
2650
        'revision',
2566
2651
        ]
2567
2652
    takes_args = ['filename']
2568
2653
    encoding_type = 'exact'
2569
2654
 
2570
2655
    @display_command
2571
 
    def run(self, filename, revision=None, name_from_revision=False):
 
2656
    def run(self, filename, revision=None, name_from_revision=False,
 
2657
            filters=False):
2572
2658
        if revision is not None and len(revision) != 1:
2573
2659
            raise errors.BzrCommandError("bzr cat --revision takes exactly"
2574
2660
                                         " one revision specifier")
2577
2663
        branch.lock_read()
2578
2664
        try:
2579
2665
            return self._run(tree, branch, relpath, filename, revision,
2580
 
                             name_from_revision)
 
2666
                             name_from_revision, filters)
2581
2667
        finally:
2582
2668
            branch.unlock()
2583
2669
 
2584
 
    def _run(self, tree, b, relpath, filename, revision, name_from_revision):
 
2670
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
 
2671
        filtered):
2585
2672
        if tree is None:
2586
2673
            tree = b.basis_tree()
2587
2674
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2616
2703
                raise errors.BzrCommandError(
2617
2704
                    "%r is not present in revision %s" % (
2618
2705
                        filename, rev_tree.get_revision_id()))
2619
 
        self.outf.write(content)
 
2706
        if filtered:
 
2707
            from bzrlib.filters import (
 
2708
                ContentFilterContext,
 
2709
                filtered_output_bytes,
 
2710
                )
 
2711
            filters = rev_tree._content_filter_stack(relpath)
 
2712
            chunks = content.splitlines(True)
 
2713
            content = filtered_output_bytes(chunks, filters,
 
2714
                ContentFilterContext(relpath, rev_tree))
 
2715
            self.outf.writelines(content)
 
2716
        else:
 
2717
            self.outf.write(content)
2620
2718
 
2621
2719
 
2622
2720
class cmd_local_time_offset(Command):
2631
2729
class cmd_commit(Command):
2632
2730
    """Commit changes into a new revision.
2633
2731
 
2634
 
    If no arguments are given, the entire tree is committed.
2635
 
 
2636
 
    If selected files are specified, only changes to those files are
2637
 
    committed.  If a directory is specified then the directory and everything
2638
 
    within it is committed.
2639
 
 
2640
 
    When excludes are given, they take precedence over selected files.
2641
 
    For example, too commit only changes within foo, but not changes within
2642
 
    foo/bar::
2643
 
 
2644
 
      bzr commit foo -x foo/bar
2645
 
 
2646
 
    If author of the change is not the same person as the committer, you can
2647
 
    specify the author's name using the --author option. The name should be
2648
 
    in the same format as a committer-id, e.g. "John Doe <jdoe@example.com>".
2649
 
    If there is more than one author of the change you can specify the option
2650
 
    multiple times, once for each author.
2651
 
 
2652
 
    A selected-file commit may fail in some cases where the committed
2653
 
    tree would be invalid. Consider::
2654
 
 
2655
 
      bzr init foo
2656
 
      mkdir foo/bar
2657
 
      bzr add foo/bar
2658
 
      bzr commit foo -m "committing foo"
2659
 
      bzr mv foo/bar foo/baz
2660
 
      mkdir foo/bar
2661
 
      bzr add foo/bar
2662
 
      bzr commit foo/bar -m "committing bar but not baz"
2663
 
 
2664
 
    In the example above, the last commit will fail by design. This gives
2665
 
    the user the opportunity to decide whether they want to commit the
2666
 
    rename at the same time, separately first, or not at all. (As a general
2667
 
    rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
2668
 
 
2669
 
    Note: A selected-file commit after a merge is not yet supported.
 
2732
    An explanatory message needs to be given for each commit. This is
 
2733
    often done by using the --message option (getting the message from the
 
2734
    command line) or by using the --file option (getting the message from
 
2735
    a file). If neither of these options is given, an editor is opened for
 
2736
    the user to enter the message. To see the changed files in the
 
2737
    boilerplate text loaded into the editor, use the --show-diff option.
 
2738
 
 
2739
    By default, the entire tree is committed and the person doing the
 
2740
    commit is assumed to be the author. These defaults can be overridden
 
2741
    as explained below.
 
2742
 
 
2743
    :Selective commits:
 
2744
 
 
2745
      If selected files are specified, only changes to those files are
 
2746
      committed.  If a directory is specified then the directory and
 
2747
      everything within it is committed.
 
2748
  
 
2749
      When excludes are given, they take precedence over selected files.
 
2750
      For example, to commit only changes within foo, but not changes
 
2751
      within foo/bar::
 
2752
  
 
2753
        bzr commit foo -x foo/bar
 
2754
  
 
2755
      A selective commit after a merge is not yet supported.
 
2756
 
 
2757
    :Custom authors:
 
2758
 
 
2759
      If the author of the change is not the same person as the committer,
 
2760
      you can specify the author's name using the --author option. The
 
2761
      name should be in the same format as a committer-id, e.g.
 
2762
      "John Doe <jdoe@example.com>". If there is more than one author of
 
2763
      the change you can specify the option multiple times, once for each
 
2764
      author.
 
2765
  
 
2766
    :Checks:
 
2767
 
 
2768
      A common mistake is to forget to add a new file or directory before
 
2769
      running the commit command. The --strict option checks for unknown
 
2770
      files and aborts the commit if any are found. More advanced pre-commit
 
2771
      checks can be implemented by defining hooks. See ``bzr help hooks``
 
2772
      for details.
 
2773
 
 
2774
    :Things to note:
 
2775
 
 
2776
      If you accidentially commit the wrong changes or make a spelling
 
2777
      mistake in the commit message say, you can use the uncommit command
 
2778
      to undo it. See ``bzr help uncommit`` for details.
 
2779
 
 
2780
      Hooks can also be configured to run after a commit. This allows you
 
2781
      to trigger updates to external systems like bug trackers. The --fixes
 
2782
      option can be used to record the association between a revision and
 
2783
      one or more bugs. See ``bzr help bugs`` for details.
 
2784
 
 
2785
      A selective commit may fail in some cases where the committed
 
2786
      tree would be invalid. Consider::
 
2787
  
 
2788
        bzr init foo
 
2789
        mkdir foo/bar
 
2790
        bzr add foo/bar
 
2791
        bzr commit foo -m "committing foo"
 
2792
        bzr mv foo/bar foo/baz
 
2793
        mkdir foo/bar
 
2794
        bzr add foo/bar
 
2795
        bzr commit foo/bar -m "committing bar but not baz"
 
2796
  
 
2797
      In the example above, the last commit will fail by design. This gives
 
2798
      the user the opportunity to decide whether they want to commit the
 
2799
      rename at the same time, separately first, or not at all. (As a general
 
2800
      rule, when in doubt, Bazaar has a policy of Doing the Safe Thing.)
2670
2801
    """
2671
2802
    # TODO: Run hooks on tree to-be-committed, and after commit.
2672
2803
 
2677
2808
 
2678
2809
    # XXX: verbose currently does nothing
2679
2810
 
2680
 
    _see_also = ['bugs', 'uncommit']
 
2811
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
2681
2812
    takes_args = ['selected*']
2682
2813
    takes_options = [
2683
2814
            ListOption('exclude', type=str, short_name='x',
2804
2935
        except PointlessCommit:
2805
2936
            # FIXME: This should really happen before the file is read in;
2806
2937
            # perhaps prepare the commit; get the message; then actually commit
2807
 
            raise errors.BzrCommandError("no changes to commit."
2808
 
                              " use --unchanged to commit anyhow")
 
2938
            raise errors.BzrCommandError("No changes to commit."
 
2939
                              " Use --unchanged to commit anyhow.")
2809
2940
        except ConflictsInTree:
2810
2941
            raise errors.BzrCommandError('Conflicts detected in working '
2811
2942
                'tree.  Use "bzr conflicts" to list, "bzr resolve FILE" to'
2911
3042
 
2912
3043
    def run(self, url='.', format=None):
2913
3044
        from bzrlib.upgrade import upgrade
2914
 
        if format is None:
2915
 
            format = bzrdir.format_registry.make_bzrdir('default')
2916
3045
        upgrade(url, format)
2917
3046
 
2918
3047
 
3145
3274
                            ),
3146
3275
                     Option('list-only',
3147
3276
                            help='List the tests instead of running them.'),
 
3277
                     RegistryOption('parallel',
 
3278
                        help="Run the test suite in parallel.",
 
3279
                        lazy_registry=('bzrlib.tests', 'parallel_registry'),
 
3280
                        value_switches=False,
 
3281
                        ),
3148
3282
                     Option('randomize', type=str, argname="SEED",
3149
3283
                            help='Randomize the order of tests using the given'
3150
3284
                                 ' seed or "now" for the current time.'),
3152
3286
                            short_name='x',
3153
3287
                            help='Exclude tests that match this regular'
3154
3288
                                 ' expression.'),
 
3289
                     Option('subunit',
 
3290
                        help='Output test progress via subunit.'),
3155
3291
                     Option('strict', help='Fail on missing dependencies or '
3156
3292
                            'known failures.'),
3157
3293
                     Option('load-list', type=str, argname='TESTLISTFILE',
3174
3310
            lsprof_timed=None, cache_dir=None,
3175
3311
            first=False, list_only=False,
3176
3312
            randomize=None, exclude=None, strict=False,
3177
 
            load_list=None, debugflag=None, starting_with=None):
 
3313
            load_list=None, debugflag=None, starting_with=None, subunit=False,
 
3314
            parallel=None):
3178
3315
        from bzrlib.tests import selftest
3179
3316
        import bzrlib.benchmarks as benchmarks
3180
3317
        from bzrlib.benchmarks import tree_creator
3184
3321
 
3185
3322
        if cache_dir is not None:
3186
3323
            tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir)
3187
 
        if not list_only:
3188
 
            print 'testing: %s' % (osutils.realpath(sys.argv[0]),)
3189
 
            print '   %s (%s python%s)' % (
3190
 
                    bzrlib.__path__[0],
3191
 
                    bzrlib.version_string,
3192
 
                    bzrlib._format_version_tuple(sys.version_info),
3193
 
                    )
3194
 
        print
3195
3324
        if testspecs_list is not None:
3196
3325
            pattern = '|'.join(testspecs_list)
3197
3326
        else:
3198
3327
            pattern = ".*"
 
3328
        if subunit:
 
3329
            try:
 
3330
                from bzrlib.tests import SubUnitBzrRunner
 
3331
            except ImportError:
 
3332
                raise errors.BzrCommandError("subunit not available. subunit "
 
3333
                    "needs to be installed to use --subunit.")
 
3334
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunner
 
3335
        if parallel:
 
3336
            self.additional_selftest_args.setdefault(
 
3337
                'suite_decorators', []).append(parallel)
3199
3338
        if benchmark:
3200
3339
            test_suite_factory = benchmarks.test_suite
3201
3340
            # Unless user explicitly asks for quiet, be verbose in benchmarks
3227
3366
        finally:
3228
3367
            if benchfile is not None:
3229
3368
                benchfile.close()
3230
 
        if result:
3231
 
            note('tests passed')
3232
 
        else:
3233
 
            note('tests failed')
3234
3369
        return int(not result)
3235
3370
 
3236
3371
 
3342
3477
 
3343
3478
            bzr merge -r 81..82 ../bzr.dev
3344
3479
 
3345
 
        To apply a merge directive contained in in /tmp/merge:
 
3480
        To apply a merge directive contained in /tmp/merge:
3346
3481
 
3347
3482
            bzr merge /tmp/merge
3348
3483
    """
3864
3999
            type=_parse_revision_str,
3865
4000
            help='Filter on local branch revisions (inclusive). '
3866
4001
                'See "help revisionspec" for details.'),
3867
 
        Option('include-merges', 'Show merged revisions.'),
 
4002
        Option('include-merges',
 
4003
               'Show all revisions in addition to the mainline ones.'),
3868
4004
        ]
3869
4005
    encoding_type = 'replace'
3870
4006
 
4415
4551
    takes_options = [
4416
4552
        Option('inet',
4417
4553
               help='Serve on stdin/out for use from inetd or sshd.'),
 
4554
        RegistryOption('protocol', 
 
4555
               help="Protocol to serve.", 
 
4556
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
 
4557
               value_switches=True),
4418
4558
        Option('port',
4419
4559
               help='Listen for connections on nominated port of the form '
4420
4560
                    '[hostname:]portnumber.  Passing 0 as the port number will '
4421
 
                    'result in a dynamically allocated port.  The default port is '
4422
 
                    '4155.',
 
4561
                    'result in a dynamically allocated port.  The default port '
 
4562
                    'depends on the protocol.',
4423
4563
               type=str),
4424
4564
        Option('directory',
4425
4565
               help='Serve contents of this directory.',
4431
4571
                ),
4432
4572
        ]
4433
4573
 
4434
 
    def run_smart_server(self, smart_server):
4435
 
        """Run 'smart_server' forever, with no UI output at all."""
4436
 
        # For the duration of this server, no UI output is permitted. note
4437
 
        # that this may cause problems with blackbox tests. This should be
4438
 
        # changed with care though, as we dont want to use bandwidth sending
4439
 
        # progress over stderr to smart server clients!
4440
 
        from bzrlib import lockdir
4441
 
        old_factory = ui.ui_factory
4442
 
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
4443
 
        try:
4444
 
            ui.ui_factory = ui.SilentUIFactory()
4445
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 0
4446
 
            smart_server.serve()
4447
 
        finally:
4448
 
            ui.ui_factory = old_factory
4449
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = old_lockdir_timeout
4450
 
 
4451
4574
    def get_host_and_port(self, port):
4452
4575
        """Return the host and port to run the smart server on.
4453
4576
 
4454
 
        If 'port' is None, the default host (`medium.BZR_DEFAULT_INTERFACE`)
4455
 
        and port (`medium.BZR_DEFAULT_PORT`) will be used.
 
4577
        If 'port' is None, None will be returned for the host and port.
4456
4578
 
4457
4579
        If 'port' has a colon in it, the string before the colon will be
4458
4580
        interpreted as the host.
4461
4583
        :return: A tuple of (host, port), where 'host' is a host name or IP,
4462
4584
            and port is an integer TCP/IP port.
4463
4585
        """
4464
 
        from bzrlib.smart import medium
4465
 
        host = medium.BZR_DEFAULT_INTERFACE
4466
 
        if port is None:
4467
 
            port = medium.BZR_DEFAULT_PORT
4468
 
        else:
 
4586
        host = None
 
4587
        if port is not None:
4469
4588
            if ':' in port:
4470
4589
                host, port = port.split(':')
4471
4590
            port = int(port)
4472
4591
        return host, port
4473
4592
 
4474
 
    def get_smart_server(self, transport, inet, port):
4475
 
        """Construct a smart server.
4476
 
 
4477
 
        :param transport: The base transport from which branches will be
4478
 
            served.
4479
 
        :param inet: If True, serve over stdin and stdout. Used for running
4480
 
            from inet.
4481
 
        :param port: The port to listen on. By default, it's `
4482
 
            medium.BZR_DEFAULT_PORT`. See `get_host_and_port` for more
4483
 
            information.
4484
 
        :return: A smart server.
4485
 
        """
4486
 
        from bzrlib.smart import medium, server
4487
 
        if inet:
4488
 
            smart_server = medium.SmartServerPipeStreamMedium(
4489
 
                sys.stdin, sys.stdout, transport)
4490
 
        else:
4491
 
            host, port = self.get_host_and_port(port)
4492
 
            smart_server = server.SmartTCPServer(
4493
 
                transport, host=host, port=port)
4494
 
            note('listening on port: %s' % smart_server.port)
4495
 
        return smart_server
4496
 
 
4497
 
    def run(self, port=None, inet=False, directory=None, allow_writes=False):
4498
 
        from bzrlib.transport import get_transport
4499
 
        from bzrlib.transport.chroot import ChrootServer
 
4593
    def run(self, port=None, inet=False, directory=None, allow_writes=False,
 
4594
            protocol=None):
 
4595
        from bzrlib.transport import get_transport, transport_server_registry
4500
4596
        if directory is None:
4501
4597
            directory = os.getcwd()
 
4598
        if protocol is None:
 
4599
            protocol = transport_server_registry.get()
 
4600
        host, port = self.get_host_and_port(port)
4502
4601
        url = urlutils.local_path_to_url(directory)
4503
4602
        if not allow_writes:
4504
4603
            url = 'readonly+' + url
4505
 
        chroot_server = ChrootServer(get_transport(url))
4506
 
        chroot_server.setUp()
4507
 
        t = get_transport(chroot_server.get_url())
4508
 
        smart_server = self.get_smart_server(t, inet, port)
4509
 
        self.run_smart_server(smart_server)
 
4604
        transport = get_transport(url)
 
4605
        protocol(transport, host, port, inet)
4510
4606
 
4511
4607
 
4512
4608
class cmd_join(Command):
4513
 
    """Combine a subtree into its containing tree.
 
4609
    """Combine a tree into its containing tree.
4514
4610
 
4515
 
    This command is for experimental use only.  It requires the target tree
4516
 
    to be in dirstate-with-subtree format, which cannot be converted into
4517
 
    earlier formats.
 
4611
    This command requires the target tree to be in a rich-root format.
4518
4612
 
4519
4613
    The TREE argument should be an independent tree, inside another tree, but
4520
4614
    not part of it.  (Such trees can be produced by "bzr split", but also by
4523
4617
    The result is a combined tree, with the subtree no longer an independant
4524
4618
    part.  This is marked as a merge of the subtree into the containing tree,
4525
4619
    and all history is preserved.
4526
 
 
4527
 
    If --reference is specified, the subtree retains its independence.  It can
4528
 
    be branched by itself, and can be part of multiple projects at the same
4529
 
    time.  But operations performed in the containing tree, such as commit
4530
 
    and merge, will recurse into the subtree.
4531
4620
    """
4532
4621
 
4533
4622
    _see_also = ['split']
4534
4623
    takes_args = ['tree']
4535
4624
    takes_options = [
4536
 
            Option('reference', help='Join by reference.'),
 
4625
            Option('reference', help='Join by reference.', hidden=True),
4537
4626
            ]
4538
 
    hidden = True
4539
4627
 
4540
4628
    def run(self, tree, reference=False):
4541
4629
        sub_tree = WorkingTree.open(tree)
4575
4663
    branch.  Commits in the top-level tree will not apply to the new subtree.
4576
4664
    """
4577
4665
 
4578
 
    # join is not un-hidden yet
4579
 
    #_see_also = ['join']
 
4666
    _see_also = ['join']
4580
4667
    takes_args = ['tree']
4581
4668
 
4582
4669
    def run(self, tree):
4767
4854
        'revision',
4768
4855
        'message',
4769
4856
        Option('body', help='Body for the email.', type=unicode),
4770
 
        RegistryOption.from_kwargs('format',
4771
 
        'Use the specified output format.',
4772
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
4773
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
4857
        RegistryOption('format',
 
4858
                       help='Use the specified output format.', 
 
4859
                       lazy_registry=('bzrlib.send', 'format_registry'))
4774
4860
        ]
4775
4861
 
4776
4862
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4777
4863
            no_patch=False, revision=None, remember=False, output=None,
4778
 
            format='4', mail_to=None, message=None, body=None, **kwargs):
4779
 
        return self._run(submit_branch, revision, public_branch, remember,
 
4864
            format=None, mail_to=None, message=None, body=None, **kwargs):
 
4865
        from bzrlib.send import send
 
4866
        return send(submit_branch, revision, public_branch, remember,
4780
4867
                         format, no_bundle, no_patch, output,
4781
 
                         kwargs.get('from', '.'), mail_to, message, body)
4782
 
 
4783
 
    def _run(self, submit_branch, revision, public_branch, remember, format,
4784
 
             no_bundle, no_patch, output, from_, mail_to, message, body):
4785
 
        from bzrlib.revision import NULL_REVISION
4786
 
        branch = Branch.open_containing(from_)[0]
4787
 
        if output is None:
4788
 
            outfile = cStringIO.StringIO()
4789
 
        elif output == '-':
4790
 
            outfile = self.outf
4791
 
        else:
4792
 
            outfile = open(output, 'wb')
4793
 
        # we may need to write data into branch's repository to calculate
4794
 
        # the data to send.
4795
 
        branch.lock_write()
4796
 
        try:
4797
 
            if output is None:
4798
 
                config = branch.get_config()
4799
 
                if mail_to is None:
4800
 
                    mail_to = config.get_user_option('submit_to')
4801
 
                mail_client = config.get_mail_client()
4802
 
                if (not getattr(mail_client, 'supports_body', False)
4803
 
                    and body is not None):
4804
 
                    raise errors.BzrCommandError(
4805
 
                        'Mail client "%s" does not support specifying body' %
4806
 
                        mail_client.__class__.__name__)
4807
 
            if remember and submit_branch is None:
4808
 
                raise errors.BzrCommandError(
4809
 
                    '--remember requires a branch to be specified.')
4810
 
            stored_submit_branch = branch.get_submit_branch()
4811
 
            remembered_submit_branch = None
4812
 
            if submit_branch is None:
4813
 
                submit_branch = stored_submit_branch
4814
 
                remembered_submit_branch = "submit"
4815
 
            else:
4816
 
                if stored_submit_branch is None or remember:
4817
 
                    branch.set_submit_branch(submit_branch)
4818
 
            if submit_branch is None:
4819
 
                submit_branch = branch.get_parent()
4820
 
                remembered_submit_branch = "parent"
4821
 
            if submit_branch is None:
4822
 
                raise errors.BzrCommandError('No submit branch known or'
4823
 
                                             ' specified')
4824
 
            if remembered_submit_branch is not None:
4825
 
                note('Using saved %s location "%s" to determine what '
4826
 
                        'changes to submit.', remembered_submit_branch,
4827
 
                        submit_branch)
4828
 
 
4829
 
            if mail_to is None:
4830
 
                submit_config = Branch.open(submit_branch).get_config()
4831
 
                mail_to = submit_config.get_user_option("child_submit_to")
4832
 
 
4833
 
            stored_public_branch = branch.get_public_branch()
4834
 
            if public_branch is None:
4835
 
                public_branch = stored_public_branch
4836
 
            elif stored_public_branch is None or remember:
4837
 
                branch.set_public_branch(public_branch)
4838
 
            if no_bundle and public_branch is None:
4839
 
                raise errors.BzrCommandError('No public branch specified or'
4840
 
                                             ' known')
4841
 
            base_revision_id = None
4842
 
            revision_id = None
4843
 
            if revision is not None:
4844
 
                if len(revision) > 2:
4845
 
                    raise errors.BzrCommandError('bzr send takes '
4846
 
                        'at most two one revision identifiers')
4847
 
                revision_id = revision[-1].as_revision_id(branch)
4848
 
                if len(revision) == 2:
4849
 
                    base_revision_id = revision[0].as_revision_id(branch)
4850
 
            if revision_id is None:
4851
 
                revision_id = branch.last_revision()
4852
 
            if revision_id == NULL_REVISION:
4853
 
                raise errors.BzrCommandError('No revisions to submit.')
4854
 
            if format == '4':
4855
 
                directive = merge_directive.MergeDirective2.from_objects(
4856
 
                    branch.repository, revision_id, time.time(),
4857
 
                    osutils.local_time_offset(), submit_branch,
4858
 
                    public_branch=public_branch, include_patch=not no_patch,
4859
 
                    include_bundle=not no_bundle, message=message,
4860
 
                    base_revision_id=base_revision_id)
4861
 
            elif format == '0.9':
4862
 
                if not no_bundle:
4863
 
                    if not no_patch:
4864
 
                        patch_type = 'bundle'
4865
 
                    else:
4866
 
                        raise errors.BzrCommandError('Format 0.9 does not'
4867
 
                            ' permit bundle with no patch')
4868
 
                else:
4869
 
                    if not no_patch:
4870
 
                        patch_type = 'diff'
4871
 
                    else:
4872
 
                        patch_type = None
4873
 
                directive = merge_directive.MergeDirective.from_objects(
4874
 
                    branch.repository, revision_id, time.time(),
4875
 
                    osutils.local_time_offset(), submit_branch,
4876
 
                    public_branch=public_branch, patch_type=patch_type,
4877
 
                    message=message)
4878
 
 
4879
 
            outfile.writelines(directive.to_lines())
4880
 
            if output is None:
4881
 
                subject = '[MERGE] '
4882
 
                if message is not None:
4883
 
                    subject += message
4884
 
                else:
4885
 
                    revision = branch.repository.get_revision(revision_id)
4886
 
                    subject += revision.get_summary()
4887
 
                basename = directive.get_disk_name(branch)
4888
 
                mail_client.compose_merge_request(mail_to, subject,
4889
 
                                                  outfile.getvalue(),
4890
 
                                                  basename, body)
4891
 
        finally:
4892
 
            if output != '-':
4893
 
                outfile.close()
4894
 
            branch.unlock()
 
4868
                         kwargs.get('from', '.'), mail_to, message, body,
 
4869
                         self.outf)
4895
4870
 
4896
4871
 
4897
4872
class cmd_bundle_revisions(cmd_send):
4898
 
 
4899
4873
    """Create a merge-directive for submitting changes.
4900
4874
 
4901
4875
    A merge directive provides many things needed for requesting merges:
4943
4917
        Option('output', short_name='o', help='Write directive to this file.',
4944
4918
               type=unicode),
4945
4919
        'revision',
4946
 
        RegistryOption.from_kwargs('format',
4947
 
        'Use the specified output format.',
4948
 
        **{'4': 'Bundle format 4, Merge Directive 2 (default)',
4949
 
           '0.9': 'Bundle format 0.9, Merge Directive 1',})
 
4920
        RegistryOption('format',
 
4921
                       help='Use the specified output format.',
 
4922
                       lazy_registry=('bzrlib.send', 'format_registry')),
4950
4923
        ]
4951
4924
    aliases = ['bundle']
4952
4925
 
4956
4929
 
4957
4930
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4958
4931
            no_patch=False, revision=None, remember=False, output=None,
4959
 
            format='4', **kwargs):
 
4932
            format=None, **kwargs):
4960
4933
        if output is None:
4961
4934
            output = '-'
4962
 
        return self._run(submit_branch, revision, public_branch, remember,
 
4935
        from bzrlib.send import send
 
4936
        return send(submit_branch, revision, public_branch, remember,
4963
4937
                         format, no_bundle, no_patch, output,
4964
 
                         kwargs.get('from', '.'), None, None, None)
 
4938
                         kwargs.get('from', '.'), None, None, None,
 
4939
                         self.outf)
4965
4940
 
4966
4941
 
4967
4942
class cmd_tag(Command):
5062
5037
        if not tags:
5063
5038
            return
5064
5039
 
5065
 
        if revision:
5066
 
            branch.lock_read()
5067
 
            try:
 
5040
        branch.lock_read()
 
5041
        try:
 
5042
            if revision:
5068
5043
                graph = branch.repository.get_graph()
5069
5044
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5070
5045
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5071
5046
                # only show revisions between revid1 and revid2 (inclusive)
5072
5047
                tags = [(tag, revid) for tag, revid in tags if
5073
5048
                    graph.is_between(revid, revid1, revid2)]
5074
 
            finally:
5075
 
                branch.unlock()
5076
 
        if sort == 'alpha':
5077
 
            tags.sort()
5078
 
        elif sort == 'time':
5079
 
            timestamps = {}
5080
 
            for tag, revid in tags:
5081
 
                try:
5082
 
                    revobj = branch.repository.get_revision(revid)
5083
 
                except errors.NoSuchRevision:
5084
 
                    timestamp = sys.maxint # place them at the end
5085
 
                else:
5086
 
                    timestamp = revobj.timestamp
5087
 
                timestamps[revid] = timestamp
5088
 
            tags.sort(key=lambda x: timestamps[x[1]])
5089
 
        if not show_ids:
5090
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5091
 
            revno_map = branch.get_revision_id_to_revno_map()
5092
 
            tags = [ (tag, '.'.join(map(str, revno_map.get(revid, ('?',)))))
5093
 
                        for tag, revid in tags ]
 
5049
            if sort == 'alpha':
 
5050
                tags.sort()
 
5051
            elif sort == 'time':
 
5052
                timestamps = {}
 
5053
                for tag, revid in tags:
 
5054
                    try:
 
5055
                        revobj = branch.repository.get_revision(revid)
 
5056
                    except errors.NoSuchRevision:
 
5057
                        timestamp = sys.maxint # place them at the end
 
5058
                    else:
 
5059
                        timestamp = revobj.timestamp
 
5060
                    timestamps[revid] = timestamp
 
5061
                tags.sort(key=lambda x: timestamps[x[1]])
 
5062
            if not show_ids:
 
5063
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5064
                for index, (tag, revid) in enumerate(tags):
 
5065
                    try:
 
5066
                        revno = branch.revision_id_to_dotted_revno(revid)
 
5067
                        if isinstance(revno, tuple):
 
5068
                            revno = '.'.join(map(str, revno))
 
5069
                    except errors.NoSuchRevision:
 
5070
                        # Bad tag data/merges can lead to tagged revisions
 
5071
                        # which are not in this branch. Fail gracefully ...
 
5072
                        revno = '?'
 
5073
                    tags[index] = (tag, revno)
 
5074
        finally:
 
5075
            branch.unlock()
5094
5076
        for tag, revspec in tags:
5095
5077
            self.outf.write('%-20s %s\n' % (tag, revspec))
5096
5078
 
5195
5177
        from bzrlib import switch
5196
5178
        tree_location = '.'
5197
5179
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5198
 
        branch = control_dir.open_branch()
 
5180
        try:
 
5181
            branch = control_dir.open_branch()
 
5182
            had_explicit_nick = branch.get_config().has_explicit_nickname()
 
5183
        except errors.NotBranchError:
 
5184
            had_explicit_nick = False
5199
5185
        try:
5200
5186
            to_branch = Branch.open(to_location)
5201
5187
        except errors.NotBranchError:
5202
 
            this_branch = control_dir.open_branch()
5203
 
            # This may be a heavy checkout, where we want the master branch
5204
 
            this_url = this_branch.get_bound_location()
5205
 
            # If not, use a local sibling
5206
 
            if this_url is None:
5207
 
                this_url = this_branch.base
 
5188
            this_url = self._get_branch_location(control_dir)
5208
5189
            to_branch = Branch.open(
5209
5190
                urlutils.join(this_url, '..', to_location))
5210
5191
        switch.switch(control_dir, to_branch, force)
5211
 
        if branch.get_config().has_explicit_nickname():
 
5192
        if had_explicit_nick:
5212
5193
            branch = control_dir.open_branch() #get the new branch!
5213
5194
            branch.nick = to_branch.nick
5214
5195
        note('Switched to branch: %s',
5215
5196
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
5216
5197
 
 
5198
    def _get_branch_location(self, control_dir):
 
5199
        """Return location of branch for this control dir."""
 
5200
        try:
 
5201
            this_branch = control_dir.open_branch()
 
5202
            # This may be a heavy checkout, where we want the master branch
 
5203
            master_location = this_branch.get_bound_location()
 
5204
            if master_location is not None:
 
5205
                return master_location
 
5206
            # If not, use a local sibling
 
5207
            return this_branch.base
 
5208
        except errors.NotBranchError:
 
5209
            format = control_dir.find_branch_format()
 
5210
            if getattr(format, 'get_reference', None) is not None:
 
5211
                return format.get_reference(control_dir)
 
5212
            else:
 
5213
                return control_dir.root_transport.base
 
5214
 
5217
5215
 
5218
5216
class cmd_view(Command):
5219
5217
    """Manage filtered views.
5521
5519
                   dry_run=dry_run, no_prompt=force)
5522
5520
 
5523
5521
 
5524
 
def _create_prefix(cur_transport):
5525
 
    needed = [cur_transport]
5526
 
    # Recurse upwards until we can create a directory successfully
5527
 
    while True:
5528
 
        new_transport = cur_transport.clone('..')
5529
 
        if new_transport.base == cur_transport.base:
5530
 
            raise errors.BzrCommandError(
5531
 
                "Failed to create path prefix for %s."
5532
 
                % cur_transport.base)
5533
 
        try:
5534
 
            new_transport.mkdir('.')
5535
 
        except errors.NoSuchFile:
5536
 
            needed.append(new_transport)
5537
 
            cur_transport = new_transport
 
5522
class cmd_reference(Command):
 
5523
    """list, view and set branch locations for nested trees.
 
5524
 
 
5525
    If no arguments are provided, lists the branch locations for nested trees.
 
5526
    If one argument is provided, display the branch location for that tree.
 
5527
    If two arguments are provided, set the branch location for that tree.
 
5528
    """
 
5529
 
 
5530
    hidden = True
 
5531
 
 
5532
    takes_args = ['path?', 'location?']
 
5533
 
 
5534
    def run(self, path=None, location=None):
 
5535
        branchdir = '.'
 
5536
        if path is not None:
 
5537
            branchdir = path
 
5538
        tree, branch, relpath =(
 
5539
            bzrdir.BzrDir.open_containing_tree_or_branch(branchdir))
 
5540
        if path is not None:
 
5541
            path = relpath
 
5542
        if tree is None:
 
5543
            tree = branch.basis_tree()
 
5544
        if path is None:
 
5545
            info = branch._get_all_reference_info().iteritems()
 
5546
            self._display_reference_info(tree, branch, info)
5538
5547
        else:
5539
 
            break
5540
 
    # Now we only need to create child directories
5541
 
    while needed:
5542
 
        cur_transport = needed.pop()
5543
 
        cur_transport.ensure_base()
 
5548
            file_id = tree.path2id(path)
 
5549
            if file_id is None:
 
5550
                raise errors.NotVersionedError(path)
 
5551
            if location is None:
 
5552
                info = [(file_id, branch.get_reference_info(file_id))]
 
5553
                self._display_reference_info(tree, branch, info)
 
5554
            else:
 
5555
                branch.set_reference_info(file_id, path, location)
 
5556
 
 
5557
    def _display_reference_info(self, tree, branch, info):
 
5558
        ref_list = []
 
5559
        for file_id, (path, location) in info:
 
5560
            try:
 
5561
                path = tree.id2path(file_id)
 
5562
            except errors.NoSuchId:
 
5563
                pass
 
5564
            ref_list.append((path, location))
 
5565
        for path, location in sorted(ref_list):
 
5566
            self.outf.write('%s %s\n' % (path, location))
5544
5567
 
5545
5568
 
5546
5569
# these get imported and then picked up by the scan for cmd_*
5553
5576
from bzrlib.bundle.commands import (
5554
5577
    cmd_bundle_info,
5555
5578
    )
 
5579
from bzrlib.foreign import cmd_dpush
5556
5580
from bzrlib.sign_my_commits import cmd_sign_my_commits
5557
5581
from bzrlib.weave_commands import cmd_versionedfile_list, \
5558
5582
        cmd_weave_plan_merge, cmd_weave_merge_text