From 1551238cb52c78755632264fa0aa0bc750f89b0f Mon Sep 17 00:00:00 2001 From: Codex GPT-5 Date: Sun, 12 Jul 2026 14:35:09 +0200 Subject: [PATCH] fix: allow relative config paths with includes (#2103) GitConfigParser asserted while resolving a relative include whenever the top-level configuration file had itself been supplied as a relative path. A regression test constructs that combination and verifies the included value is available. Normalize path-based inputs to absolute paths before reading them. This lets relative includes resolve against the containing file and ensures cycle detection uses the same canonical spelling for top-level and included paths. This matches Git's documented include behavior and the relative and chained-relative coverage in t/t1305-config-include.sh. Validation: - uv run pytest -q test/test_config.py - uv run pre-commit run --files git/config.py test/test_config.py - uv run mypy git/config.py - git diff --check - full pytest: 631 passed, 75 skipped, 34 unrelated environment failures (uninitialized nested submodules and missing legacy master branch) --- git/config.py | 2 ++ test/test_config.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/git/config.py b/git/config.py index 82747eadd..64f501424 100644 --- a/git/config.py +++ b/git/config.py @@ -634,6 +634,8 @@ def read(self) -> None: # type: ignore[override] files_to_read = list(self._file_or_files) # END ensure we have a copy of the paths to handle + files_to_read = [osp.abspath(path) if isinstance(path, (str, os.PathLike)) else path for path in files_to_read] + seen = set(files_to_read) num_read_include_files = 0 while files_to_read: diff --git a/test/test_config.py b/test/test_config.py index 3ddaf0a4b..897caba4c 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -310,6 +310,20 @@ def check_test_value(cr, value): with GitConfigParser(fpa, read_only=True) as cr: check_test_value(cr, tv) + @with_rw_directory + def test_config_relative_path_include(self, rw_dir): + included_path = osp.join(rw_dir, "included") + with GitConfigParser(included_path, read_only=False) as cw: + cw.set_value("included", "value", "included") + + config_path = osp.join(rw_dir, "config") + with GitConfigParser(config_path, read_only=False) as cw: + cw.set_value("include", "path", "included") + + relative_config_path = osp.relpath(config_path) + with GitConfigParser(relative_config_path, read_only=True) as cr: + assert cr.get_value("included", "value") == "included" + @with_rw_directory def test_multiple_include_paths_with_same_key(self, rw_dir): """Test that multiple 'path' entries under [include] are all respected.