feat(sync): :gl pull — fetch + fast-forward only on the git thread

The git channel now carries GitRequest::{Publish,Pull} with a shared
ensure_online preamble (Wi-Fi/clock/TLS once per session). pull_once
fetches origin — refreshing the tracking ref so the radio-free
up-to-date check stays honest — and maps four shapes: up to date,
LocalAhead (stranded commit, :sync's job), clean fast-forward,
Diverged (refused; no merge on the device). The fast-forward is a SAFE
checkout then ref move: it refuses to overwrite content that differs
from HEAD, the belt under the UI gate that refuses :gl while the dirty
journal is non-empty. A RAM-dirty buffer doesn't gate — its edits win
(last-writer-wins, like the reconcile).

After a pull the UI drops clean parked buffers, re-reads the clean
active buffer in place (Editor::refresh_active), and re-walks the
palette list. Firmware 0.6.0 -> 0.7.0. On-device verification pending.
This commit is contained in:
Julien Calixte
2026-07-14 01:10:00 +02:00
parent 8d22f81804
commit a941ae39b3
6 changed files with 415 additions and 72 deletions

View File

@@ -429,6 +429,15 @@ impl Storage {
}
}
/// Whether any saved-but-unpublished paths are recorded (pending or riding
/// an in-flight publish). `:gl` refuses to pull while this is true: a
/// fast-forward checkout would fight those files, and `:sync` first is the
/// single-writer appliance's natural order anyway.
pub fn has_dirty(&self) -> bool {
let d = self.dirty.borrow();
!d.pending.is_empty() || !d.in_flight.is_empty()
}
/// Snapshot the dirty paths for a publish (repo-relative). The snapshot
/// moves to `in_flight` — the journal keeps carrying it — until the UI
/// task reports the outcome: [`Storage::publish_succeeded`] forgets it,