feat: wrap palette selection; fix push classing, file walk, card load
- Palette Ctrl-N/P (and half-page keys) wrap around the result list instead of clamping at the ends; tests updated to the new semantics. - try_push maps libgit2's negotiation-time NotFastForward error to PushFailure::Rejected so the reconcile path runs instead of failing as a transport error. - walk_files decodes esp-idf d_type values (DT_REG=1/DT_DIR=2) that libc 0.2.178's generic unix table misreads as fifo/chardev — the palette walk was silently dropping every file on the card. - `just load` refuses to mirror over unpublished device edits listed in the card's .typoena-dirty journal (backup+discard via TW_DISCARD_UNPUBLISHED=1), gains --force for a from-scratch copy, and a flash-only recipe flashes the already-built ELF.
This commit is contained in:
@@ -510,9 +510,20 @@ fn try_push(repo: &Repository, refspec: &str) -> Result<(), PushFailure> {
|
||||
|
||||
let mut opts = PushOptions::new();
|
||||
opts.remote_callbacks(cbs);
|
||||
remote
|
||||
.push(&[refspec], Some(&mut opts))
|
||||
.map_err(|e| PushFailure::Other(anyhow::Error::new(e).context("push transport")))?;
|
||||
// A non-fast-forward can also surface here, not just via the callback:
|
||||
// libgit2 compares against origin's advertised tips during negotiation and
|
||||
// errors out of push() with ErrorCode::NotFastForward before sending
|
||||
// anything, so `push_update_reference` never fires. It's still the
|
||||
// remote-moved-under-us case — reconcilable, not a transport failure
|
||||
// (bit the 2026-07-13 run 3: the real-repo rejection surfaced as "push
|
||||
// transport" and skipped the reconcile built for it).
|
||||
remote.push(&[refspec], Some(&mut opts)).map_err(|e| {
|
||||
if e.code() == git2::ErrorCode::NotFastForward {
|
||||
PushFailure::Rejected(format!("{refspec}: {}", e.message()))
|
||||
} else {
|
||||
PushFailure::Other(anyhow::Error::new(e).context("push transport"))
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Some(msg) = rejection.borrow().clone() {
|
||||
return Err(PushFailure::Rejected(msg));
|
||||
|
||||
@@ -596,11 +596,20 @@ fn walk_files(dir: &std::path::Path, depth: usize, out: &mut Vec<String>) {
|
||||
let Ok(entries) = std::fs::read_dir(dir) else {
|
||||
return;
|
||||
};
|
||||
// Keep the dirent's own file type: esp-idf's FAT VFS always fills d_type
|
||||
// (DT_DIR/DT_REG, straight from the FILINFO readdir already holds), so
|
||||
// `file_type()` is free. A per-entry `metadata()` stat instead re-walks
|
||||
// the directory by path every time — measured at ~32ms/file on the SD
|
||||
// card, it turned a 1098-file walk into 35s.
|
||||
// Keep the dirent's own file type — a per-entry `metadata()` stat re-walks
|
||||
// the directory by path every time (~32ms/file on the SD card; it turned a
|
||||
// 1098-file walk into 35s). But the type needs decoding: esp-idf's
|
||||
// dirent.h says DT_REG=1 / DT_DIR=2, and std was built against libc
|
||||
// 0.2.178, which had no espidf overrides (they arrived in 0.2.186) and
|
||||
// falls back to the generic unix table — DT_FIFO=1, DT_CHR=2, DT_DIR=4,
|
||||
// DT_REG=8. Through std's eyes every card file is a "fifo" and every
|
||||
// directory a "char device": is_file()/is_dir() never matched, and the
|
||||
// 2026-07-13 walk dropped all 1157 files in 49ms. FAT can't hold fifos or
|
||||
// device nodes, so reading fifo-as-file / chardev-as-dir is unambiguous
|
||||
// here, and the is_file()/is_dir() arms take over the day the toolchain's
|
||||
// libc catches up. A type matching neither pair pays the one stat rather
|
||||
// than being silently dropped.
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
let children: Vec<_> = entries
|
||||
.flatten()
|
||||
.filter_map(|e| e.file_type().ok().map(|t| (e.path(), t)))
|
||||
@@ -612,11 +621,21 @@ fn walk_files(dir: &std::path::Path, depth: usize, out: &mut Vec<String>) {
|
||||
if name.starts_with('.') {
|
||||
continue;
|
||||
}
|
||||
if ftype.is_file() {
|
||||
let (is_file, is_dir) = if ftype.is_file() || ftype.is_fifo() {
|
||||
(true, false)
|
||||
} else if ftype.is_dir() || ftype.is_char_device() {
|
||||
(false, true)
|
||||
} else {
|
||||
match std::fs::metadata(&path) {
|
||||
Ok(m) => (m.is_file(), m.is_dir()),
|
||||
Err(_) => continue,
|
||||
}
|
||||
};
|
||||
if is_file {
|
||||
if let Some(p) = path.to_str() {
|
||||
out.push(p.to_string());
|
||||
}
|
||||
} else if ftype.is_dir() {
|
||||
} else if is_dir {
|
||||
walk_files(&path, depth + 1, out);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user