~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

(vila) Rename assertWarns in test_config to avoid clashing with unittest2
 assertWarns. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
81
81
from bzrlib.decorators import needs_write_lock
82
82
from bzrlib.lazy_import import lazy_import
83
83
lazy_import(globals(), """
 
84
import base64
84
85
import fnmatch
85
86
import re
86
87
 
88
89
    atomicfile,
89
90
    controldir,
90
91
    debug,
 
92
    directory_service,
91
93
    errors,
92
94
    lazy_regex,
93
95
    library_state,
195
197
        return self[section][name]
196
198
 
197
199
 
198
 
# FIXME: Until we can guarantee that each config file is loaded once and
199
 
# only once for a given bzrlib session, we don't want to re-read the file every
200
 
# time we query for an option so we cache the value (bad ! watch out for tests
201
 
# needing to restore the proper value). -- vila 20110219
202
 
_expand_default_value = None
203
 
def _get_expand_default_value():
204
 
    global _expand_default_value
205
 
    if _expand_default_value is not None:
206
 
        return _expand_default_value
207
 
    conf = GlobalConfig()
208
 
    # Note that we must not use None for the expand value below or we'll run
209
 
    # into infinite recursion. Using False really would be quite silly ;)
210
 
    expand = conf.get_user_option_as_bool('bzr.config.expand', expand=True)
211
 
    if expand is None:
212
 
        # This is an opt-in feature, you *really* need to clearly say you want
213
 
        # to activate it !
214
 
        expand = False
215
 
    _expand_default_value = expand
216
 
    return expand
217
 
 
218
 
 
219
200
class Config(object):
220
201
    """A configuration policy - what username, editor, gpg needs etc."""
221
202
 
226
207
        """Returns a unique ID for the config."""
227
208
        raise NotImplementedError(self.config_id)
228
209
 
229
 
    @deprecated_method(deprecated_in((2, 4, 0)))
230
 
    def get_editor(self):
231
 
        """Get the users pop up editor."""
232
 
        raise NotImplementedError
233
 
 
234
210
    def get_change_editor(self, old_tree, new_tree):
235
211
        from bzrlib import diff
236
212
        cmd = self._get_change_editor()
373
349
        """Template method to provide a user option."""
374
350
        return None
375
351
 
376
 
    def get_user_option(self, option_name, expand=None):
 
352
    def get_user_option(self, option_name, expand=True):
377
353
        """Get a generic option - no special process, no default.
378
354
 
379
355
        :param option_name: The queried option.
382
358
 
383
359
        :returns: The value of the option.
384
360
        """
385
 
        if expand is None:
386
 
            expand = _get_expand_default_value()
387
361
        value = self._get_user_option(option_name)
388
362
        if expand:
389
363
            if isinstance(value, list):
637
611
        for (oname, value, section, conf_id, parser) in self._get_options():
638
612
            if oname.startswith('bzr.mergetool.'):
639
613
                tool_name = oname[len('bzr.mergetool.'):]
640
 
                tools[tool_name] = self.get_user_option(oname)
 
614
                tools[tool_name] = self.get_user_option(oname, False)
641
615
        trace.mutter('loaded merge tools: %r' % tools)
642
616
        return tools
643
617
 
1079
1053
        conf._create_from_string(str_or_unicode, save)
1080
1054
        return conf
1081
1055
 
1082
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1083
 
    def get_editor(self):
1084
 
        return self._get_user_option('editor')
1085
 
 
1086
1056
    @needs_write_lock
1087
1057
    def set_user_option(self, option, value):
1088
1058
        """Save option and its value in the configuration."""
1518
1488
    """Return per-user configuration directory as unicode string
1519
1489
 
1520
1490
    By default this is %APPDATA%/bazaar/2.0 on Windows, ~/.bazaar on Mac OS X
1521
 
    and Linux.  On Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
 
1491
    and Linux.  On Mac OS X and Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
1522
1492
    that will be used instead.
1523
1493
 
1524
1494
    TODO: Global option --config-dir to override this.
1533
1503
        #                APPDATA, but hard to move. See bug 348640 for more.
1534
1504
        return osutils.pathjoin(base, 'bazaar', '2.0')
1535
1505
    if base is None:
1536
 
        # GZ 2012-02-01: What should OSX use instead of XDG if anything?
1537
 
        if sys.platform != 'darwin':
1538
 
            xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
1539
 
            if xdg_dir is None:
1540
 
                xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
1541
 
            xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
1542
 
            if osutils.isdir(xdg_dir):
1543
 
                trace.mutter(
1544
 
                    "Using configuration in XDG directory %s." % xdg_dir)
1545
 
                return xdg_dir
 
1506
        xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
 
1507
        if xdg_dir is None:
 
1508
            xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
 
1509
        xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
 
1510
        if osutils.isdir(xdg_dir):
 
1511
            trace.mutter(
 
1512
                "Using configuration in XDG directory %s." % xdg_dir)
 
1513
            return xdg_dir
1546
1514
        base = osutils._get_home_dir()
1547
1515
    return osutils.pathjoin(base, ".bazaar")
1548
1516
 
1587
1555
def xdg_cache_dir():
1588
1556
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
1589
1557
    # Possibly this should be different on Windows?
1590
 
    e = os.environ.get('XDG_CACHE_DIR', None)
 
1558
    e = os.environ.get('XDG_CACHE_HOME', None)
1591
1559
    if e:
1592
1560
        return e
1593
1561
    else:
1594
1562
        return os.path.expanduser('~/.cache')
1595
1563
 
1596
1564
 
1597
 
def _get_default_mail_domain():
 
1565
def _get_default_mail_domain(mailname_file='/etc/mailname'):
1598
1566
    """If possible, return the assumed default email domain.
1599
1567
 
1600
1568
    :returns: string mail domain, or None.
1603
1571
        # No implementation yet; patches welcome
1604
1572
        return None
1605
1573
    try:
1606
 
        f = open('/etc/mailname')
 
1574
        f = open(mailname_file)
1607
1575
    except (IOError, OSError), e:
1608
1576
        return None
1609
1577
    try:
1610
 
        domain = f.read().strip()
 
1578
        domain = f.readline().strip()
1611
1579
        return domain
1612
1580
    finally:
1613
1581
        f.close()
2163
2131
credential_store_registry.default_key = 'plain'
2164
2132
 
2165
2133
 
 
2134
class Base64CredentialStore(CredentialStore):
 
2135
    __doc__ = """Base64 credential store for the authentication.conf file"""
 
2136
 
 
2137
    def decode_password(self, credentials):
 
2138
        """See CredentialStore.decode_password."""
 
2139
        # GZ 2012-07-28: Will raise binascii.Error if password is not base64,
 
2140
        #                should probably propogate as something more useful.
 
2141
        return base64.decodestring(credentials['password'])
 
2142
 
 
2143
credential_store_registry.register('base64', Base64CredentialStore,
 
2144
                                   help=Base64CredentialStore.__doc__)
 
2145
 
 
2146
 
2166
2147
class BzrDirConfig(object):
2167
2148
 
2168
2149
    def __init__(self, bzrdir):
2174
2155
 
2175
2156
        It may be set to a location, or None.
2176
2157
 
2177
 
        This policy affects all branches contained by this bzrdir, except for
2178
 
        those under repositories.
 
2158
        This policy affects all branches contained by this control dir, except
 
2159
        for those under repositories.
2179
2160
        """
2180
2161
        if self._config is None:
2181
 
            raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
 
2162
            raise errors.BzrError("Cannot set configuration in %s"
 
2163
                                  % self._bzrdir)
2182
2164
        if value is None:
2183
2165
            self._config.set_option('', 'default_stack_on')
2184
2166
        else:
2189
2171
 
2190
2172
        This will either be a location, or None.
2191
2173
 
2192
 
        This policy affects all branches contained by this bzrdir, except for
2193
 
        those under repositories.
 
2174
        This policy affects all branches contained by this control dir, except
 
2175
        for those under repositories.
2194
2176
        """
2195
2177
        if self._config is None:
2196
2178
            return None
2332
2314
        :param help: a doc string to explain the option to the user.
2333
2315
 
2334
2316
        :param from_unicode: a callable to convert the unicode string
2335
 
            representing the option value in a store. This is not called for
2336
 
            the default value.
 
2317
            representing the option value in a store or its default value.
2337
2318
 
2338
2319
        :param invalid: the action to be taken when an invalid value is
2339
2320
            encountered in a store. This is called only when from_unicode is
2430
2411
                value = self.default()
2431
2412
                if not isinstance(value, unicode):
2432
2413
                    raise AssertionError(
2433
 
                    'Callable default values should be unicode')
 
2414
                        "Callable default value for '%s' should be unicode"
 
2415
                        % (self.name))
2434
2416
            else:
2435
2417
                value = self.default
2436
2418
        return value
2437
2419
 
 
2420
    def get_help_topic(self):
 
2421
        return self.name
 
2422
 
2438
2423
    def get_help_text(self, additional_see_also=None, plain=True):
2439
2424
        result = self.help
2440
2425
        from bzrlib import help_topics
2486
2471
    return float(unicode_str)
2487
2472
 
2488
2473
 
2489
 
# Use a an empty dict to initialize an empty configobj avoiding all
2490
 
# parsing and encoding checks
 
2474
# Use an empty dict to initialize an empty configobj avoiding all parsing and
 
2475
# encoding checks
2491
2476
_list_converter_config = configobj.ConfigObj(
2492
2477
    {}, encoding='utf-8', list_values=True, interpolation=False)
2493
2478
 
2565
2550
        return "".join(ret)
2566
2551
 
2567
2552
 
 
2553
_option_ref_re = lazy_regex.lazy_compile('({[^\d\W](?:\.\w|\w)*})')
 
2554
"""Describes an expandable option reference.
 
2555
 
 
2556
We want to match the most embedded reference first.
 
2557
 
 
2558
I.e. for '{{foo}}' we will get '{foo}',
 
2559
for '{bar{baz}}' we will get '{baz}'
 
2560
"""
 
2561
 
 
2562
def iter_option_refs(string):
 
2563
    # Split isolate refs so every other chunk is a ref
 
2564
    is_ref = False
 
2565
    for chunk  in _option_ref_re.split(string):
 
2566
        yield is_ref, chunk
 
2567
        is_ref = not is_ref
 
2568
 
 
2569
 
2568
2570
class OptionRegistry(registry.Registry):
2569
2571
    """Register config options by their name.
2570
2572
 
2572
2574
    some information from the option object itself.
2573
2575
    """
2574
2576
 
 
2577
    def _check_option_name(self, option_name):
 
2578
        """Ensures an option name is valid.
 
2579
 
 
2580
        :param option_name: The name to validate.
 
2581
        """
 
2582
        if _option_ref_re.match('{%s}' % option_name) is None:
 
2583
            raise errors.IllegalOptionName(option_name)
 
2584
 
2575
2585
    def register(self, option):
2576
2586
        """Register a new option to its name.
2577
2587
 
2578
2588
        :param option: The option to register. Its name is used as the key.
2579
2589
        """
 
2590
        self._check_option_name(option.name)
2580
2591
        super(OptionRegistry, self).register(option.name, option,
2581
2592
                                             help=option.help)
2582
2593
 
2591
2602
        :param member_name: the member of the module to return.  If empty or 
2592
2603
                None, get() will return the module itself.
2593
2604
        """
 
2605
        self._check_option_name(key)
2594
2606
        super(OptionRegistry, self).register_lazy(key,
2595
2607
                                                  module_name, member_name)
2596
2608
 
2815
2827
 
2816
2828
Each function takes branch, rev_id as parameters.
2817
2829
'''))
 
2830
option_registry.register_lazy('progress_bar', 'bzrlib.ui.text',
 
2831
                              'opt_progress_bar')
2818
2832
option_registry.register(
2819
2833
    Option('public_branch',
2820
2834
           default=None,
2957
2971
        self.options[name] = value
2958
2972
 
2959
2973
    def remove(self, name):
2960
 
        if name not in self.orig:
 
2974
        if name not in self.orig and name in self.options:
2961
2975
            self.orig[name] = self.get(name, None)
2962
2976
        del self.options[name]
2963
2977
 
2985
2999
            # Report concurrent updates in an ad-hoc way. This should only
2986
3000
            # occurs when different processes try to update the same option
2987
3001
            # which is not supported (as in: the config framework is not meant
2988
 
            # to be used a sharing mechanism).
 
3002
            # to be used as a sharing mechanism).
2989
3003
            if expected != reloaded:
2990
3004
                if actual is _DeletedOption:
2991
3005
                    actual = '<DELETED>'
3011
3025
    mutable_section_class = MutableSection
3012
3026
 
3013
3027
    def __init__(self):
3014
 
        # Which sections need to be saved
3015
 
        self.dirty_sections = []
 
3028
        # Which sections need to be saved (by section id). We use a dict here
 
3029
        # so the dirty sections can be shared by multiple callers.
 
3030
        self.dirty_sections = {}
3016
3031
 
3017
3032
    def is_loaded(self):
3018
3033
        """Returns True if the Store has been loaded.
3061
3076
        raise NotImplementedError(self.save)
3062
3077
 
3063
3078
    def _need_saving(self):
3064
 
        for s in self.dirty_sections:
 
3079
        for s in self.dirty_sections.values():
3065
3080
            if s.orig:
3066
3081
                # At least one dirty section contains a modification
3067
3082
                return True
3081
3096
        # get_mutable_section() call below.
3082
3097
        self.unload()
3083
3098
        # Apply the changes from the preserved dirty sections
3084
 
        for dirty in dirty_sections:
3085
 
            clean = self.get_mutable_section(dirty.id)
 
3099
        for section_id, dirty in dirty_sections.iteritems():
 
3100
            clean = self.get_mutable_section(section_id)
3086
3101
            clean.apply_changes(dirty, self)
3087
3102
        # Everything is clean now
3088
 
        self.dirty_sections = []
 
3103
        self.dirty_sections = {}
3089
3104
 
3090
3105
    def save_changes(self):
3091
3106
        """Saves the Store to persistent storage if changes occurred.
3171
3186
 
3172
3187
    def unload(self):
3173
3188
        self._config_obj = None
3174
 
        self.dirty_sections = []
 
3189
        self.dirty_sections = {}
3175
3190
 
3176
3191
    def _load_content(self):
3177
3192
        """Load the config file bytes.
3225
3240
        if not self._need_saving():
3226
3241
            return
3227
3242
        # Preserve the current version
3228
 
        current = self._config_obj
3229
 
        dirty_sections = list(self.dirty_sections)
 
3243
        dirty_sections = dict(self.dirty_sections.items())
3230
3244
        self.apply_changes(dirty_sections)
3231
3245
        # Save to the persistent storage
3232
3246
        self.save()
3267
3281
        except errors.NoSuchFile:
3268
3282
            # The file doesn't exist, let's pretend it was empty
3269
3283
            self._load_from_string('')
 
3284
        if section_id in self.dirty_sections:
 
3285
            # We already created a mutable section for this id
 
3286
            return self.dirty_sections[section_id]
3270
3287
        if section_id is None:
3271
3288
            section = self._config_obj
3272
3289
        else:
3273
3290
            section = self._config_obj.setdefault(section_id, {})
3274
3291
        mutable_section = self.mutable_section_class(section_id, section)
3275
3292
        # All mutable sections can become dirty
3276
 
        self.dirty_sections.append(mutable_section)
 
3293
        self.dirty_sections[section_id] = mutable_section
3277
3294
        return mutable_section
3278
3295
 
3279
3296
    def quote(self, value):
3298
3315
        # anyway.
3299
3316
        return 'In-Process Store, no URL'
3300
3317
 
 
3318
 
3301
3319
class TransportIniFileStore(IniFileStore):
3302
3320
    """IniFileStore that loads files from a transport.
3303
3321
 
3393
3411
# on the relevant parts of the API that needs testing -- vila 20110503 (based
3394
3412
# on a poolie's remark)
3395
3413
class GlobalStore(LockableIniFileStore):
 
3414
    """A config store for global options.
 
3415
 
 
3416
    There is a single GlobalStore for a given process.
 
3417
    """
3396
3418
 
3397
3419
    def __init__(self, possible_transports=None):
3398
3420
        t = transport.get_transport_from_path(
3402
3424
 
3403
3425
 
3404
3426
class LocationStore(LockableIniFileStore):
 
3427
    """A config store for options specific to a location.
 
3428
 
 
3429
    There is a single LocationStore for a given process.
 
3430
    """
3405
3431
 
3406
3432
    def __init__(self, possible_transports=None):
3407
3433
        t = transport.get_transport_from_path(
3411
3437
 
3412
3438
 
3413
3439
class BranchStore(TransportIniFileStore):
 
3440
    """A config store for branch options.
 
3441
 
 
3442
    There is a single BranchStore for a given branch.
 
3443
    """
3414
3444
 
3415
3445
    def __init__(self, branch):
3416
3446
        super(BranchStore, self).__init__(branch.control_transport,
3469
3499
 
3470
3500
class LocationSection(Section):
3471
3501
 
3472
 
    def __init__(self, section, extra_path):
 
3502
    def __init__(self, section, extra_path, branch_name=None):
3473
3503
        super(LocationSection, self).__init__(section.id, section.options)
3474
3504
        self.extra_path = extra_path
 
3505
        if branch_name is None:
 
3506
            branch_name = ''
3475
3507
        self.locals = {'relpath': extra_path,
3476
 
                       'basename': urlutils.basename(extra_path)}
 
3508
                       'basename': urlutils.basename(extra_path),
 
3509
                       'branchname': branch_name}
3477
3510
 
3478
3511
    def get(self, name, default=None, expand=True):
3479
3512
        value = super(LocationSection, self).get(name, default)
3526
3559
        """
3527
3560
        location_parts = self.location.rstrip('/').split('/')
3528
3561
        store = self.store
3529
 
        sections = []
3530
3562
        # Later sections are more specific, they should be returned first
3531
3563
        for _, section in reversed(list(store.get_sections())):
3532
3564
            if section.id is None:
3549
3581
 
3550
3582
    def __init__(self, store, location):
3551
3583
        super(LocationMatcher, self).__init__(store)
 
3584
        url, params = urlutils.split_segment_parameters(location)
3552
3585
        if location.startswith('file://'):
3553
3586
            location = urlutils.local_path_from_url(location)
3554
3587
        self.location = location
 
3588
        branch_name = params.get('branch')
 
3589
        if branch_name is None:
 
3590
            self.branch_name = urlutils.basename(self.location)
 
3591
        else:
 
3592
            self.branch_name = urlutils.unescape(branch_name)
3555
3593
 
3556
3594
    def _get_matching_sections(self):
3557
3595
        """Get all sections matching ``location``."""
3583
3621
            while True:
3584
3622
                section = iter_all_sections.next()
3585
3623
                if section_id == section.id:
3586
 
                    matching_sections.append(
3587
 
                        (length, LocationSection(section, extra_path)))
 
3624
                    section = LocationSection(section, extra_path,
 
3625
                                              self.branch_name)
 
3626
                    matching_sections.append((length, section))
3588
3627
                    break
3589
3628
        return matching_sections
3590
3629
 
3607
3646
            yield self.store, section
3608
3647
 
3609
3648
 
3610
 
_option_ref_re = lazy_regex.lazy_compile('({[^{}\n]+})')
3611
 
"""Describes an expandable option reference.
3612
 
 
3613
 
We want to match the most embedded reference first.
3614
 
 
3615
 
I.e. for '{{foo}}' we will get '{foo}',
3616
 
for '{bar{baz}}' we will get '{baz}'
3617
 
"""
3618
 
 
3619
 
def iter_option_refs(string):
3620
 
    # Split isolate refs so every other chunk is a ref
3621
 
    is_ref = False
3622
 
    for chunk  in _option_ref_re.split(string):
3623
 
        yield is_ref, chunk
3624
 
        is_ref = not is_ref
3625
 
 
 
3649
# FIXME: _shared_stores should be an attribute of a library state once a
 
3650
# library_state object is always available.
 
3651
_shared_stores = {}
 
3652
_shared_stores_at_exit_installed = False
3626
3653
 
3627
3654
class Stack(object):
3628
3655
    """A stack of configurations where an option can be defined"""
3655
3682
            for store, section in sections():
3656
3683
                yield store, section
3657
3684
 
3658
 
    def get(self, name, expand=None, convert=True):
 
3685
    def get(self, name, expand=True, convert=True):
3659
3686
        """Return the *first* option value found in the sections.
3660
3687
 
3661
3688
        This is where we guarantee that sections coming from Store are loaded
3674
3701
        :returns: The value of the option.
3675
3702
        """
3676
3703
        # FIXME: No caching of options nor sections yet -- vila 20110503
3677
 
        if expand is None:
3678
 
            expand = _get_expand_default_value()
3679
3704
        value = None
3680
3705
        found_store = None # Where the option value has been found
3681
3706
        # If the option is registered, it may provide additional info about
3819
3844
        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3820
3845
 
3821
3846
    def _get_overrides(self):
3822
 
        # Hack around library_state.initialize never called
 
3847
        # FIXME: Hack around library_state.initialize never called
3823
3848
        if bzrlib.global_state is not None:
3824
3849
            return bzrlib.global_state.cmdline_overrides.get_sections()
3825
3850
        return []
3826
3851
 
 
3852
    def get_shared_store(self, store, state=None):
 
3853
        """Get a known shared store.
 
3854
 
 
3855
        Store urls uniquely identify them and are used to ensure a single copy
 
3856
        is shared across all users.
 
3857
 
 
3858
        :param store: The store known to the caller.
 
3859
 
 
3860
        :param state: The library state where the known stores are kept.
 
3861
 
 
3862
        :returns: The store received if it's not a known one, an already known
 
3863
            otherwise.
 
3864
        """
 
3865
        if state is None:
 
3866
            state = bzrlib.global_state
 
3867
        if state is None:
 
3868
            global _shared_stores_at_exit_installed
 
3869
            stores = _shared_stores
 
3870
            def save_config_changes():
 
3871
                for k, store in stores.iteritems():
 
3872
                    store.save_changes()
 
3873
            if not _shared_stores_at_exit_installed:
 
3874
                # FIXME: Ugly hack waiting for library_state to always be
 
3875
                # available. -- vila 20120731
 
3876
                import atexit
 
3877
                atexit.register(save_config_changes)
 
3878
                _shared_stores_at_exit_installed = True
 
3879
        else:
 
3880
            stores = state.config_stores
 
3881
        url = store.external_url()
 
3882
        try:
 
3883
            return stores[url]
 
3884
        except KeyError:
 
3885
            stores[url] = store
 
3886
            return store
 
3887
 
3827
3888
 
3828
3889
class MemoryStack(Stack):
3829
3890
    """A configuration stack defined from a string.
3879
3940
        self.store.save()
3880
3941
 
3881
3942
 
3882
 
class GlobalStack(_CompatibleStack):
 
3943
class GlobalStack(Stack):
3883
3944
    """Global options only stack.
3884
3945
 
3885
3946
    The following sections are queried:
3893
3954
    """
3894
3955
 
3895
3956
    def __init__(self):
3896
 
        gstore = GlobalStore()
 
3957
        gstore = self.get_shared_store(GlobalStore())
3897
3958
        super(GlobalStack, self).__init__(
3898
3959
            [self._get_overrides,
3899
3960
             NameMatcher(gstore, 'DEFAULT').get_sections],
3900
3961
            gstore, mutable_section_id='DEFAULT')
3901
3962
 
3902
3963
 
3903
 
class LocationStack(_CompatibleStack):
 
3964
class LocationStack(Stack):
3904
3965
    """Per-location options falling back to global options stack.
3905
3966
 
3906
3967
 
3922
3983
        """Make a new stack for a location and global configuration.
3923
3984
 
3924
3985
        :param location: A URL prefix to """
3925
 
        lstore = LocationStore()
 
3986
        lstore = self.get_shared_store(LocationStore())
3926
3987
        if location.startswith('file://'):
3927
3988
            location = urlutils.local_path_from_url(location)
3928
 
        gstore = GlobalStore()
 
3989
        gstore = self.get_shared_store(GlobalStore())
3929
3990
        super(LocationStack, self).__init__(
3930
3991
            [self._get_overrides,
3931
3992
             LocationMatcher(lstore, location).get_sections,
3953
4014
    """
3954
4015
 
3955
4016
    def __init__(self, branch):
3956
 
        lstore = LocationStore()
 
4017
        lstore = self.get_shared_store(LocationStore())
3957
4018
        bstore = branch._get_config_store()
3958
 
        gstore = GlobalStore()
 
4019
        gstore = self.get_shared_store(GlobalStore())
3959
4020
        super(BranchStack, self).__init__(
3960
4021
            [self._get_overrides,
3961
4022
             LocationMatcher(lstore, branch.base).get_sections,
3983
4044
        # unlock saves all the changes.
3984
4045
 
3985
4046
 
3986
 
class RemoteControlStack(_CompatibleStack):
 
4047
class RemoteControlStack(Stack):
3987
4048
    """Remote control-only options stack."""
3988
4049
 
3989
4050
    # FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion
4034
4095
class cmd_config(commands.Command):
4035
4096
    __doc__ = """Display, set or remove a configuration option.
4036
4097
 
4037
 
    Display the active value for a given option.
 
4098
    Display the active value for option NAME.
4038
4099
 
4039
4100
    If --all is specified, NAME is interpreted as a regular expression and all
4040
 
    matching options are displayed mentioning their scope. The active value
4041
 
    that bzr will take into account is the first one displayed for each option.
4042
 
 
4043
 
    If no NAME is given, --all .* is implied.
4044
 
 
4045
 
    Setting a value is achieved by using name=value without spaces. The value
 
4101
    matching options are displayed mentioning their scope and without resolving
 
4102
    option references in the value). The active value that bzr will take into
 
4103
    account is the first one displayed for each option.
 
4104
 
 
4105
    If NAME is not given, --all .* is implied (all options are displayed for the
 
4106
    current scope).
 
4107
 
 
4108
    Setting a value is achieved by using NAME=value without spaces. The value
4046
4109
    is set in the most relevant scope and can be checked by displaying the
4047
4110
    option again.
 
4111
 
 
4112
    Removing a value is achieved by using --remove NAME.
4048
4113
    """
4049
4114
 
4050
4115
    takes_args = ['name?']
4071
4136
            remove=False):
4072
4137
        if directory is None:
4073
4138
            directory = '.'
 
4139
        directory = directory_service.directories.dereference(directory)
4074
4140
        directory = urlutils.normalize_url(directory)
4075
4141
        if remove and all:
4076
4142
            raise errors.BzrError(
4179
4245
    def _set_config_option(self, name, value, directory, scope):
4180
4246
        conf = self._get_stack(directory, scope, write_access=True)
4181
4247
        conf.set(name, value)
 
4248
        # Explicitly save the changes
 
4249
        conf.store.save_changes()
4182
4250
 
4183
4251
    def _remove_config_option(self, name, directory, scope):
4184
4252
        if name is None:
4187
4255
        conf = self._get_stack(directory, scope, write_access=True)
4188
4256
        try:
4189
4257
            conf.remove(name)
 
4258
            # Explicitly save the changes
 
4259
            conf.store.save_changes()
4190
4260
        except KeyError:
4191
4261
            raise errors.NoSuchConfigOption(name)
4192
4262