p_mmap emulated a mapping by malloc+read()ing the range from SD on every
call. libgit2 re-hits the pack idx/windows on every object write (via
git_odb__freshen -> git_odb_refresh), so a commit re-read pack bytes over
SPI repeatedly -- ~500ms-1.1s/op on a small repo, far worse on the real
570 MB pack.
Cache read-only mappings >= 64 KB (pack idx/windows, commit-graph, midx,
packed-refs -- all immutable on this device) in a refcounted, PSRAM-backed
slot table keyed by (dev, ino, size, mtime, offset, len), LRU-evicted under
a soft cap. Small mutable working-tree maps (diff_file.c) fall below the
floor and are never served stale.
libgit2 creates loose objects and packfiles with mode 0444 (read-only). FATFS
turns that into the AM_RDO attribute and then refuses to f_unlink them (EACCES),
and esp-idf's FATFS VFS chmod cannot clear AM_RDO — so a written object can
never be deleted, blocking re-clone recovery and (later) fetch/repack.
Shim p_open/p_creat in esp_stubs.c to force owner-write into every create mode
(compile posix.c's originals under throwaway names, same mechanism as p_rename),
so nothing is ever created read-only. Immutability is only a safety hint on an
appliance where nothing but libgit2 touches these files.
Non-regressive on hardware: git_push, which links the same component, still
commits + pushes cleanly with the shim. The delete path itself is validated via
git_sync's RECLONE_EACH_BOOT knob (pending a dedicated flash).
The loose ODB silently dropped every write on device: utimes() returned 0
unconditionally, so libgit2's freshen probe (git_futils_touch -> p_utimes)
always reported "object exists" and git_odb_write skipped the write entirely
-- blobs/trees/commits never hit disk and write_tree failed with "invalid
object specified". Gate utimes on file existence (present -> 0, absent ->
ENOENT). Also add a remove-then-rename p_rename (FATFS f_rename can't replace
an existing target, no hardlinks), with posix.c's original scoped out in the
component CMakeLists, plus symlink/gai_strerror link stubs git_push pulls in.
p_rename was verified on hardware (cleared the rename error); the utimes fix
is diagnosed from the failure + libgit2 source and build-verified, on-device
confirmation still pending.
Builds vendored libgit2 as an esp-idf component so it inherits the
lwip/mbedtls/pthread/vfs/newlib include+link graph -- the include cascade
that sank CFLAGS injection never appears. Configured for mbedTLS via a
hand-written git2_features.h. Small port surface, libgit2 sources
untouched: poll.h shim, lstat==stat (esp_port.h), p_mmap via malloc+read
(esp_map.c), and identity/symlink stubs (esp_stubs.c). Registers empty
when LIBGIT2_SRC is unset so the editor build is unaffected.
Pins the exact version libgit2-sys 0.18.5 expects, so git2's safe API can
bind it in system mode. Sits under the esp-idf component that builds it;
clone with --recurse-submodules to fetch the source.