Skip to content

Commit 24b065e

Browse files
committed
Invalidate all cached version_info on refresh
For gitpython-developers#1829.
1 parent e3e5687 commit 24b065e

File tree

1 file changed

+22
-3
lines changed

1 file changed

+22
-3
lines changed

Diff for: git/cmd.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,18 @@ class Git:
306306
"cat_file_all",
307307
"cat_file_header",
308308
"_version_info",
309+
"_version_info_token",
309310
"_git_options",
310311
"_persistent_git_options",
311312
"_environment",
312313
)
313314

314-
_excluded_ = ("cat_file_all", "cat_file_header", "_version_info")
315+
_excluded_ = (
316+
"cat_file_all",
317+
"cat_file_header",
318+
"_version_info",
319+
"_version_info_token",
320+
)
315321

316322
re_unsafe_protocol = re.compile(r"(.+)::.+")
317323

@@ -358,6 +364,8 @@ def __setstate__(self, d: Dict[str, Any]) -> None:
358364
the top level ``__init__``.
359365
"""
360366

367+
_refresh_token = object() # Since None would match an initial _version_info_token.
368+
361369
@classmethod
362370
def refresh(cls, path: Union[None, PathLike] = None) -> bool:
363371
"""This gets called by the refresh function (see the top level __init__)."""
@@ -370,7 +378,9 @@ def refresh(cls, path: Union[None, PathLike] = None) -> bool:
370378

371379
# Keep track of the old and new git executable path.
372380
old_git = cls.GIT_PYTHON_GIT_EXECUTABLE
381+
old_refresh_token = cls._refresh_token
373382
cls.GIT_PYTHON_GIT_EXECUTABLE = new_git
383+
cls._refresh_token = object()
374384

375385
# Test if the new git executable path is valid. A GitCommandNotFound error is
376386
# spawned by us. A PermissionError is spawned if the git executable cannot be
@@ -399,6 +409,7 @@ def refresh(cls, path: Union[None, PathLike] = None) -> bool:
399409

400410
# Revert to whatever the old_git was.
401411
cls.GIT_PYTHON_GIT_EXECUTABLE = old_git
412+
cls._refresh_token = old_refresh_token
402413

403414
if old_git is None:
404415
# On the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is None) we only
@@ -782,8 +793,11 @@ def __init__(self, working_dir: Union[None, PathLike] = None):
782793
# Extra environment variables to pass to git commands
783794
self._environment: Dict[str, str] = {}
784795

785-
# Cached command slots
796+
# Cached version slots
786797
self._version_info: Union[Tuple[int, int, int, int], None] = None
798+
self._version_info_token: object = None
799+
800+
# Cached command slots
787801
self.cat_file_header: Union[None, TBD] = None
788802
self.cat_file_all: Union[None, TBD] = None
789803

@@ -824,7 +838,11 @@ def version_info(self) -> Tuple[int, int, int, int]:
824838
825839
This value is generated on demand and is cached.
826840
"""
827-
if self._version_info is None:
841+
refresh_token = self._refresh_token # Copy it, in case of a concurrent refresh.
842+
843+
# Ask git for its version if we haven't done so since the last refresh.
844+
# (Refreshing is global, but version information caching is per-instance.)
845+
if self._version_info_token is not refresh_token:
828846
# We only use the first 4 numbers, as everything else could be strings in fact (on Windows).
829847
process_version = self._call_process("version") # Should be as default *args and **kwargs used.
830848
version_numbers = process_version.split(" ")[2]
@@ -833,6 +851,7 @@ def version_info(self) -> Tuple[int, int, int, int]:
833851
Tuple[int, int, int, int],
834852
tuple(int(n) for n in version_numbers.split(".")[:4] if n.isdigit()),
835853
)
854+
self._version_info_token = refresh_token
836855

837856
return self._version_info
838857

0 commit comments

Comments
 (0)