diff --git a/.github/workflows/cygwin-test.yml b/.github/workflows/cygwin-test.yml
index 962791ae7..e818803f1 100644
--- a/.github/workflows/cygwin-test.yml
+++ b/.github/workflows/cygwin-test.yml
@@ -5,20 +5,24 @@ on: [push, pull_request, workflow_dispatch]
 jobs:
   build:
     runs-on: windows-latest
+
     strategy:
       fail-fast: false
+
     env:
       CHERE_INVOKING: 1
-      SHELLOPTS: igncr
       TMP: "/tmp"
       TEMP: "/tmp"
+
     defaults:
       run:
-        shell: bash.exe --noprofile --norc -exo pipefail -o igncr "{0}"
+        shell: C:\cygwin\bin\bash.exe --noprofile --norc -exo pipefail -o igncr "{0}"
 
     steps:
     - name: Force LF line endings
-      run: git config --global core.autocrlf input
+      run: |
+        git config --global core.autocrlf false  # Affects the non-Cygwin git.
+      shell: bash
 
     - uses: actions/checkout@v4
       with:
@@ -29,36 +33,42 @@ jobs:
       with:
         packages: python39 python39-pip python39-virtualenv git
 
-    - name: Show python and git versions
+    - name: Special configuration for Cygwin's git
       run: |
-        /usr/bin/python --version
-        /usr/bin/git version
-
-    - name: Tell git to trust this repo
-      run: |
-        /usr/bin/git config --global --add safe.directory "$(pwd)"
+        git config --global --add safe.directory "$(pwd)"
+        git config --global core.autocrlf false
 
     - name: Prepare this repo for tests
       run: |
         TRAVIS=yes ./init-tests-after-clone.sh
 
-    - name: Further prepare git configuration for tests
+    - name: Set git user identity and command aliases for the tests
       run: |
-        /usr/bin/git config --global user.email "travis@ci.com"
-        /usr/bin/git config --global user.name "Travis Runner"
+        git config --global user.email "travis@ci.com"
+        git config --global user.name "Travis Runner"
         # If we rewrite the user's config by accident, we will mess it up
         # and cause subsequent tests to fail
         cat test/fixtures/.gitconfig >> ~/.gitconfig
 
     - name: Update PyPA packages
       run: |
-        /usr/bin/python -m pip install --upgrade pip setuptools wheel
+        # Get the latest pip, wheel, and prior to Python 3.12, setuptools.
+        python -m pip install -U pip $(pip freeze --all | grep -oF setuptools) wheel
 
     - name: Install project and test dependencies
       run: |
-        /usr/bin/python -m pip install ".[test]"
+        python -m pip install ".[test]"
+
+    - name: Show version and platform information
+      run: |
+        uname -a
+        command -v git python
+        git version
+        python --version
+        python -c 'import sys; print(sys.platform)'
+        python -c 'import os; print(os.name)'
+        python -c 'import git; print(git.compat.is_win)'
 
     - name: Test with pytest
       run: |
-        set +x
-        /usr/bin/python -m pytest
+        python -m pytest --color=yes -p no:sugar --instafail -vv
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5e79664a8..2204bb792 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -7,8 +7,10 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v4
-      - uses: actions/setup-python@v4
-        with:
-          python-version: "3.x"
-      - uses: pre-commit/action@v3.0.0
+    - uses: actions/checkout@v4
+
+    - uses: actions/setup-python@v4
+      with:
+        python-version: "3.x"
+
+    - uses: pre-commit/action@v3.0.0
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index a5467ef94..1b049ba02 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -10,8 +10,8 @@ permissions:
 
 jobs:
   build:
-
     runs-on: ubuntu-latest
+
     strategy:
       fail-fast: false
       matrix:
@@ -20,6 +20,7 @@ jobs:
           - experimental: false
           - python-version: "3.12"
             experimental: true
+
     defaults:
       run:
         shell: /bin/bash --noprofile --norc -exo pipefail {0}
@@ -36,16 +37,11 @@ jobs:
         python-version: ${{ matrix.python-version }}
         allow-prereleases: ${{ matrix.experimental }}
 
-    - name: Show python and git versions
-      run: |
-        python --version
-        git version
-
     - name: Prepare this repo for tests
       run: |
         TRAVIS=yes ./init-tests-after-clone.sh
 
-    - name: Prepare git configuration for tests
+    - name: Set git user identity and command aliases for the tests
       run: |
         git config --global user.email "travis@ci.com"
         git config --global user.name "Travis Runner"
@@ -55,17 +51,23 @@ jobs:
 
     - name: Update PyPA packages
       run: |
