class Config(object):
"""A Git configuration."""
+ def get(self, name):
+ """Retrieve the contents of a configuration setting.
+
+ :param name: Name of the setting, including section and
+ possible subsection.
+ :return: Contents of the setting
+ :raise KeyError: if the value is not set
+ """
+ raise NotImplementedError(self.get)
+
+ def set(self, name, value):
+ """Set a configuration value.
+
+ :param name: Name of the configuration value, including section
+ and optional subsection
+ :param: Value of the setting
+ """
+ raise NotImplementedError(self.set)
-class ConfigFile(Config):
- """A Git configuration file, like .git/config or ~/.gitconfig."""
+class ConfigDict(Config):
+ """Git configuration stored in a dictionary."""
def __init__(self):
- """Create a new ConfigFile."""
+ """Create a new ConfigDict."""
+ self._values = {}
def __eq__(self, other):
- return isinstance(other, self.__class__)
+ return (
+ isinstance(other, self.__class__) and
+ other._values == self._values)
+
+ @classmethod
+ def _parse_setting(cls, name):
+ parts = name.split(".")
+ if len(parts) == 3:
+ return (parts[0], parts[1], parts[2])
+ else:
+ return (parts[0], None, parts[1])
+
+ def get(self, name):
+ (section, subsection, variable) = self._parse_setting(name)
+ if subsection is not None:
+ try:
+ return self._values[section][subsection][variable]
+ except KeyError:
+ pass
+ return self._values[section][None][variable]
+
+ def set(self, name, value):
+ (section, subsection, variable) = self._parse_setting(name)
+ self._values.setdefault(section, {}).setdefault(subsection, {})[variable] = value
+
+
+class ConfigFile(ConfigDict):
+ """A Git configuration file, like .git/config or ~/.gitconfig.
+ """
@classmethod
def from_file(cls, f):
"""Read configuration from a file-like object."""
ret = cls()
- # FIXME
+ section = None
+ setting = None
+ for lineno, line in enumerate(f.readlines()):
+ line = line.lstrip()
+ if setting is None:
+ if line[0] == "[" and line.rstrip()[-1] == "]":
+ section = (line.strip()[1:-1], None)
+ ret._values[section[0]] = {section[1]: {}}
+ # FIXME: Parse section
+ elif "=" in line:
+ setting, value = line.split("=", 1)
+ if section is None:
+ raise ValueError("setting %r without section" % line)
+ setting = setting.strip()
+ ret._values[section[0]][section[1]][setting] = ""
+ else:
+ setting = line.strip()
+ value = True
+ if setting is not None:
+ if section is None:
+ raise ValueError("setting %r without section" % line)
+ ret._values[section[0]][section[1]][setting] += line
return ret
@classmethod
def write_to_file(self, f):
"""Write configuration to a file-like object."""
- # FIXME
+ for section_name, section in self._values.iteritems():
+ for subsection_name, subsection in section.iteritems():
+ if subsection_name is None:
+ f.write("[%s]\n" % section_name)
+ else:
+ f.write("[%s \"%s\"]\n" % (section_name, subsection_name))
+ for key, value in subsection.iteritems():
+ f.write("%s = %s\n" % (key, value))
class StackedConfig(Config):
continue
backends.append(cf)
return cls(backends)
+
+ def get(self, name):
+ for backend in self._backends:
+ try:
+ return backend.get(name)
+ except KeyError:
+ pass
+ raise KeyError(name)
+
+ def set(self, name, value):
+ raise NotImplementedError(self.set)
def _init_files(self, bare):
"""Initialize a default set of named files."""
+ from dulwich.config import ConfigFile
self._put_named_file('description', "Unnamed repository")
- self._put_named_file('config', ('[core]\n'
- 'repositoryformatversion = 0\n'
- 'filemode = true\n'
- 'bare = ' + str(bare).lower() + '\n'
- 'logallrefupdates = true\n'))
+ f = StringIO()
+ cf = ConfigFile()
+ cf.set("core.repositoryformatversion", "0")
+ cf.set("core.filemode", "true")
+ cf.set("core.bare", str(bare).lower())
+ cf.set("core.logallrefupdates", "true")
+ cf.write_to_file(f)
+ self._put_named_file('config', f.getvalue())
self._put_named_file(os.path.join('info', 'exclude'), '')
def get_named_file(self, path):
def get_config(self):
from dulwich.config import ConfigFile
try:
- p = ConfigFile.from_file(os.path.join(self._controldir, 'config'))
+ p = ConfigFile.from_path(os.path.join(self._controldir, 'config'))
except (IOError, OSError), e:
if e.errno == errno.ENOENT:
return ConfigFile()