~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Jelmer Vernooij
  • Date: 2012-04-16 11:08:11 UTC
  • mfrom: (6521 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6522.
  • Revision ID: jelmer@samba.org-20120416110811-0y996ihqy9o2bb1t
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2012 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
86
86
def _get_branch_location(control_dir, possible_transports=None):
87
87
    """Return location of branch for this control dir."""
88
88
    try:
89
 
        this_branch = control_dir.open_branch(
90
 
            possible_transports=possible_transports)
91
 
        # This may be a heavy checkout, where we want the master branch
92
 
        master_location = this_branch.get_bound_location()
93
 
        if master_location is not None:
94
 
            return master_location
95
 
        # If not, use a local sibling
96
 
        return this_branch.base
 
89
        target = control_dir.get_branch_reference()
97
90
    except errors.NotBranchError:
98
 
        format = control_dir.find_branch_format()
99
 
        if getattr(format, 'get_reference', None) is not None:
100
 
            return format.get_reference(control_dir)
101
 
        else:
102
 
            return control_dir.root_transport.base
 
91
        return control_dir.root_transport.base
 
92
    if target is not None:
 
93
        return target
 
94
    this_branch = control_dir.open_branch(
 
95
        possible_transports=possible_transports)
 
96
    # This may be a heavy checkout, where we want the master branch
 
97
    master_location = this_branch.get_bound_location()
 
98
    if master_location is not None:
 
99
        return master_location
 
100
    # If not, use a local sibling
 
101
    return this_branch.base
103
102
 
104
103
 
105
104
def _is_colocated(control_dir, possible_transports=None):
106
105
    """Check if the branch in control_dir is colocated.
107
106
 
108
107
    :param control_dir: Control directory
109
 
    :return: Boolean indicating whether 
 
108
    :return: Tuple with boolean indicating whether the branch is colocated
 
109
        and the full URL to the actual branch
110
110
    """
111
111
    # This path is meant to be relative to the existing branch
112
112
    this_url = _get_branch_location(control_dir,
132
132
def lookup_new_sibling_branch(control_dir, location, possible_transports=None):
133
133
    """Lookup the location for a new sibling branch.
134
134
 
135
 
    :param control_dir: Control directory relative to which to look up
136
 
        the name.
 
135
    :param control_dir: Control directory to find sibling branches from
137
136
    :param location: Name of the new branch
138
137
    :return: Full location to the new branch
139
138
    """
149
148
    return location
150
149
 
151
150
 
152
 
def lookup_sibling_branch(control_dir, location, possible_transports=None):
153
 
    """Lookup sibling branch.
154
 
    
 
151
def open_sibling_branch(control_dir, location, possible_transports=None):
 
152
    """Open a branch, possibly a sibling of another.
 
153
 
155
154
    :param control_dir: Control directory relative to which to lookup the
156
155
        location.
157
156
    :param location: Location to look up
162
161
        return control_dir.open_branch(location, 
163
162
            possible_transports=possible_transports)
164
163
    except (errors.NotBranchError, errors.NoColocatedBranchSupport):
 
164
        this_url = _get_branch_location(control_dir)
 
165
        return Branch.open(
 
166
            urlutils.join(
 
167
                this_url, '..', urlutils.escape(location)))
 
168
 
 
169
 
 
170
def open_nearby_branch(near=None, location=None, possible_transports=None):
 
171
    """Open a nearby branch.
 
172
 
 
173
    :param near: Optional location of container from which to open branch
 
174
    :param location: Location of the branch
 
175
    :return: Branch instance
 
176
    """
 
177
    if near is None:
 
178
        if location is None:
 
179
            location = "."
165
180
        try:
166
 
            return Branch.open(location)
 
181
            return Branch.open(location,
 
182
                possible_transports=possible_transports)
167
183
        except errors.NotBranchError:
168
 
            this_url = _get_branch_location(control_dir)
169
 
            return Branch.open(
170
 
                urlutils.join(
171
 
                    this_url, '..', urlutils.escape(location)))
172
 
 
173
 
 
174
 
@symbol_versioning.deprecated_function(symbol_versioning.deprecated_in((2, 3, 0)))
175
 
def tree_files(file_list, default_branch=u'.', canonicalize=True,
176
 
    apply_view=True):
177
 
    return internal_tree_files(file_list, default_branch, canonicalize,
178
 
        apply_view)
 
184
            near = "."
 
185
    cdir = controldir.ControlDir.open(near,
 
186
        possible_transports=possible_transports)
 
187
    return open_sibling_branch(cdir, location,
 
188
        possible_transports=possible_transports)
 
189
 
 
190
 
 
191
def iter_sibling_branches(control_dir, possible_transports=None):
 
192
    """Iterate over the siblings of a branch.
 
193
 
 
194
    :param control_dir: Control directory for which to look up the siblings
 
195
    :return: Iterator over tuples with branch name and branch object
 
196
    """
 
197
    seen_urls = set()
 
198
    try:
 
199
        reference = control_dir.get_branch_reference()
 
200
    except errors.NotBranchError:
 
201
        # There is no active branch, just return the colocated branches.
 
202
        for name, branch in control_dir.get_branches().iteritems():
 
203
            yield name, branch
 
204
        return
 
205
    if reference is not None:
 
206
        ref_branch = Branch.open(reference,
 
207
            possible_transports=possible_transports)
 
208
    else:
 
209
        ref_branch = None
 
210
    if ref_branch is None or ref_branch.name:
 
211
        if ref_branch is not None:
 
212
            control_dir = ref_branch.bzrdir
 
213
        for name, branch in control_dir.get_branches().iteritems():
 
214
            yield name, branch
 
215
    else:
 
216
        repo = ref_branch.bzrdir.find_repository()
 
217
        for branch in repo.find_branches(using=True):
 
218
            name = urlutils.relative_url(repo.user_url,
 
219
                branch.user_url).rstrip("/")
 
220
            yield name, branch
179
221
 
180
222
 
181
223
def tree_files_for_add(file_list):
243
285
    return rev_tree
244
286
 
245
287
 
246
 
# XXX: Bad function name; should possibly also be a class method of
247
 
# WorkingTree rather than a function.
248
 
@symbol_versioning.deprecated_function(symbol_versioning.deprecated_in((2, 3, 0)))
249
 
def internal_tree_files(file_list, default_branch=u'.', canonicalize=True,
250
 
    apply_view=True):
251
 
    """Convert command-line paths to a WorkingTree and relative paths.
252
 
 
253
 
    Deprecated: use WorkingTree.open_containing_paths instead.
254
 
 
255
 
    This is typically used for command-line processors that take one or
256
 
    more filenames, and infer the workingtree that contains them.
257
 
 
258
 
    The filenames given are not required to exist.
259
 
 
260
 
    :param file_list: Filenames to convert.
261
 
 
262
 
    :param default_branch: Fallback tree path to use if file_list is empty or
263
 
        None.
264
 
 
265
 
    :param apply_view: if True and a view is set, apply it or check that
266
 
        specified files are within it
267
 
 
268
 
    :return: workingtree, [relative_paths]
269
 
    """
270
 
    return WorkingTree.open_containing_paths(
271
 
        file_list, default_directory='.',
272
 
        canonicalize=True,
273
 
        apply_view=True)
274
 
 
275
 
 
276
288
def _get_view_info_for_change_reporter(tree):
277
289
    """Get the view information from a tree for change reporting."""
278
290
    view_info = None
356
368
    This will produce the same results as calling 'bzr diff --summarize'.
357
369
    """
358
370
 
359
 
    # TODO: --no-recurse, --recurse options
 
371
    # TODO: --no-recurse/-N, --recurse options
360
372
 
361
373
    takes_args = ['file*']
362
374
    takes_options = ['show-ids', 'revision', 'change', 'verbose',
791
803
    takes_args = ['file*']
792
804
    takes_options = [
793
805
        Option('no-recurse',
794
 
               help="Don't recursively add the contents of directories."),
 
806
               help="Don't recursively add the contents of directories.",
 
807
               short_name='N'),
795
808
        Option('dry-run',
796
809
               help="Show what would be done, but don't actually do anything."),
797
810
        'verbose',
937
950
            tree = work_tree
938
951
            extra_trees = []
939
952
 
 
953
        self.add_cleanup(tree.lock_read().unlock)
940
954
        if file_list is not None:
941
955
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
942
956
                                      require_versioned=True)
943
957
            # find_ids_across_trees may include some paths that don't
944
958
            # exist in 'tree'.
945
 
            entries = sorted(
946
 
                (tree.id2path(file_id), tree.inventory[file_id])
947
 
                for file_id in file_ids if tree.has_id(file_id))
 
959
            entries = tree.iter_entries_by_dir(specific_file_ids=file_ids)
948
960
        else:
949
 
            entries = tree.inventory.entries()
 
961
            entries = tree.iter_entries_by_dir()
950
962
 
951
 
        self.cleanup_now()
952
 
        for path, entry in entries:
 
963
        for path, entry in sorted(entries):
953
964
            if kind and kind != entry.kind:
954
965
                continue
 
966
            if path == "":
 
967
                continue
955
968
            if show_ids:
956
969
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
957
970
            else:
1029
1042
                and rel_names[0].lower() == rel_names[1].lower()):
1030
1043
                into_existing = False
1031
1044
            else:
1032
 
                inv = tree.inventory
1033
1045
                # 'fix' the case of a potential 'from'
1034
1046
                from_id = tree.path2id(
1035
1047
                            tree.get_canonical_inventory_path(rel_names[0]))
1036
1048
                if (not osutils.lexists(names_list[0]) and
1037
 
                    from_id and inv.get_file_kind(from_id) == "directory"):
 
1049
                    from_id and tree.stored_kind(from_id) == "directory"):
1038
1050
                    into_existing = False
1039
1051
        # move/rename
1040
1052
        if into_existing:
1215
1227
            # Remembers if asked explicitly or no previous location is set
1216
1228
            if (remember
1217
1229
                or (remember is None and branch_to.get_parent() is None)):
 
1230
                # FIXME: This shouldn't be done before the pull
 
1231
                # succeeds... -- vila 2012-01-02
1218
1232
                branch_to.set_parent(branch_from.base)
1219
1233
 
1220
1234
        if revision is not None:
1559
1573
                active_branch = dir.open_branch(name="")
1560
1574
            except errors.NotBranchError:
1561
1575
                active_branch = None
1562
 
            branches = dir.get_branches()
1563
1576
            names = {}
1564
 
            for name, branch in branches.iteritems():
 
1577
            for name, branch in iter_sibling_branches(dir):
1565
1578
                if name == "":
1566
1579
                    continue
1567
1580
                active = (active_branch is not None and
1668
1681
    def run(self, dir=u'.'):
1669
1682
        tree = WorkingTree.open_containing(dir)[0]
1670
1683
        self.add_cleanup(tree.lock_read().unlock)
1671
 
        new_inv = tree.inventory
1672
1684
        old_tree = tree.basis_tree()
1673
1685
        self.add_cleanup(old_tree.lock_read().unlock)
1674
 
        old_inv = old_tree.inventory
1675
1686
        renames = []
1676
1687
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
1677
1688
        for f, paths, c, v, p, n, k, e in iterator:
1848
1859
 
1849
1860
    This makes Bazaar stop tracking changes to the specified files. Bazaar will
1850
1861
    delete them if they can easily be recovered using revert otherwise they
1851
 
    will be backed up (adding an extention of the form .~#~). If no options or
 
1862
    will be backed up (adding an extension of the form .~#~). If no options or
1852
1863
    parameters are given Bazaar will scan for files that are being tracked by
1853
1864
    Bazaar but missing in your tree and stop tracking them for you.
1854
1865
    """
2062
2073
         RegistryOption('format',
2063
2074
                help='Specify a format for this branch. '
2064
2075
                'See "help formats".',
2065
 
                lazy_registry=('bzrlib.bzrdir', 'format_registry'),
 
2076
                lazy_registry=('bzrlib.controldir', 'format_registry'),
2066
2077
                converter=lambda name: controldir.format_registry.make_bzrdir(name),
2067
2078
                value_switches=True,
2068
2079
                title="Branch format",
2391
2402
        self.add_cleanup(tree.lock_read().unlock)
2392
2403
        old = tree.basis_tree()
2393
2404
        self.add_cleanup(old.lock_read().unlock)
2394
 
        for path, ie in old.inventory.iter_entries():
 
2405
        for path, ie in old.iter_entries_by_dir():
2395
2406
            if not tree.has_id(ie.file_id):
2396
2407
                self.outf.write(path)
2397
2408
                if show_ids:
2435
2446
        self.add_cleanup(wt.lock_read().unlock)
2436
2447
        basis = wt.basis_tree()
2437
2448
        self.add_cleanup(basis.lock_read().unlock)
2438
 
        basis_inv = basis.inventory
2439
 
        inv = wt.inventory
2440
 
        for file_id in inv:
2441
 
            if basis_inv.has_id(file_id):
2442
 
                continue
2443
 
            if inv.is_root(file_id) and len(basis_inv) == 0:
2444
 
                continue
2445
 
            path = inv.id2path(file_id)
 
2449
        root_id = wt.get_root_id()
 
2450
        for file_id in wt.all_file_ids():
 
2451
            if basis.has_id(file_id):
 
2452
                continue
 
2453
            if root_id == file_id:
 
2454
                continue
 
2455
            path = wt.id2path(file_id)
2446
2456
            if not os.access(osutils.pathjoin(wt.basedir, path), os.F_OK):
2447
2457
                continue
2448
2458
            if null:
2810
2820
            self.add_cleanup(b.lock_read().unlock)
2811
2821
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2812
2822
 
2813
 
        if b.get_config().validate_signatures_in_log():
 
2823
        if b.get_config_stack().get('validate_signatures_in_log'):
2814
2824
            signatures = True
2815
2825
 
2816
2826
        if signatures:
3536
3546
            tokens = fixed_bug.split(':')
3537
3547
            if len(tokens) == 1:
3538
3548
                if default_bugtracker is None:
3539
 
                    branch_config = branch.get_config()
3540
 
                    default_bugtracker = branch_config.get_user_option(
 
3549
                    branch_config = branch.get_config_stack()
 
3550
                    default_bugtracker = branch_config.get(
3541
3551
                        "bugtracker")
3542
3552
                if default_bugtracker is None:
3543
3553
                    raise errors.BzrCommandError(gettext(
3882
3892
            if directory is None:
3883
3893
                c = Branch.open_containing(u'.')[0].get_config_stack()
3884
3894
            else:
3885
 
                c = Branch.open(directory).get_config_stack()
 
3895
                b = Branch.open(directory)
 
3896
                self.add_cleanup(b.lock_write().unlock)
 
3897
                c = b.get_config_stack()
3886
3898
        else:
3887
3899
            c = _mod_config.GlobalStack()
3888
3900
        c.set('email', name)
3891
3903
class cmd_nick(Command):
3892
3904
    __doc__ = """Print or set the branch nickname.
3893
3905
 
3894
 
    If unset, the tree root directory name is used as the nickname.
3895
 
    To print the current nickname, execute with no argument.
 
3906
    If unset, the colocated branch name is used for colocated branches, and
 
3907
    the branch directory name is used for other branches.  To print the
 
3908
    current nickname, execute with no argument.
3896
3909
 
3897
3910
    Bound branches use the nickname of its master branch unless it is set
3898
3911
    locally.
4694
4707
                if tree.kind(file_id) != "directory":
4695
4708
                    continue
4696
4709
 
4697
 
                for name, ie in tree.inventory.iter_entries(file_id):
 
4710
                # FIXME: Support nested trees
 
4711
                for name, ie in tree.root_inventory.iter_entries(file_id):
4698
4712
                    interesting_ids.add(ie.file_id)
4699
4713
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4700
4714
        else:
4733
4747
 
4734
4748
 
4735
4749
class cmd_revert(Command):
4736
 
    __doc__ = """Revert files to a previous revision.
 
4750
    __doc__ = """\
 
4751
    Set files in the working tree back to the contents of a previous revision.
4737
4752
 
4738
4753
    Giving a list of files will revert only those files.  Otherwise, all files
4739
4754
    will be reverted.  If the revision is not specified with '--revision', the
4740
 
    last committed revision is used.
 
4755
    working tree basis revision is used. A revert operation affects only the
 
4756
    working tree, not any revision history like the branch and repository or
 
4757
    the working tree basis revision.
4741
4758
 
4742
4759
    To remove only some changes, without reverting to a prior version, use
4743
4760
    merge instead.  For example, "merge . -r -2..-3" (don't forget the ".")
4744
4761
    will remove the changes introduced by the second last commit (-2), without
4745
4762
    affecting the changes introduced by the last commit (-1).  To remove
4746
4763
    certain changes on a hunk-by-hunk basis, see the shelve command.
 
4764
    To update the branch to a specific revision or the latest revision and
 
4765
    update the working tree accordingly while preserving local changes, see the
 
4766
    update command.
4747
4767
 
4748
 
    By default, any files that have been manually changed will be backed up
4749
 
    first.  (Files changed only by merge are not backed up.)  Backup files have
4750
 
    '.~#~' appended to their name, where # is a number.
 
4768
    Uncommitted changes to files that are reverted will be discarded.
 
4769
    Howver, by default, any files that have been manually changed will be
 
4770
    backed up first.  (Files changed only by merge are not backed up.)  Backup
 
4771
    files have '.~#~' appended to their name, where # is a number.
4751
4772
 
4752
4773
    When you provide files, you can use their current pathname or the pathname
4753
4774
    from the target revision.  So you can use revert to "undelete" a file by
5000
5021
                             "You have %d extra revisions:\n", 
5001
5022
                             len(local_extra)) %
5002
5023
                len(local_extra))
 
5024
            rev_tag_dict = {}
 
5025
            if local_branch.supports_tags():
 
5026
                rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5003
5027
            for revision in iter_log_revisions(local_extra,
5004
5028
                                local_branch.repository,
5005
 
                                verbose):
 
5029
                                verbose,
 
5030
                                rev_tag_dict):
5006
5031
                lf.log_revision(revision)
5007
5032
            printed_local = True
5008
5033
            status_code = 1
5016
5041
                             "You are missing %d revisions:\n",
5017
5042
                             len(remote_extra)) %
5018
5043
                len(remote_extra))
 
5044
            if remote_branch.supports_tags():
 
5045
                rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5019
5046
            for revision in iter_log_revisions(remote_extra,
5020
5047
                                remote_branch.repository,
5021
 
                                verbose):
 
5048
                                verbose,
 
5049
                                rev_tag_dict):
5022
5050
                lf.log_revision(revision)
5023
5051
            status_code = 1
5024
5052
 
5281
5309
            else:
5282
5310
                if location is None:
5283
5311
                    if b.get_bound_location() is not None:
5284
 
                        raise errors.BzrCommandError(gettext('Branch is already bound'))
 
5312
                        raise errors.BzrCommandError(
 
5313
                            gettext('Branch is already bound'))
5285
5314
                    else:
5286
 
                        raise errors.BzrCommandError(gettext('No location supplied '
5287
 
                            'and no previous location known'))
 
5315
                        raise errors.BzrCommandError(
 
5316
                            gettext('No location supplied'
 
5317
                                    ' and no previous location known'))
5288
5318
        b_other = Branch.open(location)
5289
5319
        try:
5290
5320
            b.bind(b_other)
5494
5524
               help="Protocol to serve.",
5495
5525
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
5496
5526
               value_switches=True),
 
5527
        Option('listen',
 
5528
               help='Listen for connections on nominated address.', type=str),
5497
5529
        Option('port',
5498
 
               help='Listen for connections on nominated port of the form '
5499
 
                    '[hostname:]portnumber.  Passing 0 as the port number will '
5500
 
                    'result in a dynamically allocated port.  The default port '
5501
 
                    'depends on the protocol.',
5502
 
               type=str),
 
5530
               help='Listen for connections on nominated port.  Passing 0 as '
 
5531
                    'the port number will result in a dynamically allocated '
 
5532
                    'port.  The default port depends on the protocol.',
 
5533
               type=int),
5503
5534
        custom_help('directory',
5504
5535
               help='Serve contents of this directory.'),
5505
5536
        Option('allow-writes',
5515
5546
               help='Override the default idle client timeout (5min).'),
5516
5547
        ]
5517
5548
 
5518
 
    def get_host_and_port(self, port):
5519
 
        """Return the host and port to run the smart server on.
5520
 
 
5521
 
        If 'port' is None, None will be returned for the host and port.
5522
 
 
5523
 
        If 'port' has a colon in it, the string before the colon will be
5524
 
        interpreted as the host.
5525
 
 
5526
 
        :param port: A string of the port to run the server on.
5527
 
        :return: A tuple of (host, port), where 'host' is a host name or IP,
5528
 
            and port is an integer TCP/IP port.
5529
 
        """
5530
 
        host = None
5531
 
        if port is not None:
5532
 
            if ':' in port:
5533
 
                host, port = port.split(':')
5534
 
            port = int(port)
5535
 
        return host, port
5536
 
 
5537
 
    def run(self, port=None, inet=False, directory=None, allow_writes=False,
5538
 
            protocol=None, client_timeout=None):
 
5549
    def run(self, listen=None, port=None, inet=False, directory=None,
 
5550
            allow_writes=False, protocol=None, client_timeout=None):
5539
5551
        from bzrlib import transport
5540
5552
        if directory is None:
5541
5553
            directory = os.getcwd()
5542
5554
        if protocol is None:
5543
5555
            protocol = transport.transport_server_registry.get()
5544
 
        host, port = self.get_host_and_port(port)
5545
5556
        url = transport.location_to_url(directory)
5546
5557
        if not allow_writes:
5547
5558
            url = 'readonly+' + url
5548
5559
        t = transport.get_transport_from_url(url)
5549
 
        try:
5550
 
            protocol(t, host, port, inet, client_timeout)
5551
 
        except TypeError, e:
5552
 
            # We use symbol_versioning.deprecated_in just so that people
5553
 
            # grepping can find it here.
5554
 
            # symbol_versioning.deprecated_in((2, 5, 0))
5555
 
            symbol_versioning.warn(
5556
 
                'Got TypeError(%s)\ntrying to call protocol: %s.%s\n'
5557
 
                'Most likely it needs to be updated to support a'
5558
 
                ' "timeout" parameter (added in bzr 2.5.0)'
5559
 
                % (e, protocol.__module__, protocol),
5560
 
                DeprecationWarning)
5561
 
            protocol(t, host, port, inet)
 
5560
        protocol(t, listen, port, inet, client_timeout)
5562
5561
 
5563
5562
 
5564
5563
class cmd_join(Command):
5700
5699
        if public_branch is None:
5701
5700
            public_branch = stored_public_branch
5702
5701
        elif stored_public_branch is None:
 
5702
            # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5703
5703
            branch.set_public_branch(public_branch)
5704
5704
        if not include_bundle and public_branch is None:
5705
5705
            raise errors.BzrCommandError(gettext('No public branch specified or'
6265
6265
                 possible_transports=possible_transports,
6266
6266
                 source_branch=branch).open_branch()
6267
6267
        else:
6268
 
            to_branch = lookup_sibling_branch(control_dir, to_location)
 
6268
            try:
 
6269
                to_branch = Branch.open(to_location,
 
6270
                    possible_transports=possible_transports)
 
6271
            except errors.NotBranchError:
 
6272
                to_branch = open_sibling_branch(control_dir, to_location,
 
6273
                    possible_transports=possible_transports)
6269
6274
        if revision is not None:
6270
6275
            revision = revision.as_revision_id(to_branch)
6271
6276
        switch.switch(control_dir, to_branch, force, revision_id=revision)
6468
6473
 
6469
6474
    takes_args = ["location?"]
6470
6475
 
 
6476
    takes_options = ['directory',
 
6477
        Option('force', help='Remove branch even if it is the active branch.')]
 
6478
 
6471
6479
    aliases = ["rmbranch"]
6472
6480
 
6473
 
    def run(self, location=None):
6474
 
        if location is None:
6475
 
            location = "."
6476
 
        cdir = controldir.ControlDir.open_containing(location)[0]
6477
 
        cdir.destroy_branch()
 
6481
    def run(self, directory=None, location=None, force=False):
 
6482
        br = open_nearby_branch(near=directory, location=location)
 
6483
        if not force and br.bzrdir.has_workingtree():
 
6484
            try:
 
6485
                active_branch = br.bzrdir.open_branch(name="")
 
6486
            except errors.NotBranchError:
 
6487
                active_branch = None
 
6488
            if (active_branch is not None and
 
6489
                br.control_url == active_branch.control_url):
 
6490
                raise errors.BzrCommandError(
 
6491
                    gettext("Branch is active. Use --force to remove it."))
 
6492
        br.bzrdir.destroy_branch(br.name)
6478
6493
 
6479
6494
 
6480
6495
class cmd_shelve(Command):
6710
6725
        ('cmd_resolve', ['resolved'], 'bzrlib.conflicts'),
6711
6726
        ('cmd_conflicts', [], 'bzrlib.conflicts'),
6712
6727
        ('cmd_sign_my_commits', [], 'bzrlib.commit_signature_commands'),
6713
 
        ('cmd_verify_signatures', [],
6714
 
                                        'bzrlib.commit_signature_commands'),
 
6728
        ('cmd_verify_signatures', [], 'bzrlib.commit_signature_commands'),
6715
6729
        ('cmd_test_script', [], 'bzrlib.cmd_test_script'),
6716
6730
        ]:
6717
6731
        builtin_command_registry.register_lazy(name, aliases, module_name)