Skip to content

RepositoryConfig.add_file: TypeError introduced in v1.21 might be too zealous #1508

Open

Description

@jorio

In pygit2 1.21, RepositoryConfig.add_file overrides Config.add_file to raise TypeError.

This new TypeError blocks the following real-world scenario that used to be possible in previous versions:

Say there's no XDG config file on the system when you create a Repository object.
If your application then creates ~/.config/git/config, the existing repo.config won't pick it up.
Pre-1.21, you used to be able to call repo.config.add_file(path, ConfigLevel.XDG, force=False) to solve this.

libgit2 itself doesn't prevent this, and it works fine in this scenario, so I believe this new restriction is artificial.

The only other alternative, as far as I know, would be to recreate the Repository object from scratch; or, to rewire RepositoryConfig.add_file to Config.add_file after importing libgit2. But neither of those solutions are really satisfactory.

What are your thoughts on removing this TypeError?

Activity

  1. jdavid commented on Nov 6, 2026

    @jdavid
    Member

    Kimi's review...


    In pygit2 1.21, RepositoryConfig.add_file raises TypeError, which blocks re-adding an XDG config file (~/.config/git/config) to an already-open repo.config when that file is created after the Repository is opened. The reporter says libgit2 itself allows this, so the restriction is artificial and should be removed.

    What I verified

    The facts are accurate. Pre-1.21, repo.config returned a plain Config whose add_file called git_config_add_file_ondisk directly, so the scenario worked. The TypeError was added in commit 9a4694a (PR #1501, "Refactor Config to extract DefaultConfig and RepositoryConfig subclasses"), released in 1.21, as groundwork for PR #1473's planned context-manager config overrides. It's a pure Python-layer override (pygit2/config.py:907-918), and libgit2 1.9.7 does accept the call — I confirmed the add succeeds and reads see the new values.

    But the restriction isn't purely artificial — it guards a real footgun I reproduced. After hot-adding a lower-level file (XDG, level 3) to a repository config, all subsequent writes through that object silently redirect from .git/config to the added file, contradicting RepositoryConfig's documented contract that "when a write operation occurs, only the local configuration is changed" (pygit2/config.py:825-826). Root cause in libgit2:

    • git_repository_config pins write targeting via git_config_set_writeorder(cfg, [LOCAL], 1), giving local write_order = 0 and every other backend -1 (ci/libgit2-1.9.7/src/libgit2/repository.c:1342-1344).
    • git_config__add_instance assigns any newly added backend write_order = level (config.c:308), and writers are sorted by write order descending — so the added XDG backend (3) outranks local (0) and becomes the sole write target.

    This hazard existed pre-1.21 too, but the 1.21 refactor drew an explicit semantic boundary (RepositoryConfig = effective config with local as the only write target), and the override enforces it.

    Also relevant to the reporter's workaround: libgit2 caches the opened config on the repository object (repo->_config, repository.c:1440), so re-accessing the repo.config property does not pick up a newly created XDG file — I verified a fresh repo.config still misses the new values. Recreating the Repository object genuinely is the only clean workaround today, as the reporter suspected.

    Assessment

    The use case is legitimate and libgit2 permits the operation, but simply deleting the TypeError would expose users to the silent write redirect. Reasonable resolutions:

    1. Allow add_file on RepositoryConfig but re-pin the write order afterward — git_config_set_writeorder is public API in libgit2 1.9 (include/git2/config.h:343), so pygit2 could call it with [ConfigLevel.LOCAL] after a successful add. This restores pre-1.21 capability while fixing the write-redirect bug that pre-1.21 had.
    2. Keep the TypeError and add a purpose-built reload API (e.g., re-open a given level's file), implemented with the same write-order fix.
    3. Treat the write redirect as a libgit2 bug and pursue it upstream — a backend added to a write-order-pinned config arguably shouldn't become the default writer.

    Note also that libgit2's config docs steer custom additions toward GIT_CONFIG_LEVEL_APP (include/git2/config.h:36-41), which is the design PR #1473's context-manager overrides will use — worth considering how that interacts with any add_file relaxation.

  2. jdavid commented on Nov 6, 2026

    @jdavid
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions