on master help sign in
Add a native touchedPaths builtin; make status O(changed) not O(paths^2)
d1806bciko
config.j+3−3
@@ -141,6 +141,8 @@ splitOn : Text -> Text -> [Text] -- splitOn "/" "a/b" = ["a" "b"]
141-- path's blob is unresolved rather than an error.
141-- path's blob is unresolved rather than an error.
142replay : Snapshot -> Change -> Snapshot
142replay : Snapshot -> Change -> Snapshot
143unresolved : Blob -> Bool
143unresolved : Blob -> Bool
144+touchedPaths : Change -> [Path] -- paths whose content differs, one pass;
145+ -- blobs compared by content id, bytes never read
144blob : Text -> Blob -- a resolved regular file
146blob : Text -> Blob -- a resolved regular file
145text : Blob -> Text -- its content (markers if unresolved)
147text : Blob -> Text -- its content (markers if unresolved)
146by : Id -> Repo -> Repo -- focus the commit with this id; crash if absent
148by : Id -> Repo -> Repo -- focus the commit with this id; crash if absent
@@ -530,9 +532,7 @@ contentAt = \p s -> (head (entryAt p s)).content or blob ""
530
532
531-- The paths a change touches.
533-- The paths a change touches.
532touched : Change -> [Path]
534touched : Change -> [Path]
533−touched = \ch ->
535+touched = touchedPaths
534− let paths = map (.path) ch.to ++ filter (\p -> not (member p (map (.path) ch.to))) (map (.path) ch.from)
535− in filter (\p -> entryAt p ch.to /= entryAt p ch.from) paths
536
536
537-- Paths whose content differs between the focus and its parent.
537-- Paths whose content differs between the focus and its parent.
538changed : Repo -> [Path]
538changed : Repo -> [Path]
src/builtins.rs+25
@@ -94,6 +94,7 @@ pub fn all_builtins() -> Vec<(String, Value)> {
94 builtin("splitOn", 2, b_split_on),
94 builtin("splitOn", 2, b_split_on),
95 builtin("replay", 2, b_replay),
95 builtin("replay", 2, b_replay),
96 builtin("unresolved", 1, b_unresolved),
96 builtin("unresolved", 1, b_unresolved),
97+ builtin("touchedPaths", 1, b_touched_paths),
97 builtin("blob", 1, b_blob),
98 builtin("blob", 1, b_blob),
98 builtin("text", 1, b_text),
99 builtin("text", 1, b_text),
99 builtin("by", 2, b_by),
100 builtin("by", 2, b_by),
@@ -512,6 +513,30 @@ fn b_unresolved(_i: &mut Interp, args: &[Value]) -> BResult {
512 }
513 }
513}
514}
514
515
516+/// the paths whose content differs between `from` and `to` snapshots (§7.3):
517+/// one pass over both path-keyed maps, comparing blobs by content id — never
518+/// reading file bytes. Replaces the quadratic `entryAt`-per-path scan.
519+fn b_touched_paths(_i: &mut Interp, args: &[Value]) -> BResult {
520+ let ch = &args[0];
521+ let from = crate::domain::snapshot_map(ch.field("from")?.as_list()?)?;
522+ let to = crate::domain::snapshot_map(ch.field("to")?.as_list()?)?;
523+ let mut paths: std::collections::BTreeSet<Vec<String>> = std::collections::BTreeSet::new();
524+ paths.extend(from.keys().cloned());
525+ paths.extend(to.keys().cloned());
526+ let mut out = Vec::new();
527+ for p in paths {
528+ let changed = match (from.get(&p), to.get(&p)) {
529+ (None, Some(_)) | (Some(_), None) => true,
530+ (Some(a), Some(b)) => !crate::value::value_eq(a, b)?,
531+ (None, None) => false,
532+ };
533+ if changed {
534+ out.push(Value::list(p.into_iter().map(Value::text).collect()));
535+ }
536+ }
537+ Ok(Value::list(out))
538+}
539+
515fn b_blob(_i: &mut Interp, args: &[Value]) -> BResult {
540fn b_blob(_i: &mut Interp, args: &[Value]) -> BResult {
516 let t = args[0].as_text()?;
541 let t = args[0].as_text()?;
517 Ok(Value::Blob(Rc::new(BlobVal {
542 Ok(Value::Blob(Rc::new(BlobVal {
src/config.rs+1−1
@@ -40,7 +40,7 @@ const RESERVED_BUILTINS: &[&str] = &[
40 ">=", "show", "::", "map", "filter", "length", "null", "head", "tail", "last", "nth", "take",
40 ">=", "show", "::", "map", "filter", "length", "null", "head", "tail", "last", "nth", "take",
41 "drop", "member", "range", "foldl", "concat", "++", "startsWith", "endsWith", "splitOn",
41 "drop", "member", "range", "foldl", "concat", "++", "startsWith", "endsWith", "splitOn",
42 "replay", "unresolved", "blob", "text", "by", "meta", "validate", "diff", "difft", "treeWith",
42 "replay", "unresolved", "blob", "text", "by", "meta", "validate", "diff", "difft", "treeWith",
43− "extract",
43+ "extract", "touchedPaths",
44];
44];
45
45
46pub fn is_reserved_builtin(name: &str) -> bool {
46pub fn is_reserved_builtin(name: &str) -> bool {