diff --git a/.github/workflows/cygwin-test.yml b/.github/workflows/cygwin-test.yml
index 808dc5608..962791ae7 100644
--- a/.github/workflows/cygwin-test.yml
+++ b/.github/workflows/cygwin-test.yml
@@ -12,38 +12,53 @@ jobs:
       SHELLOPTS: igncr
       TMP: "/tmp"
       TEMP: "/tmp"
+    defaults:
+      run:
+        shell: bash.exe --noprofile --norc -exo pipefail -o igncr "{0}"
 
     steps:
     - name: Force LF line endings
       run: git config --global core.autocrlf input
+
     - uses: actions/checkout@v4
       with:
-        fetch-depth: 9999
+        fetch-depth: 0
+        submodules: recursive
+
     - uses: cygwin/cygwin-install-action@v4
       with:
         packages: python39 python39-pip python39-virtualenv git
+
+    - name: Show python and git versions
+      run: |
+        /usr/bin/python --version
+        /usr/bin/git version
+
     - name: Tell git to trust this repo
-      shell: bash.exe -eo pipefail -o igncr "{0}"
       run: |
-          /usr/bin/git config --global --add safe.directory "$(pwd)"
-    - name: Install dependencies and prepare tests
-      shell: bash.exe -eo pipefail -o igncr "{0}"
+        /usr/bin/git config --global --add safe.directory "$(pwd)"
+
+    - name: Prepare this repo for tests
       run: |
-        set -x
-        /usr/bin/python -m pip install --upgrade pip setuptools wheel
-        /usr/bin/python --version; /usr/bin/git --version
-        /usr/bin/git submodule update --init --recursive
-        /usr/bin/git fetch --tags
-        /usr/bin/python -m pip install -r requirements.txt
-        /usr/bin/python -m pip install -r test-requirements.txt
         TRAVIS=yes ./init-tests-after-clone.sh
+
+    - name: Further prepare git configuration for tests
+      run: |
         /usr/bin/git config --global user.email "travis@ci.com"
         /usr/bin/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
+
+    - name: Install project and test dependencies
+      run: |
+        /usr/bin/python -m pip install ".[test]"
+
     - name: Test with pytest
-      shell: bash.exe -eo pipefail -o igncr "{0}"
       run: |
+        set +x
         /usr/bin/python -m pytest
-      continue-on-error: false
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index a6af507d1..a5467ef94 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -15,51 +15,70 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+        include:
+          - experimental: false
+          - python-version: "3.12"
+            experimental: true
+    defaults:
+      run:
+        shell: /bin/bash --noprofile --norc -exo pipefail {0}
 
     steps:
     - uses: actions/checkout@v4
       with:
-        fetch-depth: 9999
+        fetch-depth: 0
+        submodules: recursive
+
     - name: Set up Python ${{ matrix.python-version }}
       uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
-    - name: Install dependencies and prepare tests
-      run: |
-        set -x
+        allow-prereleases: ${{ matrix.experimental }}
 
-        python -m pip install --upgrade pip setuptools wheel
-        python --version; git --version
-        git submodule update --init --recursive
-        git fetch --tags --force
+    - name: Show python and git versions
+      run: |
+        python --version
+        git version
 
-        pip install -r requirements.txt
-        pip install -r test-requirements.txt
+    - name: Prepare this repo for tests
+      run: |
         TRAVIS=yes ./init-tests-after-clone.sh
 
+    - name: Prepare git configuration for tests
+      run: |
         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: |
+        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
+
+    - name: Install project and test dependencies
+      run: |
+        pip install ".[test]"
+
     - name: Check types with mypy
-      # With new versions of pypi new issues might arise. This is a problem if there is nobody able to fix them,
-      # so we have to ignore errors until that changes.
-      continue-on-error: true
       run: |
-        set -x
         mypy -p git
+      # With new versions of mypy new issues might arise. This is a problem if there is nobody able to fix them,
+      # so we have to ignore errors until that changes.
+      continue-on-error: true
 
     - name: Test with pytest
       run: |
