From 77f74ce59338560fe158655ce4b41bddab527f07 Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Mon, 8 Jan 2024 19:21:47 -0600
Subject: [PATCH 1/7] initial patch to use git bash rather than WSL

---
 git/__init__.py  |   1 +
 git/cmd.py       | 174 ++++++++++++++++++++++++++++++++++++++++++++++-
 git/index/fun.py |   4 +-
 3 files changed, 175 insertions(+), 4 deletions(-)

diff --git a/git/__init__.py b/git/__init__.py
index c6a52ef30..e0b6d18b4 100644
--- a/git/__init__.py
+++ b/git/__init__.py
@@ -126,6 +126,7 @@ def refresh(path: Optional[PathLike] = None) -> None:
 
     if not Git.refresh(path=path):
         return
+    Git.refresh_bash()
     if not FetchInfo.refresh():  # noqa: F405
         return  # type: ignore [unreachable]
 
diff --git a/git/cmd.py b/git/cmd.py
index 9c94748c5..0a0d2e32a 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -11,12 +11,13 @@
 import logging
 import os
 import signal
-from subprocess import Popen, PIPE, DEVNULL
+from subprocess import Popen, PIPE, DEVNULL, run, CalledProcessError
 import subprocess
 import threading
 from textwrap import dedent
+from pathlib import Path
 
