diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5acde1a..2fe73ca 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,4 +8,4 @@ updates:
 - package-ecosystem: "gitsubmodule"
   directory: "/"
   schedule:
-    interval: "monthly"
+    interval: "weekly"
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 73b3902..907698d 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -8,12 +8,17 @@ on: [push, pull_request, workflow_dispatch]
 jobs:
   build:
 
-    runs-on: ubuntu-latest
+    runs-on: ${{ matrix.os }}
     strategy:
+      fail-fast: false
       matrix:
-        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+        os: [ubuntu-latest]
+        experimental: [false]
         include:
-        - experimental: false
+        - python-version: "3.7"
+          os: ubuntu-22.04
+          experimental: false
     continue-on-error: ${{ matrix.experimental }}
 
     steps:
@@ -21,7 +26,7 @@ jobs:
       with:
         fetch-depth: 0
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: ${{ matrix.python-version }}
         allow-prereleases: ${{ matrix.experimental }}
diff --git a/Makefile b/Makefile
index 7aa5a71..20436bb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,40 +1,12 @@
-PYTHON = python3
-SETUP = $(PYTHON) setup.py
-TESTFLAGS =
+.PHONY: all clean release force_release
 
-all::
+all:
 	@grep -Ee '^[a-z].*:' Makefile | cut -d: -f1 | grep -vF all
 
-release:: clean
-	# Check if latest tag is the current head we're releasing
-	echo "Latest tag = $$(git tag | sort -nr | head -n1)"
-	echo "HEAD SHA       = $$(git rev-parse head)"
-	echo "Latest tag SHA = $$(git tag | sort -nr | head -n1 | xargs git rev-parse)"
-	@test "$$(git rev-parse head)" = "$$(git tag | sort -nr | head -n1 | xargs git rev-parse)"
-	make force_release
+clean:
+	rm -rf build/ dist/ .eggs/ .tox/
 
