{"id":481,"library":"dulwich","title":"Dulwich","description":"Dulwich is a pure-Python implementation of Git, providing an interface to both local and remote Git repositories without relying on the `git` executable or native code like `pygit2`. It offers both lower-level \"plumbing\" and higher-level \"porcelain\" APIs for interacting with Git objects and operations. The current version is 1.1.0, and it generally releases new versions every few weeks to months.","status":"active","version":"1.1.0","language":"python","source_language":"en","source_url":"https://github.com/dulwich/dulwich","tags":["git","version control","vcs","pure-python"],"install":[{"cmd":"pip install dulwich","lang":"bash","label":"Standard installation"},{"cmd":"pip install --no-binary dulwich dulwich --config-settings \"--build-option=--pure\"","lang":"bash","label":"Install without optional Rust extensions"}],"dependencies":[],"imports":[{"symbol":"Repo","correct":"from dulwich.repo import Repo"},{"note":"For higher-level Git operations like clone, commit, log, etc.","symbol":"porcelain","correct":"from dulwich import porcelain"}],"quickstart":{"code":"import os\nfrom dulwich.repo import Repo\nfrom dulwich import porcelain\nfrom tempfile import TemporaryDirectory\n\n# Create a temporary directory for the repository\nwith TemporaryDirectory() as temp_dir:\n    repo_path = os.path.join(temp_dir, 'my_repo')\n\n    # Initialize a new repository\n    repo = porcelain.init(repo_path)\n    print(f\"Initialized repository at: {repo_path}\")\n\n    # Create a file\n    file_path = os.path.join(repo_path, 'README.md')\n    with open(file_path, 'w') as f:\n        f.write('# My Dulwich Repo\\n')\n    print(f\"Created file: {file_path}\")\n\n    # Add the file to the index and commit\n    porcelain.add(repo_path, ['README.md'])\n    porcelain.commit(repo_path, message=b'Initial commit: Add README')\n    print(\"Committed initial README.md\")\n\n    # Log the commit\n    for entry in porcelain.log(repo_path, max_entries=1):\n        print(f\"Latest commit: {entry.commit.message.decode().strip()}\")\n\n    # Example of low-level (plumbing) API to get commit message\n    low_level_repo = Repo(repo_path)\n    head_id = low_level_repo.head()\n    latest_commit = low_level_repo[head_id]\n    print(f\"Low-level API: Latest commit message: {latest_commit.message.decode().strip()}\")\n","lang":"python","description":"This quickstart demonstrates how to initialize a new Git repository, add a file, commit the changes using Dulwich's high-level 'porcelain' API, and then retrieve the latest commit message using its lower-level 'plumbing' API. It uses a temporary directory for a clean example."},"warnings":[{"fix":"Refer to the Dulwich documentation for the 1.0.0 release notes and replace usage of deprecated functions with their modern equivalents.","message":"Dulwich 1.0.0 removed several deprecated functions. Code relying on these removed functions will break.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Review calls to `dulwich.porcelain` functions. Ensure compatibility with the current API by referring to the official documentation. If directly importing from `dulwich.porcelain.*` submodules, verify the paths are still valid or use the top-level `dulwich.porcelain` module for re-exported functions.","message":"Version 0.25.0 introduced significant changes to public APIs, particularly the `dulwich.porcelain` module which was reorganized into submodules (e.g., `dulwich.porcelain.tags`, `dulwich.porcelain.notes`). While the main `dulwich.porcelain` module re-exports functions for backward compatibility, direct imports from old submodule paths or reliance on the previous internal structure may break in future releases, especially after 1.0.0.","severity":"breaking","affected_versions":">=0.25.0, <1.0.0 (API changes occurred), >=1.0.0 (removed deprecated functions)"},{"fix":"Ensure your Python environment is running Python 3.10 or a newer compatible version. Check `python --version`.","message":"Dulwich requires Python 3.10 or newer for recent versions. Running with older Python 3 versions (e.g., 3.9 or earlier) will lead to compatibility issues or errors.","severity":"gotcha","affected_versions":">=1.0.0 (and potentially earlier recent releases)"},{"fix":"For optimal performance, install Dulwich without the `--no-binary` option, allowing the optional Rust extensions to be built (requires a Rust toolchain). If using `--no-binary dulwich`, ensure `--config-settings \"--build-option=--pure\"` is *not* used unless slower performance is acceptable.","message":"While Dulwich is a pure-Python Git implementation, its performance for low-level operations can be significantly improved by installing with optional Rust bindings (formerly C extensions). Without these, operations might be noticeably slower.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T14:12:07.205Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Run `pip install dulwich` in your terminal or ensure it's included in your project's `requirements.txt` and installed within your virtual environment.","cause":"The 'dulwich' library is not installed in the Python environment where the code is being executed.","error":"ModuleNotFoundError: No module named 'dulwich'"},{"fix":"Provide valid credentials, either by embedding them in the URL (e.g., `https://username:password@example.com/repo.git`) or by passing `username` and `password` (or `auth_info` for more complex authentication) directly to `dulwich.porcelain.clone` or `dulwich.porcelain.push`.","cause":"Authentication failed when attempting to access a remote Git repository over HTTP/HTTPS, likely due to incorrect or missing username, password, or access token.","error":"dulwich.client.HTTPUnauthorized: No valid credentials provided."},{"fix":"Ensure the repository has been initialized with `dulwich.porcelain.init()` and has at least one commit. If it's an existing repository, check its integrity and ensure the `.git/HEAD` file and other references are valid.","cause":"This error typically occurs when `dulwich` attempts to read the 'HEAD' reference in a Git repository, but the repository is empty, corrupted, or not properly initialized, thus 'HEAD' does not exist or points to an invalid object.","error":"KeyError: b'HEAD'"},{"fix":"Use the higher-level `dulwich.porcelain.clone` or `dulwich.porcelain.push` functions, which are designed to handle `username` and `password` keyword arguments correctly for remote operations, or embed the credentials directly in the remote URL string.","cause":"You are passing 'username' and 'password' as direct keyword arguments to a lower-level `dulwich.client` constructor (e.g., `HttpGitClient.__init__`) which does not accept them directly, especially in older versions or when bypassing the recommended `dulwich.porcelain` functions.","error":"TypeError: __init__() got an unexpected keyword argument 'username'."}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":6.3,"disk_size":"24.0M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":6.3,"disk_size":"24.0M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":6.3,"disk_size":"24M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":6.3,"disk_size":"26M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":6,"disk_size":"27.4M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":6,"disk_size":"27.4M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":6,"disk_size":"28M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":6,"disk_size":"30M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.9,"disk_size":"18.5M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.9,"disk_size":"18.5M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":5.9,"disk_size":"19M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.9,"disk_size":"21M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":5.7,"disk_size":"18.1M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.7,"disk_size":"18.1M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":5.5,"disk_size":"19M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.5,"disk_size":"20M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":6.1,"disk_size":"22.7M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":6.1,"disk_size":"22.7M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":6.1,"disk_size":"23M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":6.1,"disk_size":"25M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}