-        python -m pip install --upgrade pip
-        if pip freeze --all | grep --quiet '^setuptools=='; then
-            # Python prior to 3.12 ships setuptools. Upgrade it if present.
-            python -m pip install --upgrade setuptools
-        fi
-        python -m pip install --upgrade wheel
+        # Get the latest pip, wheel, and prior to Python 3.12, setuptools.
+        python -m pip install -U pip $(pip freeze --all | grep -oF setuptools) wheel
 
     - name: Install project and test dependencies
       run: |
         pip install ".[test]"
 
+    - name: Show version and platform information
+      run: |
+        uname -a
+        command -v git python
+        git version
+        python --version
+        python -c 'import sys; print(sys.platform)'
+        python -c 'import os; print(os.name)'
+        python -c 'import git; print(git.compat.is_win)'
+
     - name: Check types with mypy
       run: |
         mypy -p git
@@ -75,7 +77,7 @@ jobs:
 
     - name: Test with pytest
       run: |
-        pytest
+        pytest --color=yes -p no:sugar --instafail -vv
       continue-on-error: false
 
     - name: Documentation
diff --git a/pyproject.toml b/pyproject.toml
index fa06458eb..f4fc33fec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
 [tool.pytest.ini_options]
 python_files = 'test_*.py'
 testpaths = 'test'  # space separated list of paths from root e.g test tests doc/testing
-addopts = '--cov=git --cov-report=term --maxfail=10 --force-sugar --disable-warnings'
+addopts = '--cov=git --cov-report=term --disable-warnings'
 filterwarnings = 'ignore::DeprecationWarning'
 # --cov   coverage
 # --cov-report term  # send report to terminal term-missing -> terminal with line numbers  html  xml
diff --git a/test-requirements.txt b/test-requirements.txt
index b00dd6f06..1c08c736f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,4 +5,5 @@ mypy
 pre-commit
 pytest
 pytest-cov
+pytest-instafail
 pytest-sugar
diff --git a/test/test_base.py b/test/test_base.py
index b77c8117d..90e701c4b 100644
--- a/test/test_base.py
+++ b/test/test_base.py
@@ -7,7 +7,7 @@
 import os
 import sys
 import tempfile
-from unittest import SkipTest, skipIf
+from unittest import skipIf
 
 from git import Repo
 from git.objects import Blob, Tree, Commit, TagObject
@@ -126,7 +126,7 @@ def test_add_unicode(self, rw_repo):
         try:
             file_path.encode(sys.getfilesystemencoding())
         except UnicodeEncodeError as e:
-            raise SkipTest("Environment doesn't support unicode filenames") from e
+            raise RuntimeError("Environment doesn't support unicode filenames") from e
 
         with open(file_path, "wb") as fp:
             fp.write(b"something")
diff --git a/test/test_config.py b/test/test_config.py
index 481e129c6..f805570d5 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -100,6 +100,7 @@ def test_includes_order(self):
             # values must be considered as soon as they get them
             assert r_config.get_value("diff", "tool") == "meld"
             try:
+                # FIXME: Split this assertion out somehow and mark it xfail (or fix it).
                 assert r_config.get_value("sec", "var1") == "value1_main"
             except AssertionError as e:
                 raise SkipTest("Known failure -- included values are not in effect right away") from e
diff --git a/test/test_fun.py b/test/test_fun.py
index d76e189ed..f39955aa0 100644
--- a/test/test_fun.py
+++ b/test/test_fun.py
@@ -2,7 +2,6 @@
 from stat import S_IFDIR, S_IFREG, S_IFLNK, S_IXUSR
 from os import stat
 import os.path as osp
-from unittest import SkipTest
 
 from git import Git
 from git.index import IndexFile
@@ -279,7 +278,7 @@ def test_linked_worktree_traversal(self, rw_dir):
         """Check that we can identify a linked worktree based on a .git file"""
         git = Git(rw_dir)
         if git.version_info[:3] < (2, 5, 1):
-            raise SkipTest("worktree feature unsupported")
+            raise RuntimeError("worktree feature unsupported (test needs git 2.5.1 or later)")
 
         rw_master = self.rorepo.clone(join_path_native(rw_dir, "master_repo"))
         branch = rw_master.create_head("aaaaaaaa")
diff --git a/test/test_index.py b/test/test_index.py
index fba9c78ec..06db3aedd 100644
--- a/test/test_index.py
+++ b/test/test_index.py
@@ -7,10 +7,13 @@
 
 from io import BytesIO
 import os
+import os.path as osp
+from pathlib import Path
 from stat import S_ISLNK, ST_MODE
