Repository: gitpython-developers/GitPython

Issue:
hasconfig:remote.*.url not supported by includeIf
the regex https://github.com/gitpython-developers/GitPython/blob/6d09bb610932d6081da7a516f8e7215414d953e5/git/config.py#L69 is missing `hasconfig:remote.*.url` from https://git-scm.com/docs/git-config#Documentation/git-config.txt-codehasconfigremoteurlcode which is covered in the git documentation.

Discussion:

Fix (patch):
diff --git a/git/config.py b/git/config.py
index 345290a39..200c81bb7 100644
--- a/git/config.py
+++ b/git/config.py
@@ -66,7 +66,7 @@
 CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository")
 """The configuration level of a configuration file."""
 
-CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch):(.+)\"")
+CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch|hasconfig:remote\.\*\.url):(.+)\"")
 """Section pattern to detect conditional includes.
 
 See: https://git-scm.com/docs/git-config#_conditional_includes
@@ -590,7 +590,11 @@ def _included_paths(self) -> List[Tuple[str, str]]:
 
                 if fnmatch.fnmatchcase(branch_name, value):
                     paths += self.items(section)
-
+            elif keyword == "hasconfig:remote.*.url":
+                for remote in self._repo.remotes:
+                    if fnmatch.fnmatchcase(remote.url, value):
+                        paths += self.items(section)
+                        break
         return paths
 
     def read(self) -> None:  # type: ignore[override]


Test changes:
diff --git a/test/test_config.py b/test/test_config.py
index 8e1007d9e..56ac0f304 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -373,6 +373,41 @@ def test_conditional_includes_from_branch_name_error(self, rw_dir):
             assert not config._has_includes()
             assert config._included_paths() == []
 
+    @with_rw_directory
+    def test_conditional_includes_remote_url(self, rw_dir):
+        # Initiate mocked repository.
+        repo = mock.Mock()
+        repo.remotes = [mock.Mock(url="https://github.com/foo/repo")]
+
+        # Initiate config files.
+        path1 = osp.join(rw_dir, "config1")
+        path2 = osp.join(rw_dir, "config2")
+        template = '[includeIf "hasconfig:remote.*.url:{}"]\n    path={}\n'
+
+        # Ensure that config with hasconfig and full url is correct.
+        with open(path1, "w") as stream:
+            stream.write(template.format("https://github.com/foo/repo", path2))
+
+        with GitConfigParser(path1, repo=repo) as config:
+            assert config._has_includes()
+            assert config._included_paths() == [("path", path2)]
+
+        # Ensure that config with hasconfig and incorrect url is incorrect.
+        with open(path1, "w") as stream:
+            stream.write(template.format("incorrect", path2))
+
+        with GitConfigParser(path1, repo=repo) as config:
+            assert not config._has_includes()
+            assert config._included_paths() == []
+
+        # Ensure that config with hasconfig and url using glob pattern is correct.
+        with open(path1, "w") as stream:
+            stream.write(template.format("**/**github.com*/**", path2))
+
+        with GitConfigParser(path1, repo=repo) as config:
+            assert config._has_includes()
+            assert config._included_paths() == [("path", path2)]
+
     def test_rename(self):
         file_obj = self._to_memcache(fixture_path("git_config"))
         with GitConfigParser(file_obj, read_only=False, merge_includes=False) as cw: