Pure Python implementation of the Git version control system providing comprehensive access to Git repositories without requiring the Git command-line tool
—
Comprehensive Git configuration file parsing and manipulation supporting repository, user, and system-level configuration with hierarchical stacking, type conversion, multi-value support, URL rewriting, and advanced pattern matching for includeIf directives.
Classes for reading and writing Git configuration files with hierarchical support.
class Config:
"""Abstract base class for Git configuration."""
def get(self, section: SectionLike, name: NameLike) -> Value:
"""
Get configuration value.
Args:
section: Section name or tuple with section and optional subsection
name: Configuration key name
Returns:
Configuration value as bytes
Raises:
KeyError: If the value is not set
"""
def get_multivar(self, section: SectionLike, name: NameLike) -> Iterator[Value]:
"""
Get multiple values for a configuration key.
Args:
section: Section name or tuple with section and optional subsection
name: Configuration key name
Returns:
Iterator over configuration values
Raises:
KeyError: If the value is not set
"""
def get_boolean(
self,
section: SectionLike,
name: NameLike,
default: Optional[bool] = None
) -> Optional[bool]:
"""
Get configuration value as boolean.
Args:
section: Section name or tuple with section and optional subsection
name: Configuration key name
default: Default value if not found
Returns:
Boolean value or default
Raises:
ValueError: If value is not a valid boolean
"""
def set(
self,
section: SectionLike,
name: NameLike,
value: Union[ValueLike, bool]
) -> None:
"""
Set configuration value.
Args:
section: Section name or tuple with section and optional subsection
name: Configuration key name
value: Value to set
"""
def items(self, section: SectionLike) -> Iterator[Tuple[Name, Value]]:
"""
Iterate over items in a configuration section.
Args:
section: Section name or tuple with section and optional subsection
Yields:
Tuples of (name, value) pairs
"""
def sections(self) -> Iterator[Section]:
"""
Iterate over configuration sections.
Returns:
Iterator over section tuples
"""
def has_section(self, name: Section) -> bool:
"""
Check if a section exists.
Args:
name: Section name to check
Returns:
True if section exists
"""
class ConfigDict(Config):
"""Git configuration stored in a dictionary.
Provides in-memory configuration storage with case-insensitive
section names and support for multi-valued configuration keys.
"""
def __init__(
self,
values: Optional[MutableMapping[Section, MutableMapping[Name, Value]]] = None,
encoding: Optional[str] = None
):
"""
Initialize dictionary-based configuration.
Args:
values: Initial configuration values
encoding: Text encoding (defaults to system default)
"""
def __getitem__(self, key: Section) -> CaseInsensitiveOrderedMultiDict[Name, Value]:
"""Get section by name."""
def __setitem__(self, key: Section, value: MutableMapping[Name, Value]) -> None:
"""Set section by name."""
def __delitem__(self, key: Section) -> None:
"""Delete section by name."""
def __iter__(self) -> Iterator[Section]:
"""Iterate over section names."""
def __len__(self) -> int:
"""Number of sections."""
def keys(self) -> KeysView[Section]:
"""Get section names."""
class ConfigFile(ConfigDict):
"""File-based configuration implementation.
Reads and writes Git configuration files with proper parsing
of sections, subsections, and multi-line values.
"""
def __init__(
self,
filename: Optional[str] = None,
file: Optional[BinaryIO] = None,
encoding: Optional[str] = None
):
"""
Initialize file-based configuration.
Args:
filename: Path to configuration file
file: File-like object to read from
encoding: Text encoding (defaults to UTF-8)
"""
@classmethod
def from_path(cls, path: str) -> "ConfigFile":
"""
Create ConfigFile from file path.
Args:
path: Path to configuration file
Returns:
ConfigFile instance
"""
@classmethod
def from_file(cls, f: BinaryIO) -> "ConfigFile":
"""
Create ConfigFile from file object.
Args:
f: File-like object to read from
Returns:
ConfigFile instance
"""
def write_to_path(self, path: str) -> None:
"""
Write configuration to file path.
Args:
path: Output file path
"""
def write_to_file(self, f: BinaryIO) -> None:
"""
Write configuration to file-like object.
Args:
f: File-like object to write to
"""
def write(self) -> None:
"""
Write configuration back to original file.
"""
class StackedConfig(Config):
"""Configuration which reads from multiple config files.
Provides layered configuration where values are looked up
in order from multiple configuration sources (local, global, system).
"""
def __init__(
self,
backends: List[ConfigFile],
writable: Optional[ConfigFile] = None
):
"""
Initialize stacked configuration.
Args:
backends: List of configuration files (searched in order)
writable: Configuration file for write operations
"""
@classmethod
def default(cls) -> "StackedConfig":
"""
Create default stacked configuration.
Returns:
StackedConfig with system, global, and user configurations
"""
@classmethod
def default_backends(cls) -> List[ConfigFile]:
"""
Get default configuration backends.
Searches for configuration files in Git's standard locations:
- User config: ~/.gitconfig or $XDG_CONFIG_HOME/git/config
- System config: /etc/gitconfig (Unix) or system paths (Windows)
Respects GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM, and GIT_CONFIG_NOSYSTEM
environment variables.
Returns:
List of ConfigFile objects for found configuration files
"""Helper classes for configuration file handling.
class CaseInsensitiveOrderedMultiDict:
"""Case-insensitive dictionary supporting multiple values per key."""
def __init__(self): ...
def __getitem__(self, key: str) -> Any: ...
def __setitem__(self, key: str, value: Any) -> None: ...
def __contains__(self, key: str) -> bool: ...
def get(self, key: str, default=None) -> Any:
"""Get value with default."""
def getlist(self, key: str) -> List[Any]:
"""Get all values for key as list."""
def items(self) -> Iterator[Tuple[str, Any]]:
"""Iterate over key-value pairs."""Functions for reading and parsing Git submodule configuration.
def read_submodules(config_file) -> Iterator[Tuple[bytes, bytes, bytes]]:
"""
Read submodule configuration from .gitmodules file.
Args:
config_file: Configuration file object
Yields:
Tuples of (path, name, url)
"""
def parse_submodules(config: Config) -> Dict[bytes, Dict[bytes, bytes]]:
"""
Parse submodule configuration into structured format.
Args:
config: Configuration object
Returns:
Dictionary mapping submodule names to their configuration
"""Functions for handling Git URL rewriting configuration (insteadOf).
def iter_instead_of(config: Config, push: bool = False) -> Iterator[Tuple[bytes, bytes]]:
"""
Iterate over URL rewriting rules.
Args:
config: Configuration object
push: Whether to get pushInsteadOf rules
Yields:
Tuples of (original_prefix, replacement_prefix)
"""
def apply_instead_of(config: Config, url: bytes, push: bool = False) -> bytes:
"""
Apply URL rewriting rules to a URL.
Args:
config: Configuration object
url: Original URL
push: Whether to apply pushInsteadOf rules
Returns:
Rewritten URL
"""Functions for Git configuration pattern matching.
def match_glob_pattern(pattern: str, path: str) -> bool:
"""
Match path against Git glob pattern.
Args:
pattern: Glob pattern (supports *, ?, [])
path: Path to match
Returns:
True if path matches pattern
"""from dulwich.config import ConfigFile
# Read repository configuration
config = ConfigFile.from_path('.git/config')
# Get configuration values
user_name = config.get(('user',), 'name')
user_email = config.get(('user',), 'email')
print(f"User: {user_name.decode()} <{user_email.decode()}>")
# Set configuration values
config.set(('user',), 'name', 'John Doe')
config.set(('user',), 'email', 'john@example.com')
# Work with sections
for section in config.sections():
print(f"Section: {section}")
for name, value in config.items(section):
print(f" {name.decode()}: {value.decode()}")
# Write changes back to file
config.write() # Writes to original file
# Or write to different path
config.write_to_path('/tmp/config-backup')from dulwich.config import ConfigFile, StackedConfig
# Create stacked configuration (system -> global -> local)
system_config = ConfigFile('/etc/gitconfig')
global_config = ConfigFile('~/.gitconfig')
local_config = ConfigFile('.git/config')
stacked = StackedConfig([system_config, global_config, local_config],
writable=local_config)
# Get value (searches from local to system)
core_editor = stacked.get(b'core', b'editor')
# Set value (writes to writable backend)
stacked.set(b'user', b'name', b'Jane Doe'.encode())from dulwich.config import ConfigFile, parse_submodules
# Read .gitmodules file
gitmodules = ConfigFile('.gitmodules')
# Parse submodule configuration
submodules = parse_submodules(gitmodules)
for submodule_name, submodule_config in submodules.items():
path = submodule_config[b'path']
url = submodule_config[b'url']
print(f"Submodule {submodule_name.decode()}: {path.decode()} -> {url.decode()}")from dulwich.config import ConfigFile, apply_instead_of
# Configure URL rewriting
config = ConfigFile('.git/config')
config.set(b'url', b'https://github.com/', b'git@github.com:')
config.set(b'url "https://github.com/"', b'insteadOf', b'gh:')
# Apply rewriting
original_url = b'gh:user/repo.git'
rewritten_url = apply_instead_of(config, original_url)
print(f"Rewritten: {original_url.decode()} -> {rewritten_url.decode()}")from dulwich.config import ConfigFile
config = ConfigFile('.git/config')
# Set multiple values for same key
config.set(b'remote "origin"', b'fetch', b'+refs/heads/*:refs/remotes/origin/*')
config.set(b'remote "origin"', b'fetch', b'+refs/pull/*/head:refs/remotes/origin/pr/*')
# Get all values
fetch_specs = config.get_multivar(b'remote "origin"', b'fetch')
for spec in fetch_specs:
print(f"Fetch spec: {spec.decode()}")Install with Tessl CLI
npx tessl i tessl/pypi-dulwich