-from git.compat import defenc, force_bytes, safe_decode
+from git.compat import defenc, force_bytes, safe_decode, is_win
 from git.exc import (
     CommandError,
     GitCommandError,
@@ -305,6 +306,175 @@ def __setstate__(self, d: Dict[str, Any]) -> None:
     the top level ``__init__``.
     """
 
+    _bash_exec_env_var = "GIT_PYTHON_BASH_EXECUTABLE"
+
+    bash_exec_name = "bash"
+    """Default bash command that should work on Linux, Windows, and other systems."""
+
+    GIT_PYTHON_BASH_EXECUTABLE = None
+    """Provide the full path to the bash executable. Otherwise it assumes bash is in the path.
+
+    Note that the bash executable is actually found during the refresh step in
+    the top level ``__init__``.
+    """
+
+    @classmethod
+    def _get_default_bash_path(cls):
+        # Assumes that, if user is running in Windows, they probably are using
+        # Git for Windows, which includes Git BASH and should be associated
+        # with the configured Git command set in `refresh()`.  Regardless of
+        # if the Git command assumes it is installed in (root)/cmd/git.exe or
+        # (root)/bin/git.exe, the root is always up two levels from the git
+        # command.  Try going up to levels from the currently configured
+        # git command, then navigate to (root)/bin/bash.exe.  If this exists,
+        # prefer it over the WSL version in System32, direct access to which
+        # is reportedly deprecated.  Fail back to default "bash.exe" if
+        # the Git for Windows lookup doesn't work.
+        #
+        # This addresses issues where git hooks are intended to run assuming
+        # the "native" Windows environment as seen by git.exe rather than
+        # inside the git sandbox of WSL, which is likely configured
+        # independetly of the Windows Git.  A noteworthy example are repos with
+        # Git LFS, where Git LFS may be installed in Windows but not in WSL.
+        if not is_win:
+            return 'bash'
+        try:
+            wheregit = run(['where', Git.GIT_PYTHON_GIT_EXECUTABLE],
+                           check=True, stdout=PIPE).stdout
+        except CalledProcessError:
+            return 'bash.exe'
+        gitpath = Path(wheregit.decode(defenc).splitlines()[0])
+        gitroot = gitpath.parent.parent
+        gitbash = gitroot / 'bin' / 'bash.exe'
+        return str(gitbash) if gitbash.exists else 'bash.exe'
+
+    @classmethod
+    def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
+        """This gets called by the refresh function (see the top level __init__)."""
+        # Discern which path to refresh with.
+        if path is not None:
+            new_bash = os.path.expanduser(path)
+            new_bash = os.path.abspath(new_bash)
+        else:
+            new_bash = os.environ.get(cls._bash_exec_env_var)
+            if new_bash is None:
+                new_bash = cls._get_default_bash_path()
+
+        # Keep track of the old and new bash executable path.
+        old_bash = cls.GIT_PYTHON_BASH_EXECUTABLE
+        cls.GIT_PYTHON_BASH_EXECUTABLE = new_bash
+
+        # Test if the new git executable path is valid. A GitCommandNotFound error is
+        # spawned by us. A PermissionError is spawned if the git executable cannot be
+        # executed for whatever reason.
+        has_bash = False
+        try:
+            run([cls.GIT_PYTHON_BASH_EXECUTABLE, '--version'])
+            has_bash = True
+        except CalledProcessError:
+            pass
+
+        # Warn or raise exception if test failed.
+        if not has_bash:
+            err = (
+                dedent(
+                    f"""\
+                Bad bash executable.
+                The bash executable must be specified in one of the following ways:
+                    - be included in your $PATH
+                    - be set via ${cls._bash_exec_env_var}
+                    - explicitly set via git.refresh_bash()
+                """
+                )
+            )
+
+            # Revert to whatever the old_bash was.
+            cls.GIT_PYTHON_BASH_EXECUTABLE = old_bash
+
+            if old_bash is None:
+                # On the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is None) we only
+                # are quiet, warn, or error depending on the GIT_PYTHON_REFRESH value.
+
+                # Determine what the user wants to happen during the initial refresh we
+                # expect GIT_PYTHON_REFRESH to either be unset or be one of the
+                # following values:
+                #
+                #   0|q|quiet|s|silence|n|none
+                #   1|w|warn|warning
+                #   2|r|raise|e|error
+
+                mode = os.environ.get(cls._refresh_env_var, "raise").lower()
+
+                quiet = ["quiet", "q", "silence", "s", "none", "n", "0"]
+                warn = ["warn", "w", "warning", "1"]
+                error = ["error", "e", "raise", "r", "2"]
+
+                if mode in quiet:
+                    pass
+                elif mode in warn or mode in error:
+                    err = (
+                        dedent(
+                            """\
+                        %s
+                        All commit hook commands will error until this is rectified.
+
+                        This initial warning can be silenced or aggravated in the future by setting the
+                        $%s environment variable. Use one of the following values:
+                            - %s: for no warning or exception
+                            - %s: for a printed warning
+                            - %s: for a raised exception
+
+                        Example:
+                            export %s=%s
+                        """
+                        )
+                        % (
+                            err,
+                            cls._refresh_env_var,
+                            "|".join(quiet),
+                            "|".join(warn),
+                            "|".join(error),
+                            cls._refresh_env_var,
+                            quiet[0],
+                        )
+                    )
+
+                    if mode in warn:
+                        print("WARNING: %s" % err)
+                    else:
+                        raise ImportError(err)
+                else:
+                    err = (
+                        dedent(
+                            """\
+                        %s environment variable has been set but it has been set with an invalid value.
+
+                        Use only the following values:
+                            - %s: for no warning or exception
+                            - %s: for a printed warning
+                            - %s: for a raised exception
+                        """
+                        )
+                        % (
+                            cls._refresh_env_var,
+                            "|".join(quiet),
+                            "|".join(warn),
+                            "|".join(error),
+                        )
+                    )
+                    raise ImportError(err)
+
+                # We get here if this was the init refresh and the refresh mode was not
+                # error. Go ahead and set the GIT_PYTHON_BASH_EXECUTABLE such that we
+                # discern the difference between a first import and a second import.
+                cls.GIT_PYTHON_BASH_EXECUTABLE = cls.bash_exec_name
+            else:
+                # After the first refresh (when GIT_PYTHON_BASH_EXECUTABLE is no longer
+                # None) we raise an exception.
+                raise GitCommandNotFound("bash", err)
+
+        return has_bash
+
     @classmethod
     def refresh(cls, path: Union[None, PathLike] = None) -> bool:
         """This gets called by the refresh function (see the top level __init__)."""
diff --git a/git/index/fun.py b/git/index/fun.py
index 402f85d2b..c3ed4ab23 100644
--- a/git/index/fun.py
+++ b/git/index/fun.py
@@ -18,7 +18,7 @@
 )
 import subprocess
 
-from git.cmd import PROC_CREATIONFLAGS, handle_process_output
+from git.cmd import PROC_CREATIONFLAGS, handle_process_output, Git
 from git.compat import defenc, force_bytes, force_text, safe_decode
 from git.exc import HookExecutionError, UnmergedEntriesError
 from git.objects.fun import (
@@ -96,7 +96,7 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
             # Windows only uses extensions to determine how to open files
             # (doesn't understand shebangs). Try using bash to run the hook.
             relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
-            cmd = ["bash.exe", relative_hp]
+            cmd = [Git.GIT_PYTHON_BASH_EXECUTABLE, relative_hp]
 
         process = subprocess.Popen(
             cmd + list(args),

From 4bde10debd95e59ad8702be515ae4bbc9fe0a8ee Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Mon, 8 Jan 2024 20:19:07 -0600
Subject: [PATCH 2/7] fix typos preventing import

---
 git/cmd.py   | 12 +++++++-----
 randytest.py |  1 +
 2 files changed, 8 insertions(+), 5 deletions(-)
 create mode 100644 randytest.py

diff --git a/git/cmd.py b/git/cmd.py
index 0a0d2e32a..b2bc43426 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -334,8 +334,9 @@ def _get_default_bash_path(cls):
         # This addresses issues where git hooks are intended to run assuming
         # the "native" Windows environment as seen by git.exe rather than
         # inside the git sandbox of WSL, which is likely configured
-        # independetly of the Windows Git.  A noteworthy example are repos with
-        # Git LFS, where Git LFS may be installed in Windows but not in WSL.
+        # independently of the Windows Git.  A noteworthy example are repos
+        # with Git LFS, where Git LFS may be installed in Windows but not
+        # in WSL.
         if not is_win:
             return 'bash'
         try:
@@ -346,7 +347,7 @@ def _get_default_bash_path(cls):
         gitpath = Path(wheregit.decode(defenc).splitlines()[0])
         gitroot = gitpath.parent.parent
         gitbash = gitroot / 'bin' / 'bash.exe'
-        return str(gitbash) if gitbash.exists else 'bash.exe'
+        return str(gitbash) if gitbash.exists() else 'bash.exe'
 
     @classmethod
     def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
@@ -357,7 +358,7 @@ def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
             new_bash = os.path.abspath(new_bash)
         else:
             new_bash = os.environ.get(cls._bash_exec_env_var)
-            if new_bash is None:
+            if not new_bash:
                 new_bash = cls._get_default_bash_path()
 
         # Keep track of the old and new bash executable path.
@@ -369,7 +370,8 @@ def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
         # executed for whatever reason.
         has_bash = False
         try:
-            run([cls.GIT_PYTHON_BASH_EXECUTABLE, '--version'])
+            run([cls.GIT_PYTHON_BASH_EXECUTABLE, '--version'],
+                check=True, stdout=PIPE)
             has_bash = True
         except CalledProcessError:
             pass
diff --git a/randytest.py b/randytest.py
new file mode 100644
index 000000000..4ebf4512a
--- /dev/null
+++ b/randytest.py
@@ -0,0 +1 @@
+import git

From 66766687be1f0ed8b57a878c77265a676c6875d0 Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Mon, 8 Jan 2024 20:21:11 -0600
Subject: [PATCH 3/7] remove rogue temp file

---
 randytest.py | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 randytest.py

diff --git a/randytest.py b/randytest.py
deleted file mode 100644
index 4ebf4512a..000000000
--- a/randytest.py
+++ /dev/null
@@ -1 +0,0 @@
-import git

From 50c74f45a0e8c3704930e5785b53af1095d36b72 Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Mon, 8 Jan 2024 20:38:08 -0600
Subject: [PATCH 4/7] updates after running black

---
 AUTHORS    |  1 +
 git/cmd.py | 20 ++++++++------------
 2 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 3b97c9473..cef6462cd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -53,5 +53,6 @@ Contributors are:
 -Santos Gallegos <stsewd _at_ proton.me>
 -Wenhan Zhu <wzhu.cosmos _at_ gmail.com>
 -Eliah Kagan <eliah.kagan _at_ gmail.com>
+-Randy Eckman <emanspeaks _at_ gmail.com>
 
 Portions derived from other open source works and are clearly marked.
diff --git a/git/cmd.py b/git/cmd.py
index b2bc43426..2b75a55d1 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -338,16 +338,15 @@ def _get_default_bash_path(cls):
         # with Git LFS, where Git LFS may be installed in Windows but not
         # in WSL.
         if not is_win:
-            return 'bash'
+            return "bash"
         try:
-            wheregit = run(['where', Git.GIT_PYTHON_GIT_EXECUTABLE],
-                           check=True, stdout=PIPE).stdout
+            wheregit = run(["where", Git.GIT_PYTHON_GIT_EXECUTABLE], check=True, stdout=PIPE).stdout
         except CalledProcessError:
-            return 'bash.exe'
+            return "bash.exe"
         gitpath = Path(wheregit.decode(defenc).splitlines()[0])
         gitroot = gitpath.parent.parent
-        gitbash = gitroot / 'bin' / 'bash.exe'
-        return str(gitbash) if gitbash.exists() else 'bash.exe'
+        gitbash = gitroot / "bin" / "bash.exe"
+        return str(gitbash) if gitbash.exists() else "bash.exe"
 
     @classmethod
     def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
@@ -370,24 +369,21 @@ def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
         # executed for whatever reason.
         has_bash = False
         try:
-            run([cls.GIT_PYTHON_BASH_EXECUTABLE, '--version'],
-                check=True, stdout=PIPE)
+            run([cls.GIT_PYTHON_BASH_EXECUTABLE, "--version"], check=True, stdout=PIPE)
             has_bash = True
         except CalledProcessError:
             pass
 
         # Warn or raise exception if test failed.
         if not has_bash:
-            err = (
-                dedent(
-                    f"""\
+            err = dedent(
+                f"""\
                 Bad bash executable.
                 The bash executable must be specified in one of the following ways:
                     - be included in your $PATH
                     - be set via ${cls._bash_exec_env_var}
                     - explicitly set via git.refresh_bash()
                 """
-                )
             )
 
             # Revert to whatever the old_bash was.