-import tempfile
-from unittest import skipIf
 import shutil
+import tempfile
+
+import pytest
 
 from git import (
     IndexFile,
@@ -28,21 +31,26 @@
 from git.index.fun import hook_path
 from git.index.typ import BaseIndexEntry, IndexEntry
 from git.objects import Blob
-from test.lib import TestBase, fixture_path, fixture, with_rw_repo
-from test.lib import with_rw_directory
-from git.util import Actor, rmtree
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS, hex_to_bin
+from test.lib import TestBase, fixture, fixture_path, with_rw_directory, with_rw_repo
+from git.util import Actor, hex_to_bin, rmtree
 from gitdb.base import IStream
 
-import os.path as osp
-from git.cmd import Git
+HOOKS_SHEBANG = "#!/usr/bin/env sh\n"
 
-from pathlib import Path
 
-HOOKS_SHEBANG = "#!/usr/bin/env sh\n"
+def _found_in(cmd, directory):
+    """Check if a command is resolved in a directory (without following symlinks)."""
+    path = shutil.which(cmd)
+    return path and Path(path).parent == Path(directory)
+
 
 is_win_without_bash = is_win and not shutil.which("bash.exe")
 
+is_win_with_wsl_bash = is_win and _found_in(
+    cmd="bash.exe",
+    directory=Path(os.getenv("WINDIR")) / "System32",
+)
+
 
 def _make_hook(git_dir, name, content, make_exec=True):
     """A helper to create a hook"""
@@ -422,14 +430,6 @@ def _count_existing(self, repo, files):
 
     # END num existing helper
 
-    @skipIf(
-        HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
-        """FIXME: File "C:\\projects\\gitpython\\git\\test\\test_index.py", line 642, in test_index_mutation
-                self.assertEqual(fd.read(), link_target)
-                AssertionError: '!<symlink>\xff\xfe/\x00e\x00t\x00c\x00/\x00t\x00h\x00a\x00t\x00\x00\x00'
-                != '/etc/that'
-                """,
-    )
     @with_rw_repo("0.1.6")
     def test_index_mutation(self, rw_repo):
         index = rw_repo.index
@@ -910,7 +910,11 @@ def test_pre_commit_hook_fail(self, rw_repo):
         else:
             raise AssertionError("Should have caught a HookExecutionError")
 
-    @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
+    @pytest.mark.xfail(
+        is_win_without_bash or is_win_with_wsl_bash,
+        reason="Specifically seems to fail on WSL bash (in spite of #1399)",
+        raises=AssertionError,
+    )
     @with_rw_repo("HEAD", bare=True)
     def test_commit_msg_hook_success(self, rw_repo):
         commit_message = "commit default head by Frèderic Çaufl€"
diff --git a/test/test_repo.py b/test/test_repo.py
index 15899ec50..364b895fb 100644
--- a/test/test_repo.py
+++ b/test/test_repo.py
@@ -13,7 +13,7 @@
 import pickle
 import sys
 import tempfile
-from unittest import mock, skipIf, SkipTest, skip
+from unittest import mock, skip
 
 import pytest
 
@@ -41,10 +41,8 @@
     UnsafeProtocolError,
 )
 from git.repo.fun import touch
-from test.lib import TestBase, with_rw_repo, fixture
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS, cygpath
-from test.lib import with_rw_directory
-from git.util import join_path_native, rmtree, rmfile, bin_to_hex
+from git.util import bin_to_hex, cygpath, join_path_native, rmfile, rmtree
+from test.lib import TestBase, fixture, with_rw_directory, with_rw_repo
 
 import os.path as osp
 
@@ -764,16 +762,6 @@ def test_blame_accepts_rev_opts(self, git):
         self.rorepo.blame("HEAD", "README.md", rev_opts=["-M", "-C", "-C"])
         git.assert_called_once_with(*expected_args, **boilerplate_kwargs)
 
-    @skipIf(
-        HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
-        """FIXME: File "C:\\projects\\gitpython\\git\\cmd.py", line 671, in execute
-                    raise GitCommandError(command, status, stderr_value, stdout_value)
-                GitCommandError: Cmd('git') failed due to: exit code(128)
-                  cmdline: git add 1__��ava verb��ten 1_test _myfile 1_test_other_file
-                          1_��ava-----verb��ten
-                  stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
-                """,
-    )
     @with_rw_repo("HEAD", bare=False)
     def test_untracked_files(self, rwrepo):
         for run, repo_add in enumerate((rwrepo.index.add, rwrepo.git.add)):