-        set -x
         pytest
       continue-on-error: false
 
     - name: Documentation
       run: |
-        set -x
         pip install -r doc/requirements.txt
         make -C doc html
diff --git a/Makefile b/Makefile
index 2af8de084..f2cbf826a 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,8 @@ release: clean
 	make force_release
 
 force_release: clean
-	git push --tags origin main
-	python3 setup.py sdist bdist_wheel
+	# IF we're in a virtual environment, add build tools
+	test -z "$$VIRTUAL_ENV" || pip install -U build twine
+	python3 -m build --sdist --wheel
 	twine upload dist/*
+	git push --tags origin main
diff --git a/README.md b/README.md
index 94fcc76d9..ca470a851 100644
--- a/README.md
+++ b/README.md
@@ -49,30 +49,51 @@ The installer takes care of installing them for you.
 
 ### INSTALL
 
-If you have downloaded the source code:
+GitPython and its required package dependencies can be installed in any of the following ways, all of which should typically be done in a [virtual environment](https://docs.python.org/3/tutorial/venv.html).
 
-```bash
-python setup.py install
-```
+#### From PyPI
 
-or if you want to obtain a copy from the Pypi repository:
+To obtain and install a copy [from PyPI](https://pypi.org/project/GitPython/), run:
 
 ```bash
 pip install GitPython
 ```
 
-Both commands will install the required package dependencies.
+(A distribution package can also be downloaded for manual installation at [the PyPI page](https://pypi.org/project/GitPython/).)
+
+#### From downloaded source code
+
+If you have downloaded the source code, run this from inside the unpacked `GitPython` directory:
+
+```bash
+pip install .
+```
 
-A distribution package can be obtained for manual installation at: <http://pypi.python.org/pypi/GitPython>.
+#### By cloning the source code repository
 
-If you like to clone from source, you can do it like so:
+To clone the [the GitHub repository](https://github.com/gitpython-developers/GitPython) from source to work on the code, you can do it like so:
 
 ```bash
 git clone https://github.com/gitpython-developers/GitPython
-git submodule update --init --recursive
+cd GitPython
+git fetch --tags
 ./init-tests-after-clone.sh
 ```
 
+If you are cloning [your own fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then replace the above `git clone` command with one that gives the URL of your fork. Or use this [`gh`](https://cli.github.com/) command (assuming you have `gh` and your fork is called `GitPython`):
+
+```bash
+gh repo clone GitPython
+```
+
+Having cloned the repo, create and activate your [virtual environment](https://docs.python.org/3/tutorial/venv.html). Then make an [editable install](https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs):
+
+```bash
+pip install -e ".[test]"
+```
+
+In the less common case that you do not want to install test dependencies, `pip install -e .` can be used instead.
+
 ### Limitations
 
 #### Leakage of System Resources
@@ -101,20 +122,49 @@ On _Windows_, make sure you have `git-daemon` in your PATH. For MINGW-git, the `
 exists in `Git\mingw64\libexec\git-core\`; CYGWIN has no daemon, but should get along fine
 with MINGW's.
 
-Ensure testing libraries are installed.
-In the root directory, run: `pip install -r test-requirements.txt`
+#### Install test dependencies
 
-To lint, run: `pre-commit run --all-files`
+Ensure testing libraries are installed. This is taken care of already if you installed with:
 
-To typecheck, run: `mypy -p git`
+```bash
+pip install -e ".[test]"
+```
 
-To test, run: `pytest`
+Otherwise, you can run:
 
-For automatic code formatting run: `black git`
+```bash
+pip install -r test-requirements.txt
+```
+
+#### Test commands
+
+To test, run:
+
+```bash
+pytest
+```
+
+To lint, run:
+
+```bash
+pre-commit run --all-files
+```
+
+To typecheck, run:
+
+```bash
+mypy -p git
+```
+
+For automatic code formatting, run:
+
+```bash
+black git
+```
 
-Configuration for flake8 is in the ./.flake8 file.
+Configuration for flake8 is in the `./.flake8` file.
 
-Configurations for mypy, pytest and coverage.py are in ./pyproject.toml.
+Configurations for `mypy`, `pytest`, `coverage.py`, and `black` are in `./pyproject.toml`.
 
 The same linting and testing will also be performed against different supported python versions
 upon submitting a pull request (or on each push if you have a fork with a "main" branch and actions enabled).
@@ -138,13 +188,15 @@ Please have a look at the [contributions file][contributing].
 
 ### How to make a new release
 
-- Update/verify the **version** in the `VERSION` file
-- Update/verify that the `doc/source/changes.rst` changelog file was updated
-- Commit everything
-- Run `git tag -s <version>` to tag the version in Git
-- Run `make release`
+- Update/verify the **version** in the `VERSION` file.
+- Update/verify that the `doc/source/changes.rst` changelog file was updated.
+- Commit everything.
+- Run `git tag -s <version>` to tag the version in Git.
+- _Optionally_ create and activate a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) using `venv` or `virtualenv`.\
+(When run in a virtual environment, the next step will automatically take care of installing `build` and `twine` in it.)
+- Run `make release`.
 - Close the milestone mentioned in the _changelog_ and create a new one. _Do not reuse milestones by renaming them_.
-- Got to [GitHub Releases](https://github.com/gitpython-developers/GitPython/releases) and publish a new one with the recently pushed tag. Generate the changelog.
+- Go to [GitHub Releases](https://github.com/gitpython-developers/GitPython/releases) and publish a new one with the recently pushed tag. Generate the changelog.
 
 ### How to verify a release (DEPRECATED)
 
diff --git a/VERSION b/VERSION
index d87cdbb81..b402c1a8b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1.35
+3.1.36
diff --git a/doc/source/changes.rst b/doc/source/changes.rst
index 6302176bd..06ec4b72c 100644
--- a/doc/source/changes.rst
+++ b/doc/source/changes.rst
@@ -2,6 +2,14 @@
 Changelog
 =========
 
+3.1.36
+======
+
+Note that this release should be a no-op, it's mainly for testing the changed release-process.
+
+See the following for all changes.
+https://github.com/gitpython-developers/gitpython/milestone/66?closed=1
+
 3.1.35
 ======
 
diff --git a/init-tests-after-clone.sh b/init-tests-after-clone.sh
index e852f3cd9..95ced98b7 100755
--- a/init-tests-after-clone.sh
+++ b/init-tests-after-clone.sh
@@ -1,7 +1,9 @@
-#!/bin/bash -e
+#!/usr/bin/env bash
+
+set -e
 
 if [[ -z "$TRAVIS" ]]; then
-  read -p "This operation will destroy locally modified files. Continue ? [N/y]: " answer
+  read -rp "This operation will destroy locally modified files. Continue ? [N/y]: " answer
   if [[ ! $answer =~ [yY] ]]; then
     exit 2
   fi
@@ -13,4 +15,4 @@ git reset --hard HEAD~1
 git reset --hard HEAD~1
 git reset --hard HEAD~1
 git reset --hard __testing_point__
-git submodule update --init --recursive
\ No newline at end of file
+git submodule update --init --recursive
diff --git a/pyproject.toml b/pyproject.toml
index 32c9d4a26..42bb31eda 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
 [build-system]
-requires = ["setuptools", "wheel"]
+requires = ["setuptools"]
 build-backend = "setuptools.build_meta"
 
 [tool.pytest.ini_options]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 946b4c94f..f6705341c 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -4,7 +4,6 @@
 # libraries for additional local testing/linting - to be added to test-requirements.txt when all pass
 
 flake8-type-checking;python_version>="3.8"      # checks for TYPE_CHECKING only imports
-black
 
 pytest-icdiff
 # pytest-profiling
diff --git a/setup.py b/setup.py
index ebece64eb..bc53bf6c8 100755
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 from typing import Sequence
 from setuptools import setup, find_packages
 from setuptools.command.build_py import build_py as _build_py
@@ -6,8 +8,8 @@
 import os
 import sys
 
-with open(os.path.join(os.path.dirname(__file__), "VERSION")) as v:
-    VERSION = v.readline().strip()
+with open(os.path.join(os.path.dirname(__file__), "VERSION")) as ver_file:
+    VERSION = ver_file.readline().strip()
 
 with open("requirements.txt") as reqs_file:
     requirements = reqs_file.read().splitlines()
@@ -47,7 +49,7 @@ def _stamp_version(filename: str) -> None:
         with open(filename) as f:
             for line in f:
                 if "__version__ =" in line:
-                    line = line.replace("\"git\"", "'%s'" % VERSION)
+                    line = line.replace('"git"', "'%s'" % VERSION)
                     found = True
                 out.append(line)
     except OSError:
@@ -93,7 +95,7 @@ def build_py_modules(basedir: str, excludes: Sequence = ()) -> Sequence:
     package_dir={"git": "git"},
     python_requires=">=3.7",
     install_requires=requirements,
-    tests_require=requirements + test_requirements,
+    extras_require={"test": test_requirements},
     zip_safe=False,
     long_description=long_description,
     long_description_content_type="text/markdown",
@@ -122,5 +124,6 @@ def build_py_modules(basedir: str, excludes: Sequence = ()) -> Sequence:
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
+        "Programming Language :: Python :: 3.12",
     ],
 )
diff --git a/test-requirements.txt b/test-requirements.txt
index 6c6d57060..62f409824 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,15 +1,9 @@
+black
+coverage[toml]
 ddt>=1.1.1, !=1.4.3
 mypy
-
-black
-
 pre-commit
-
-virtualenv
-
 pytest
 pytest-cov
-coverage[toml]
 pytest-sugar
-
-gitdb
+virtualenv
diff --git a/test/test_installation.py b/test/test_installation.py
index c092aef5e..d856ebc94 100644
--- a/test/test_installation.py
+++ b/test/test_installation.py
@@ -4,6 +4,7 @@
 import ast
 import os
 import subprocess
+from git.compat import is_win
 from test.lib import TestBase
 from test.lib.helper import with_rw_directory
 
@@ -12,8 +13,9 @@ class TestInstallation(TestBase):
     def setUp_venv(self, rw_dir):
         self.venv = rw_dir
         subprocess.run(["virtualenv", self.venv], stdout=subprocess.PIPE)
-        self.python = os.path.join(self.venv, "bin/python3")
-        self.pip = os.path.join(self.venv, "bin/pip3")
+        bin_name = "Scripts" if is_win else "bin"
+        self.python = os.path.join(self.venv, bin_name, "python")
+        self.pip = os.path.join(self.venv, bin_name, "pip")
         self.sources = os.path.join(self.venv, "src")
         self.cwd = os.path.dirname(os.path.dirname(__file__))
         os.symlink(self.cwd, self.sources, target_is_directory=True)
@@ -22,24 +24,14 @@ def setUp_venv(self, rw_dir):
     def test_installation(self, rw_dir):
         self.setUp_venv(rw_dir)
         result = subprocess.run(
-            [self.pip, "install", "-r", "requirements.txt"],
+            [self.pip, "install", "."],
             stdout=subprocess.PIPE,
             cwd=self.sources,
         )
         self.assertEqual(
             0,
             result.returncode,
-            msg=result.stderr or result.stdout or "Can't install requirements",
-        )
-        result = subprocess.run(
-            [self.python, "setup.py", "install"],
-            stdout=subprocess.PIPE,
-            cwd=self.sources,
-        )
-        self.assertEqual(
-            0,
-            result.returncode,
-            msg=result.stderr or result.stdout or "Can't build - setup.py failed",
+            msg=result.stderr or result.stdout or "Can't install project",
         )
         result = subprocess.run([self.python, "-c", "import git"], stdout=subprocess.PIPE, cwd=self.sources)
         self.assertEqual(