fix(sync): apply the pull fast-forward as a tree diff, not checkout_tree

First on-device ff attempt (2026-07-14) crashed: libgit2's SAFE
checkout iterates the whole working directory (readdir over SPI on all
~1100 files) to find dirty files — the same O(tree) wall the splice
commit avoids — and with fetch memory still resident it exhausted
internal DRAM; esp-idf's spi_master then NULL-derefs on its own
failed-DMA-alloc path instead of erroring.

apply_tree_diff mirrors the splice: diff_tree_to_tree skips identical
subtree OIDs in memory, then writes/deletes exactly the changed paths
(unlink+tmp+rename — FAT rename won't overwrite). Belt kept O(changed):
any to-be-clobbered file whose content no longer hashes to the old blob
aborts the pull before the first write. Half-applied + unmoved ref
self-heals on the next :gl (idempotent re-apply).

Also raise the internal DMA reserve 32→64 KB so the SPI driver never
sees a failed alloc (its error path is the crash), and log the fetch
completion so the fetch/apply phases are separable in the serial log.
This commit is contained in:
Julien Calixte
2026-07-14 09:17:36 +02:00
parent 0734c1f67e
commit 329995ab58
2 changed files with 116 additions and 6 deletions

View File

@@ -43,6 +43,14 @@ CONFIG_FATFS_LFN_HEAP=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_USE_MALLOC=y
# Internal reserve for DMA/internal-only allocations (default 32 KB). The
# first on-device `:gl` fast-forward (2026-07-14) exhausted internal DRAM
# mid-checkout and an SD-SPI DMA bounce-buffer alloc failed — esp-idf's
# spi_master then NULL-derefs on its own error path (LoadProhibited in
# setup/uninstall_priv_desc) instead of returning, so the DMA floor must be
# generous enough that the driver never sees a failed alloc. 64 KB, paired
# with the file-list interning that returns ~180 KB to internal.
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
# FatFS fast seek (sync-latency root cause, 2026-07-12). Without it, lseek
# resolves by walking the file's FAT cluster chain over SPI — forward from the