@@ -1245,7 +1233,7 @@ def test_merge_base(self):
     def test_is_ancestor(self):
         git = self.rorepo.git
         if git.version_info[:3] < (1, 8, 0):
-            raise SkipTest("git merge-base --is-ancestor feature unsupported")
+            raise RuntimeError("git merge-base --is-ancestor feature unsupported (test needs git 1.8.0 or later)")
 
         repo = self.rorepo
         c1 = "f6aa8d1"
@@ -1293,7 +1281,7 @@ def test_git_work_tree_dotgit(self, rw_dir):
         based on it."""
         git = Git(rw_dir)
         if git.version_info[:3] < (2, 5, 1):
-            raise SkipTest("worktree feature unsupported")
+            raise RuntimeError("worktree feature unsupported (test needs git 2.5.1 or later)")
 
         rw_master = self.rorepo.clone(join_path_native(rw_dir, "master_repo"))
         branch = rw_master.create_head("aaaaaaaa")
diff --git a/test/test_submodule.py b/test/test_submodule.py
index 0aa80e5ce..79ff2c5f2 100644
--- a/test/test_submodule.py
+++ b/test/test_submodule.py
@@ -7,7 +7,7 @@
 import tempfile
 from pathlib import Path
 import sys
-from unittest import mock, skipIf
+from unittest import mock, skipUnless
 
 import pytest
 
@@ -474,14 +474,13 @@ def test_base_bare(self, rwrepo):
         reason="Cygwin GitPython can't find submodule SHA",
         raises=ValueError,
     )
-    @skipIf(
+    @pytest.mark.xfail(
         HIDE_WINDOWS_KNOWN_ERRORS,
-        """
-        File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
-        raise GitCommandNotFound(command, err)
-        git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
-        cmdline: git clone -n --shared -v C:\\projects\\gitpython\\.git Users\\appveyor\\AppData\\Local\\Temp\\1\\tmplyp6kr_rnon_bare_test_root_module
-        """,  # noqa E501
+        reason=(
+            '"The process cannot access the file because it is being used by another process"'
+            + " on first call to rm.update"
+        ),
+        raises=PermissionError,
     )
     @with_rw_repo(k_subm_current, bare=False)
     def test_root_module(self, rwrepo):
@@ -749,15 +748,13 @@ def test_list_only_valid_submodules(self, rwdir):
         repo = git.Repo(repo_path)
         assert len(repo.submodules) == 0
 
-    @skipIf(
+    @pytest.mark.xfail(
         HIDE_WINDOWS_KNOWN_ERRORS,
-        """FIXME on cygwin: File "C:\\projects\\gitpython\\git\\cmd.py", line 671, in execute
-                raise GitCommandError(command, status, stderr_value, stdout_value)
-            GitCommandError: Cmd('git') failed due to: exit code(128)
-              cmdline: git add 1__Xava verbXXten 1_test _myfile 1_test_other_file 1_XXava-----verbXXten
-              stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
-             FIXME on appveyor: see https://ci.appveyor.com/project/Byron/gitpython/build/1.0.185
-                """,
+        reason=(
+            '"The process cannot access the file because it is being used by another process"'
+            + " on first call to sm.move"
+        ),
+        raises=PermissionError,
     )
     @with_rw_directory
     @_patch_git_config("protocol.file.allow", "always")
@@ -1039,7 +1036,7 @@ def test_branch_renames(self, rw_dir):
         assert sm_mod.commit() == sm_pfb.commit, "Now head should have been reset"
         assert sm_mod.head.ref.name == sm_pfb.name
 
-    @skipIf(not is_win, "Specifically for Windows.")
+    @skipUnless(is_win, "Specifically for Windows.")
     def test_to_relative_path_with_super_at_root_drive(self):
         class Repo(object):
             working_tree_dir = "D:\\"
@@ -1050,9 +1047,9 @@ class Repo(object):
         msg = '_to_relative_path should be "submodule_path" but was "%s"' % relative_path
         assert relative_path == "submodule_path", msg
 
-    @skipIf(
-        True,
-        "for some unknown reason the assertion fails, even though it in fact is working in more common setup",
+    @pytest.mark.xfail(
+        reason="for some unknown reason the assertion fails, even though it in fact is working in more common setup",
+        raises=AssertionError,
     )
     @with_rw_directory
     def test_depth(self, rwdir):
diff --git a/test/test_tree.py b/test/test_tree.py
index e59705645..c5ac8d539 100644
--- a/test/test_tree.py
+++ b/test/test_tree.py
@@ -5,24 +5,14 @@
 # the BSD License: https://opensource.org/license/bsd-3-clause/
 
 from io import BytesIO
-from unittest import skipIf
 
 from git.objects import Tree, Blob
 from test.lib import TestBase
-from git.util import HIDE_WINDOWS_KNOWN_ERRORS
 
 import os.path as osp
 
 
 class TestTree(TestBase):
-    @skipIf(
-        HIDE_WINDOWS_KNOWN_ERRORS,
-        """
-        File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
-        raise GitCommandNotFound(command, err)
-        git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
-        cmdline: git cat-file --batch-check""",
-    )
     def test_serializable(self):
         # tree at the given commit contains a submodule as well
         roottree = self.rorepo.tree("6c1faef799095f3990e9970bc2cb10aa0221cf9c")
@@ -51,14 +41,6 @@ def test_serializable(self):
             testtree._deserialize(stream)
         # END for each item in tree
 
-    @skipIf(
-        HIDE_WINDOWS_KNOWN_ERRORS,
-        """
-        File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
-        raise GitCommandNotFound(command, err)
-        git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
-        cmdline: git cat-file --batch-check""",
-    )
     def test_traverse(self):
         root = self.rorepo.tree("0.1.6")
         num_recursive = 0
diff --git a/test/test_util.py b/test/test_util.py
index 42edc57cf..2b1e518ed 100644
--- a/test/test_util.py
+++ b/test/test_util.py
@@ -9,11 +9,11 @@
 import sys
 import tempfile
 import time
-from unittest import mock, skipIf
+from unittest import mock, skipUnless
 from datetime import datetime
 
-import pytest
 import ddt
+import pytest
 
 from git.cmd import dashify
 from git.compat import is_win
@@ -85,17 +85,26 @@ def setup(self):
             "array": [42],
         }
 
-    @skipIf(not is_win, "Paths specifically for Windows.")
+    # FIXME: Mark only the /proc-prefixing cases xfail, somehow (or fix them).
+    @pytest.mark.xfail(
+        reason="Many return paths prefixed /proc/cygdrive instead.",
+        raises=AssertionError,
+    )
+    @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.")
     @ddt.idata(_norm_cygpath_pairs + _unc_cygpath_pairs)
     def test_cygpath_ok(self, case):
         wpath, cpath = case
         cwpath = cygpath(wpath)
         self.assertEqual(cwpath, cpath, wpath)
 
-    @skipIf(not is_win, "Paths specifically for Windows.")
+    @pytest.mark.xfail(
+        reason=r'2nd example r".\bar" -> "bar" fails, returns "./bar"',
+        raises=AssertionError,
+    )
+    @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.")
     @ddt.data(
         (r"./bar", "bar"),
-        (r".\bar", "bar"),
+        (r".\bar", "bar"),  # FIXME: Mark only this one xfail, somehow (or fix it).
         (r"../bar", "../bar"),
         (r"..\bar", "../bar"),
         (r"../bar/.\foo/../chu", "../bar/chu"),
@@ -105,7 +114,7 @@ def test_cygpath_norm_ok(self, case):
         cwpath = cygpath(wpath)
         self.assertEqual(cwpath, cpath or wpath, wpath)
 
-    @skipIf(not is_win, "Paths specifically for Windows.")
+    @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.")
     @ddt.data(
         r"C:",
         r"C:Relative",
@@ -118,7 +127,7 @@ def test_cygpath_invalids(self, wpath):
         cwpath = cygpath(wpath)
         self.assertEqual(cwpath, wpath.replace("\\", "/"), wpath)
 
-    @skipIf(not is_win, "Paths specifically for Windows.")
+    @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.")
     @ddt.idata(_norm_cygpath_pairs)
     def test_decygpath(self, case):
         wpath, cpath = case
@@ -156,11 +165,6 @@ def test_lock_file(self):
         lock_file._obtain_lock_or_raise()
         lock_file._release_lock()
 
-    @pytest.mark.xfail(
-        sys.platform == "cygwin",
-        reason="Cygwin fails here for some reason, always",
-        raises=AssertionError,
-    )
     def test_blocking_lock_file(self):
         my_file = tempfile.mktemp()
         lock_file = BlockingLockFile(my_file)
@@ -173,9 +177,8 @@ def test_blocking_lock_file(self):
         self.assertRaises(IOError, wait_lock._obtain_lock)
         elapsed = time.time() - start
         extra_time = 0.02
-        if is_win:
-            # for Appveyor
-            extra_time *= 6  # NOTE: Indeterministic failures here...
+        if is_win or sys.platform == "cygwin":
+            extra_time *= 6  # NOTE: Indeterministic failures without this...
         self.assertLess(elapsed, wait_time + extra_time)
 
     def test_user_id(self):