|
@@ -97,23 +97,38 @@ class GitConfigStateBase(metaclass=abc.ABCMeta):
|
|
"""
|
|
"""
|
|
|
|
|
|
@abc.abstractmethod
|
|
@abc.abstractmethod
|
|
- def unset_config(self, key: str, *, scope: GitConfigScope):
|
|
|
|
|
|
+ def unset_config(self, key: str, *, scope: GitConfigScope,
|
|
|
|
+ missing_ok: bool):
|
|
"""When invoked, remove a singlar value from `key` in this state's underlying data.
|
|
"""When invoked, remove a singlar value from `key` in this state's underlying data.
|
|
|
|
+
|
|
|
|
+ If missing_ok is False and `key` is not present in the given scope, this
|
|
|
|
+ must raise GitConfigUnsetMissingValue with `key`.
|
|
"""
|
|
"""
|
|
|
|
|
|
@abc.abstractmethod
|
|
@abc.abstractmethod
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
- scope: GitConfigScope):
|
|
|
|
|
|
+ scope: GitConfigScope, missing_ok: bool):
|
|
"""When invoked, remove all values from `key` in this state's underlying data.
|
|
"""When invoked, remove all values from `key` in this state's underlying data.
|
|
|
|
|
|
If `value_pattern` is supplied, only values matching this pattern will
|
|
If `value_pattern` is supplied, only values matching this pattern will
|
|
be removed.
|
|
be removed.
|
|
|
|
|
|
|
|
+ If missing_ok is False and `key` is not present in the given scope, this
|
|
|
|
+ must raise GitConfigUnsetMissingValue with `key`.
|
|
|
|
+
|
|
TODO: Make value_pattern an re.Pattern. This wasn't done at the time of
|
|
TODO: Make value_pattern an re.Pattern. This wasn't done at the time of
|
|
this refactor to keep the refactor small.
|
|
this refactor to keep the refactor small.
|
|
"""
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
+class GitConfigUnsetMissingValue(ValueError):
|
|
|
|
+
|
|
|
|
+ def __init__(self, key: str, scope: str) -> None:
|
|
|
|
+ super().__init__(
|
|
|
|
+ f'Cannot unset missing key {key!r} in scope {scope!r} with missing_ok=False.'
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
class CachedGitConfigState(object):
|
|
class CachedGitConfigState(object):
|
|
"""This represents the observable git configuration state for a given
|
|
"""This represents the observable git configuration state for a given
|
|
repository (whose top-level path is `root`).
|
|
repository (whose top-level path is `root`).
|
|
@@ -210,7 +225,8 @@ class CachedGitConfigState(object):
|
|
missing_ok: If `value` is None (i.e. this is an unset operation),
|
|
missing_ok: If `value` is None (i.e. this is an unset operation),
|
|
ignore retcode=5 from `git config` (meaning that the value is
|
|
ignore retcode=5 from `git config` (meaning that the value is
|
|
not present). If `value` is not None, then this option has no
|
|
not present). If `value` is not None, then this option has no
|
|
- effect.
|
|
|
|
|
|
+ effect. If this is false and the key is missing, this will raise
|
|
|
|
+ GitConfigUnsetMissingValue.
|
|
modify_all: If True, this will change a set operation to
|
|
modify_all: If True, this will change a set operation to
|
|
`--replace-all`, and will change an unset operation to
|
|
`--replace-all`, and will change an unset operation to
|
|
`--unset-all`.
|
|
`--unset-all`.
|
|
@@ -223,25 +239,13 @@ class CachedGitConfigState(object):
|
|
`modify_all=False`.
|
|
`modify_all=False`.
|
|
"""
|
|
"""
|
|
if value is None:
|
|
if value is None:
|
|
- if key not in self._maybe_load_config():
|
|
|
|
- if missing_ok:
|
|
|
|
- # Implements early return - underlying state doesn't need to
|
|
|
|
- # be told to remove already-missing keys.
|
|
|
|
- #
|
|
|
|
- # For GitConfigStateReal this avoids lots of unnecessary
|
|
|
|
- # subprocess invocations to unset keys which don't exist.
|
|
|
|
- return
|
|
|
|
-
|
|
|
|
- raise ValueError(
|
|
|
|
- f'CachedGitConfigState: Cannot unset missing key {key!r}'
|
|
|
|
- ' with missing_ok=False.')
|
|
|
|
-
|
|
|
|
if modify_all:
|
|
if modify_all:
|
|
self._impl.unset_config_multi(key,
|
|
self._impl.unset_config_multi(key,
|
|
value_pattern=value_pattern,
|
|
value_pattern=value_pattern,
|
|
- scope=scope)
|
|
|
|
|
|
+ scope=scope,
|
|
|
|
+ missing_ok=missing_ok)
|
|
else:
|
|
else:
|
|
- self._impl.unset_config(key, scope=scope)
|
|
|
|
|
|
+ self._impl.unset_config(key, scope=scope, missing_ok=missing_ok)
|
|
else:
|
|
else:
|
|
if modify_all:
|
|
if modify_all:
|
|
self._impl.set_config_multi(key,
|
|
self._impl.set_config_multi(key,
|
|
@@ -299,15 +303,32 @@ class GitConfigStateReal(GitConfigStateBase):
|
|
args.append('--add')
|
|
args.append('--add')
|
|
GIT.Capture(args, cwd=self.root)
|
|
GIT.Capture(args, cwd=self.root)
|
|
|
|
|
|
- def unset_config(self, key: str, *, scope: GitConfigScope):
|
|
|
|
- GIT.Capture(['config', f'--{scope}', '--unset', key], cwd=self.root)
|
|
|
|
|
|
+ def unset_config(self, key: str, *, scope: GitConfigScope,
|
|
|
|
+ missing_ok: bool):
|
|
|
|
+ accepted_retcodes = (0, 5) if missing_ok else (0, )
|
|
|
|
+ try:
|
|
|
|
+ GIT.Capture(['config', f'--{scope}', '--unset', key],
|
|
|
|
+ cwd=self.root,
|
|
|
|
+ accepted_retcodes=accepted_retcodes)
|
|
|
|
+ except subprocess2.CalledProcessError as cpe:
|
|
|
|
+ if cpe.returncode == 5:
|
|
|
|
+ raise GitConfigUnsetMissingValue(key, scope)
|
|
|
|
+ raise
|
|
|
|
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
- scope: GitConfigScope):
|
|
|
|
|
|
+ scope: GitConfigScope, missing_ok: bool):
|
|
|
|
+ accepted_retcodes = (0, 5) if missing_ok else (0, )
|
|
args = ['config', f'--{scope}', '--unset-all', key]
|
|
args = ['config', f'--{scope}', '--unset-all', key]
|
|
if value_pattern is not None:
|
|
if value_pattern is not None:
|
|
args.append(value_pattern)
|
|
args.append(value_pattern)
|
|
- GIT.Capture(args, cwd=self.root)
|
|
|
|
|
|
+ try:
|
|
|
|
+ GIT.Capture(args,
|
|
|
|
+ cwd=self.root,
|
|
|
|
+ accepted_retcodes=accepted_retcodes)
|
|
|
|
+ except subprocess2.CalledProcessError as cpe:
|
|
|
|
+ if cpe.returncode == 5:
|
|
|
|
+ raise GitConfigUnsetMissingValue(key, scope)
|
|
|
|
+ raise
|
|
|
|
|
|
|
|
|
|
class GitConfigStateTest(GitConfigStateBase):
|
|
class GitConfigStateTest(GitConfigStateBase):
|
|
@@ -370,23 +391,33 @@ class GitConfigStateTest(GitConfigStateBase):
|
|
newval.append(value)
|
|
newval.append(value)
|
|
cfg[key] = newval
|
|
cfg[key] = newval
|
|
|
|
|
|
- def unset_config(self, key: str, *, scope: GitConfigScope):
|
|
|
|
|
|
+ def unset_config(self, key: str, *, scope: GitConfigScope,
|
|
|
|
+ missing_ok: bool):
|
|
cfg = self._get_scope(scope)
|
|
cfg = self._get_scope(scope)
|
|
cur = cfg.get(key)
|
|
cur = cfg.get(key)
|
|
- if cur is None or len(cur) == 1:
|
|
|
|
|
|
+ if cur is None:
|
|
|
|
+ if missing_ok:
|
|
|
|
+ return
|
|
|
|
+ raise GitConfigUnsetMissingValue(key, scope)
|
|
|
|
+ if len(cur) == 1:
|
|
del cfg[key]
|
|
del cfg[key]
|
|
return
|
|
return
|
|
raise ValueError(f'GitConfigStateTest: Cannot unset key {key} '
|
|
raise ValueError(f'GitConfigStateTest: Cannot unset key {key} '
|
|
f'- current value {cur!r} is multiple.')
|
|
f'- current value {cur!r} is multiple.')
|
|
|
|
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
def unset_config_multi(self, key: str, *, value_pattern: Optional[str],
|
|
- scope: GitConfigScope):
|
|
|
|
|
|
+ scope: GitConfigScope, missing_ok: bool):
|
|
cfg = self._get_scope(scope)
|
|
cfg = self._get_scope(scope)
|
|
|
|
+ cur = cfg.get(key)
|
|
|
|
+ if cur is None:
|
|
|
|
+ if not missing_ok:
|
|
|
|
+ raise GitConfigUnsetMissingValue(key, scope)
|
|
|
|
+ return
|
|
|
|
+
|
|
if value_pattern is None:
|
|
if value_pattern is None:
|
|
del cfg[key]
|
|
del cfg[key]
|
|
return
|
|
return
|
|
|
|
|
|
- cur = cfg.get(key)
|
|
|
|
if cur is None:
|
|
if cur is None:
|
|
del cfg[key]
|
|
del cfg[key]
|
|
return
|
|
return
|