From f065d1fba422a528a133719350e027f1241273df Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Sun, 21 Jan 2024 00:49:13 -0600
Subject: [PATCH 5/7] address first round of comments from PR #1791

---
 .github/workflows/pythonpackage.yml |  10 +-
 git/__init__.py                     |   9 +-
 git/cmd.py                          | 193 +++++++++-------------------
 test/test_index.py                  |  90 ++++++-------
 4 files changed, 120 insertions(+), 182 deletions(-)

diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 08ff4efdf..b7aa150f9 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -35,11 +35,11 @@ jobs:
         python-version: ${{ matrix.python-version }}
         allow-prereleases: ${{ matrix.experimental }}
 
-    - name: Set up WSL (Windows)
-      if: startsWith(matrix.os, 'windows')
-      uses: Vampire/setup-wsl@v2.0.2
-      with:
-        distribution: Debian
+    # - name: Set up WSL (Windows)
+    #   if: startsWith(matrix.os, 'windows')
+    #   uses: Vampire/setup-wsl@v2.0.2
+    #   with:
+    #     distribution: Debian
 
     - name: Prepare this repo for tests
       run: |
diff --git a/git/__init__.py b/git/__init__.py
index e0b6d18b4..f9050dbfa 100644
--- a/git/__init__.py
+++ b/git/__init__.py
@@ -120,7 +120,14 @@
 
 
 def refresh(path: Optional[PathLike] = None) -> None:
-    """Convenience method for setting the git executable path."""
+    """
+    Convenience method for setting the git and bash executable paths.
+
+    Note that the default behavior of invoking commit hooks on Windows has
+    changed to not prefer WSL bash with the introduction of
+    `Git.GIT_PYTHON_BASH_EXECUTABLE`.  See the `refresh_bash()` documentation
+    for details on the default values and search paths.
+    """
     global GIT_OK
     GIT_OK = False
 
diff --git a/git/cmd.py b/git/cmd.py
index 4bc1f4819..12cbf36b2 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -11,13 +11,13 @@
 import logging
 import os
 import signal
-from subprocess import Popen, PIPE, DEVNULL, run, CalledProcessError
+from subprocess import Popen, PIPE, DEVNULL
 import subprocess
 import threading
 from textwrap import dedent
 from pathlib import Path
 
-from git.compat import defenc, force_bytes, safe_decode, is_win
+from git.compat import defenc, force_bytes, safe_decode
 from git.exc import (
     CommandError,
     GitCommandError,
@@ -364,24 +364,27 @@ def __setstate__(self, d: Dict[str, Any]) -> None:
     _bash_exec_env_var = "GIT_PYTHON_BASH_EXECUTABLE"
 
     bash_exec_name = "bash"
-    """Default bash command that should work on Linux, Windows, and other systems."""
+    """Default bash command."""
 
     GIT_PYTHON_BASH_EXECUTABLE = None
-    """Provide the full path to the bash executable. Otherwise it assumes bash is in the path.
-
-    Note that the bash executable is actually found during the refresh step in
-    the top level ``__init__``.
+    """
+    Provides the path to the bash executable used for commit hooks.  This is
+    ordinarily set by `Git.refresh_bash()`.  Note that the default behavior of
+    invoking commit hooks on Windows has changed to not prefer WSL bash with
+    the introduction of this variable.  See the `Git.refresh_bash()`
+    documentation for details on the default values and search paths.
     """
 
     @classmethod
-    def _get_default_bash_path(cls):
+    def _get_default_bash_path(cls) -> str:
         # Assumes that, if user is running in Windows, they probably are using
         # Git for Windows, which includes Git BASH and should be associated
-        # with the configured Git command set in `refresh()`.  Regardless of
-        # if the Git command assumes it is installed in (root)/cmd/git.exe or
-        # (root)/bin/git.exe, the root is always up two levels from the git
-        # command.  Try going up to levels from the currently configured
-        # git command, then navigate to (root)/bin/bash.exe.  If this exists,
+        # with the configured Git command set in `refresh()`.
+        # Uses the output of `git --exec-path` for the currently configured
+        # Git command to find its `git-core` directory.  If one assumes that
+        # the `git-core` directory is always three levels deeper than the
+        # root directory of the Git installation, we can try going up three
+        # levels and then navigating to (root)/bin/bash.exe.  If this exists,
         # prefer it over the WSL version in System32, direct access to which
         # is reportedly deprecated.  Fail back to default "bash.exe" if
         # the Git for Windows lookup doesn't work.
@@ -392,145 +395,73 @@ def _get_default_bash_path(cls):
         # independently of the Windows Git.  A noteworthy example are repos
         # with Git LFS, where Git LFS may be installed in Windows but not
         # in WSL.
-        if not is_win:
+        if os.name != 'nt':
             return "bash"
-        try:
-            wheregit = run(["where", Git.GIT_PYTHON_GIT_EXECUTABLE], check=True, stdout=PIPE).stdout
-        except CalledProcessError:
-            return "bash.exe"
-        gitpath = Path(wheregit.decode(defenc).splitlines()[0])
-        gitroot = gitpath.parent.parent
+        gitcore = Path(cls()._call_process("--exec-path"))
+        gitroot = gitcore.parent.parent.parent
         gitbash = gitroot / "bin" / "bash.exe"
         return str(gitbash) if gitbash.exists() else "bash.exe"
 
     @classmethod
     def refresh_bash(cls, path: Union[None, PathLike] = None) -> bool:
-        """This gets called by the refresh function (see the top level __init__)."""
+        """
+        Refreshes the cached path to the bash executable used for executing
+        commit hook scripts.  This gets called by the top-level `refresh()`
+        function on initial package import (see the top level __init__), but
+        this method may be invoked manually if the path changes after import.
+
+        This method only checks one path for a valid bash executable at a time,
+        using the first non-empty path provided in the following priority
+        order:
+
+          1. the explicit `path` argument to this method
+          2. the environment variable `GIT_PYTHON_BASH_EXECUTABLE` if it is set
+             and available via `os.environ` upon calling this method
+          3. if the current platform is not Windows, the simple string `"bash"`
+          4. if the current platform is Windows, inferred from the current
+             provided Git executable assuming it is part of a Git for Windows
+             distribution.
+
+        The current platform is checked based on the call `os.name`.
+
+        This is a change to the default behavior from previous versions of
+        GitPython.  In the event backwards compatibility is needed, the `path`
+        argument or the environment variable may be set to the string
+        `"bash.exe"`, which on most systems invokes the WSL bash by default.
+
+        This change to default behavior addresses issues where git hooks are
+        intended to run assuming the "native" Windows environment as seen by
+        git.exe rather than inside the git sandbox of WSL, which is likely
+        configured independently of the Windows Git.  A noteworthy example are
+        repos with Git LFS, where Git LFS may be installed in Windows but not
+        in WSL.
+        """
         # Discern which path to refresh with.
         if path is not None:
             new_bash = os.path.expanduser(path)
-            new_bash = os.path.abspath(new_bash)
+            # new_bash = os.path.abspath(new_bash)
         else:
             new_bash = os.environ.get(cls._bash_exec_env_var)
             if not new_bash:
                 new_bash = cls._get_default_bash_path()
 
         # Keep track of the old and new bash executable path.
-        old_bash = cls.GIT_PYTHON_BASH_EXECUTABLE
+        # old_bash = cls.GIT_PYTHON_BASH_EXECUTABLE
         cls.GIT_PYTHON_BASH_EXECUTABLE = new_bash
 
-        # Test if the new git executable path is valid. A GitCommandNotFound error is
-        # spawned by us. A PermissionError is spawned if the git executable cannot be
-        # executed for whatever reason.
-        has_bash = False
-        try:
-            run([cls.GIT_PYTHON_BASH_EXECUTABLE, "--version"], check=True, stdout=PIPE)
-            has_bash = True
-        except CalledProcessError:
-            pass
-
-        # Warn or raise exception if test failed.
-        if not has_bash:
-            err = dedent(
-                f"""\
-                Bad bash executable.
-                The bash executable must be specified in one of the following ways:
-                    - be included in your $PATH
-                    - be set via ${cls._bash_exec_env_var}
-                    - explicitly set via git.refresh_bash()
-                """
-            )
-
-            # Revert to whatever the old_bash was.
-            cls.GIT_PYTHON_BASH_EXECUTABLE = old_bash
-
-            if old_bash is None:
-                # On the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is None) we only
-                # are quiet, warn, or error depending on the GIT_PYTHON_REFRESH value.
-
-                # Determine what the user wants to happen during the initial refresh we
-                # expect GIT_PYTHON_REFRESH to either be unset or be one of the
-                # following values:
-                #
-                #   0|q|quiet|s|silence|n|none
-                #   1|w|warn|warning
-                #   2|r|raise|e|error
-
-                mode = os.environ.get(cls._refresh_env_var, "raise").lower()
-
-                quiet = ["quiet", "q", "silence", "s", "none", "n", "0"]
-                warn = ["warn", "w", "warning", "1"]
-                error = ["error", "e", "raise", "r", "2"]
-
-                if mode in quiet:
-                    pass
-                elif mode in warn or mode in error:
-                    err = (
-                        dedent(
-                            """\
-                        %s
-                        All commit hook commands will error until this is rectified.
-
-                        This initial warning can be silenced or aggravated in the future by setting the
-                        $%s environment variable. Use one of the following values:
-                            - %s: for no warning or exception
-                            - %s: for a printed warning
-                            - %s: for a raised exception
-
-                        Example:
-                            export %s=%s
-                        """
-                        )
-                        % (
-                            err,
-                            cls._refresh_env_var,
-                            "|".join(quiet),
-                            "|".join(warn),
-                            "|".join(error),
-                            cls._refresh_env_var,
-                            quiet[0],
-                        )
-                    )
-
-                    if mode in warn:
-                        print("WARNING: %s" % err)
-                    else:
-                        raise ImportError(err)
-                else:
-                    err = (
-                        dedent(
-                            """\
-                        %s environment variable has been set but it has been set with an invalid value.
-
-                        Use only the following values:
-                            - %s: for no warning or exception
-                            - %s: for a printed warning
-                            - %s: for a raised exception
-                        """
-                        )
-                        % (
-                            cls._refresh_env_var,
-                            "|".join(quiet),
-                            "|".join(warn),
-                            "|".join(error),
-                        )
-                    )
-                    raise ImportError(err)
-
-                # We get here if this was the init refresh and the refresh mode was not
-                # error. Go ahead and set the GIT_PYTHON_BASH_EXECUTABLE such that we
-                # discern the difference between a first import and a second import.
-                cls.GIT_PYTHON_BASH_EXECUTABLE = cls.bash_exec_name
-            else:
-                # After the first refresh (when GIT_PYTHON_BASH_EXECUTABLE is no longer
-                # None) we raise an exception.
-                raise GitCommandNotFound("bash", err)
-
+        # Test if the new git executable path exists.
+        has_bash = Path(cls.GIT_PYTHON_BASH_EXECUTABLE).exists()
         return has_bash
 
     @classmethod
     def refresh(cls, path: Union[None, PathLike] = None) -> bool:
-        """This gets called by the refresh function (see the top level __init__)."""
+        """
+        This gets called by the refresh function (see the top level __init__).
+
+        Note that calling this method directly does not automatically update
+        the cached path to `bash`; either invoke the top level `refresh()`
+        function or call `Git.refresh_bash()` directly.
+        """
         # Discern which path to refresh with.
         if path is not None:
             new_git = os.path.expanduser(path)
diff --git a/test/test_index.py b/test/test_index.py
index 22eac355b..f4d4005e1 100644
--- a/test/test_index.py
+++ b/test/test_index.py
@@ -1019,16 +1019,16 @@ class Mocked:
         rel = index._to_relative_path(path)
         self.assertEqual(rel, os.path.relpath(path, root))
 
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.Absent,
-        reason="Can't run a hook on Windows without bash.exe.",
-        rasies=HookExecutionError,
-    )
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.WslNoDistro,
-        reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
-        raises=HookExecutionError,
-    )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.Absent,
+    #     reason="Can't run a hook on Windows without bash.exe.",
+    #     rasies=HookExecutionError,
+    # )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.WslNoDistro,
+    #     reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
+    #     raises=HookExecutionError,
+    # )
     @with_rw_repo("HEAD", bare=True)
     def test_run_commit_hook(self, rw_repo):
         index = rw_repo.index
@@ -1078,27 +1078,27 @@ def test_hook_uses_shell_not_from_cwd(self, rw_dir, case):
             output = Path(rw_dir, "output.txt").read_text(encoding="utf-8")
             self.assertEqual(output, "Ran intended hook.\n")
 
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.Absent,
-        reason="Can't run a hook on Windows without bash.exe.",
-        rasies=HookExecutionError,
-    )
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.WslNoDistro,
-        reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
-        raises=HookExecutionError,
-    )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.Absent,
+    #     reason="Can't run a hook on Windows without bash.exe.",
+    #     rasies=HookExecutionError,
+    # )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.WslNoDistro,
+    #     reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
+    #     raises=HookExecutionError,
+    # )
     @with_rw_repo("HEAD", bare=True)
     def test_pre_commit_hook_success(self, rw_repo):
         index = rw_repo.index
         _make_hook(index.repo.git_dir, "pre-commit", "exit 0")
         index.commit("This should not fail")
 
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.WslNoDistro,
-        reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
-        raises=AssertionError,
-    )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.WslNoDistro,
+    #     reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
+    #     raises=AssertionError,
+    # )
     @with_rw_repo("HEAD", bare=True)
     def test_pre_commit_hook_fail(self, rw_repo):
         index = rw_repo.index
@@ -1121,21 +1121,21 @@ def test_pre_commit_hook_fail(self, rw_repo):
         else:
             raise AssertionError("Should have caught a HookExecutionError")
 
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.Absent,
-        reason="Can't run a hook on Windows without bash.exe.",
-        rasies=HookExecutionError,
-    )
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.Wsl,
-        reason="Specifically seems to fail on WSL bash (in spite of #1399)",
-        raises=AssertionError,
-    )
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.WslNoDistro,
-        reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
-        raises=HookExecutionError,
-    )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.Absent,
+    #     reason="Can't run a hook on Windows without bash.exe.",
+    #     rasies=HookExecutionError,
+    # )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.Wsl,
+    #     reason="Specifically seems to fail on WSL bash (in spite of #1399)",
+    #     raises=AssertionError,
+    # )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.WslNoDistro,
+    #     reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
+    #     raises=HookExecutionError,
+    # )
     @with_rw_repo("HEAD", bare=True)
     def test_commit_msg_hook_success(self, rw_repo):
         commit_message = "commit default head by Frèderic Çaufl€"
@@ -1149,11 +1149,11 @@ def test_commit_msg_hook_success(self, rw_repo):
         new_commit = index.commit(commit_message)
         self.assertEqual(new_commit.message, "{} {}".format(commit_message, from_hook_message))
 
-    @pytest.mark.xfail(
-        type(_win_bash_status) is WinBashStatus.WslNoDistro,
-        reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
-        raises=AssertionError,
-    )
+    # @pytest.mark.xfail(
+    #     type(_win_bash_status) is WinBashStatus.WslNoDistro,
+    #     reason="Currently uses the bash.exe of WSL, even with no WSL distro installed",
+    #     raises=AssertionError,
+    # )
     @with_rw_repo("HEAD", bare=True)
     def test_commit_msg_hook_fail(self, rw_repo):
         index = rw_repo.index

From 0c14cac36f43240584ae286ab4a3fc42552dad74 Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Sun, 21 Jan 2024 00:53:26 -0600
Subject: [PATCH 6/7] address lint issue

---
 git/cmd.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git/cmd.py b/git/cmd.py
index 12cbf36b2..b4f1bc329 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -395,7 +395,7 @@ def _get_default_bash_path(cls) -> str:
         # independently of the Windows Git.  A noteworthy example are repos
         # with Git LFS, where Git LFS may be installed in Windows but not
         # in WSL.
-        if os.name != 'nt':
+        if os.name != "nt":
             return "bash"
         gitcore = Path(cls()._call_process("--exec-path"))
         gitroot = gitcore.parent.parent.parent

From 8200ad10463d6df34e9b21c977e0d9092b908611 Mon Sep 17 00:00:00 2001
From: Randy Eckman <randy.eckman@nasa.gov>
Date: Sun, 21 Jan 2024 01:26:29 -0600
Subject: [PATCH 7/7] possible fix to test_hook_uses_shell_not_from_cwd

---
 test/test_index.py | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/test/test_index.py b/test/test_index.py
index f4d4005e1..660ab3695 100644
--- a/test/test_index.py
+++ b/test/test_index.py
@@ -1064,19 +1064,19 @@ def test_hook_uses_shell_not_from_cwd(self, rw_dir, case):
         shutil.copy(fixture_path("polyglot"), hook_path("polyglot", repo.git_dir))
         payload = Path(rw_dir, "payload.txt")
 
-        if type(_win_bash_status) in {WinBashStatus.Absent, WinBashStatus.WslNoDistro}:
-            # The real shell can't run, but the impostor should still not be used.
-            with self.assertRaises(HookExecutionError):
-                with maybe_chdir:
-                    run_commit_hook("polyglot", repo.index)
-            self.assertFalse(payload.exists())
-        else:
-            # The real shell should run, and not the impostor.
-            with maybe_chdir:
-                run_commit_hook("polyglot", repo.index)
-            self.assertFalse(payload.exists())
-            output = Path(rw_dir, "output.txt").read_text(encoding="utf-8")
-            self.assertEqual(output, "Ran intended hook.\n")
+        # if type(_win_bash_status) in {WinBashStatus.Absent, WinBashStatus.WslNoDistro}:
+        #     # The real shell can't run, but the impostor should still not be used.
+        #     with self.assertRaises(HookExecutionError):
+        #         with maybe_chdir:
+        #             run_commit_hook("polyglot", repo.index)
+        #     self.assertFalse(payload.exists())
+        # else:
+        # The real shell should run, and not the impostor.
+        with maybe_chdir:
+            run_commit_hook("polyglot", repo.index)
+        self.assertFalse(payload.exists())
+        output = Path(rw_dir, "output.txt").read_text(encoding="utf-8")
+        self.assertEqual(output, "Ran intended hook.\n")
 
     # @pytest.mark.xfail(
     #     type(_win_bash_status) is WinBashStatus.Absent,