-force_release:: clean
-	git push --tags
-	python3 setup.py sdist bdist_wheel
+force_release: clean
+	./build-release.sh
 	twine upload dist/*
-
-doc:: 
-	make -C doc/ html
-
-build::
-	$(SETUP) build
-	$(SETUP) build_ext -i
-
-build_ext::
-	$(SETUP) build_ext -i
-	
-install::
-	$(SETUP) install
-
-clean::
-	$(SETUP) clean --all
-	rm -f *.so
-
-coverage:: build
-	PYTHONPATH=. $(PYTHON) -m pytest --cov=gitdb gitdb
-
+	git push --tags origin master
diff --git a/README.rst b/README.rst
index 29c70f7..61ce28b 100644
--- a/README.rst
+++ b/README.rst
@@ -16,34 +16,38 @@ Installation
     :target: https://readthedocs.org/projects/gitdb/?badge=latest
     :alt: Documentation Status
 
-From `PyPI <https://pypi.python.org/pypi/gitdb>`_
+From `PyPI <https://pypi.python.org/pypi/gitdb>`_::
 
  pip install gitdb
 
 SPEEDUPS
 ========
 
-If you want to go up to 20% faster, you can install gitdb-speedups with:
+If you want to go up to 20% faster, you can install gitdb-speedups with::
 
  pip install gitdb-speedups
 
+However, please note that gitdb-speedups is not currently maintained.
+
 REQUIREMENTS
 ============
 
+* smmap - declared as a dependency, automatically installed
 * pytest - for running the tests
 
 SOURCE
 ======
-The source is available in a git repository at gitorious and github:
+
+The source is available in a git repository on GitHub:
 
 https://github.com/gitpython-developers/gitdb
 
-Once the clone is complete, please be sure to initialize the submodules using
+Once the clone is complete, please be sure to initialize the submodule using::
 
  cd gitdb
  git submodule update --init
 
-Run the tests with
+Run the tests with::
 
  pytest
 
@@ -53,13 +57,13 @@ DEVELOPMENT
 .. image:: https://github.com/gitpython-developers/gitdb/workflows/Python%20package/badge.svg
     :target: https://github.com/gitpython-developers/gitdb/actions
 
-The library is considered mature, and not under active development. It's primary (known) use is in git-python.
+The library is considered mature, and not under active development. Its primary (known) use is in GitPython.
 
 INFRASTRUCTURE
 ==============
 
-* Mailing List
-    * http://groups.google.com/group/git-python
+* Discussions
+    * https://github.com/gitpython-developers/GitPython/discussions
 
 * Issue Tracker
     * https://github.com/gitpython-developers/gitdb/issues
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..95389ff
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,3 @@
+# Security Policy
+
+See [GitPython](https://github.com/gitpython-developers/GitPython/blob/main/SECURITY.md). Vulnerabilities found in `gitdb` can be reported there.
diff --git a/build-release.sh b/build-release.sh
new file mode 100755
index 0000000..5840e44
--- /dev/null
+++ b/build-release.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# This script builds a release. If run in a venv, it auto-installs its tools.
+# You may want to run "make release" instead of running this script directly.
+
+set -eEu
+
+function release_with() {
+    $1 -m build --sdist --wheel
+}
+
+if test -n "${VIRTUAL_ENV:-}"; then
+    deps=(build twine)  # Install twine along with build, as we need it later.
+    echo "Virtual environment detected. Adding packages: ${deps[*]}"
+    pip install --quiet --upgrade "${deps[@]}"
+    echo 'Starting the build.'
+    release_with python
+else
+    function suggest_venv() {
+        venv_cmd='python -m venv env && source env/bin/activate'
+        printf "HELP: To avoid this error, use a virtual-env with '%s' instead.\n" "$venv_cmd"
+    }
+    trap suggest_venv ERR  # This keeps the original exit (error) code.
+    echo 'Starting the build.'
+    release_with python3 # Outside a venv, use python3.
+fi
diff --git a/doc/source/changes.rst b/doc/source/changes.rst
index 0b8de13..b4340e4 100644
--- a/doc/source/changes.rst
+++ b/doc/source/changes.rst
@@ -2,6 +2,12 @@
 Changelog
 #########
 
+******
+4.0.12
+******
+
+- various improvements - please see the release on GitHub for details.
+
 ******
 4.0.11
 ******
diff --git a/gitdb/__init__.py b/gitdb/__init__.py
index 803a428..1fb7df8 100644
--- a/gitdb/__init__.py
+++ b/gitdb/__init__.py
@@ -4,34 +4,12 @@
 # the New BSD License: https://opensource.org/license/bsd-3-clause/
 """Initialize the object database module"""
 
-import sys
-import os
-
-#{ Initialization
-
-
-def _init_externals():
-    """Initialize external projects by putting them into the path"""
-    if 'PYOXIDIZER' not in os.environ:
-        where = os.path.join(os.path.dirname(__file__), 'ext', 'smmap')
-        if os.path.exists(where):
-            sys.path.append(where)
-
-    import smmap
-    del smmap
-    # END handle imports
-
-#} END initialization
-
-_init_externals()
-
 __author__ = "Sebastian Thiel"
 __contact__ = "byronimo@gmail.com"
 __homepage__ = "https://github.com/gitpython-developers/gitdb"
-version_info = (4, 0, 11)
+version_info = (4, 0, 12)
 __version__ = '.'.join(str(i) for i in version_info)
 
-
 # default imports
 from gitdb.base import *
 from gitdb.db import *
diff --git a/gitdb/db/loose.py b/gitdb/db/loose.py
index 256fec9..e6765cd 100644
--- a/gitdb/db/loose.py
+++ b/gitdb/db/loose.py
@@ -2,6 +2,8 @@
 #
 # This module is part of GitDB and is released under
 # the New BSD License: https://opensource.org/license/bsd-3-clause/
+from contextlib import suppress
+
 from gitdb.db.base import (
     FileDBBase,
     ObjectDBR,
@@ -52,6 +54,7 @@
 import tempfile
 import os
 import sys
+import time
 
 
 __all__ = ('LooseObjectDB', )
@@ -90,10 +93,8 @@ def readable_db_object_path(self, hexsha):
         """
         :return: readable object path to the object identified by hexsha
         :raise BadObject: If the object file does not exist"""
-        try:
+        with suppress(KeyError):
             return self._hexsha_to_file[hexsha]
-        except KeyError:
-            pass
         # END ignore cache misses
 
         # try filesystem
@@ -205,7 +206,7 @@ def store(self, istream):
             # END assure target stream is closed
         except:
             if tmp_path:
-                os.remove(tmp_path)
+                remove(tmp_path)
             raise
         # END assure tmpfile removal on error
 
@@ -228,9 +229,22 @@ def store(self, istream):
                 rename(tmp_path, obj_path)
             # end rename only if needed
 
-            # make sure its readable for all ! It started out as rw-- tmp file
-            # but needs to be rwrr
-            chmod(obj_path, self.new_objects_mode)
+            # Ensure rename is actually done and file is stable
+            # Retry up to 14 times - quadratic wait & retry in ms.
+            # The total maximum wait time is 1000ms, which should be vastly enough for the
+            # OS to return and commit the file to disk.
+            for backoff_ms in [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 181]:
+                with suppress(PermissionError):
+                    # make sure its readable for all ! It started out as rw-- tmp file
+                    # but needs to be rwrr
+                    chmod(obj_path, self.new_objects_mode)
+                    break
+                time.sleep(backoff_ms / 1000.0)
+            else:
+                raise PermissionError(
+                    "Impossible to apply `chmod` to file {}".format(obj_path)
+                )
+
         # END handle dry_run
 
         istream.binsha = hex_to_bin(hexsha)
diff --git a/gitdb/ext/smmap b/gitdb/ext/smmap
index 256c5a2..8f82e6c 160000
--- a/gitdb/ext/smmap
+++ b/gitdb/ext/smmap
@@ -1 +1 @@
-Subproject commit 256c5a21de2d14aca02c9689d7d63f78c4e0ef61
+Subproject commit 8f82e6c19661f9b735cc55cc89031a189e408894
diff --git a/gitdb/test/test_base.py b/gitdb/test/test_base.py
index 8fc9e35..17906c9 100644
--- a/gitdb/test/test_base.py
+++ b/gitdb/test/test_base.py
@@ -73,7 +73,7 @@ def test_streams(self):
 
         # test deltapackstream
         dpostream = ODeltaPackStream(*(dpinfo + (stream, )))
-        dpostream.stream is stream
+        assert dpostream.stream is stream
         dpostream.read(5)
         stream._assert()
         assert stream.bytes == 5
@@ -92,7 +92,7 @@ def test_streams(self):
 
         assert istream.size == s
         istream.size = s * 2
-        istream.size == s * 2
+        assert istream.size == s * 2
         assert istream.type == str_blob_type
         istream.type = "something"
         assert istream.type == "something"
diff --git a/setup.py b/setup.py
index f67f7a5..3a91543 100755
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
 __author__ = "Sebastian Thiel"
 __contact__ = "byronimo@gmail.com"
 __homepage__ = "https://github.com/gitpython-developers/gitdb"
-version_info = (4, 0, 11)
+version_info = (4, 0, 12)
 __version__ = '.'.join(str(i) for i in version_info)
 
 setup(
@@ -41,6 +41,7 @@
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Programming Language :: Python :: 3 :: Only",
     ]
 )