L
ling-miao
↓ 0 下载miao: a Ling-native version control system — temples, souls, rites and talismans instead of repos, branches, commits and tags; adaptive whole/delta/chunk storage; SSH temple fairs for sharing; a LingStationX-themed GUI; ROYGBIV everywhere
关键词 vcs, version-control, git, miao, ling
lingfu install ling-miao代码 / 文件 (268)
ling/store.ling
# ━━━ store.ling — the temple: repo layout, adaptive object store, altar ━━━
# A repository is a temple (.miao). Every version of a file is a spirit,
# identified by the BLAKE3 hash of its full content. HOW a spirit is
# enshrined is decided per-version by the diviner (sx_spirit_put):
# whole — small spirits, stored entire
# delta — text spirits, stored as a line-delta on the previous version
# (chains bounded by depth so summoning stays fast)
# chunks — giant spirits, split by content-defined chunking; chunks
# dedup across versions AND across files
# Payloads live in relics/pay/<blake3>, raw bytes on disk.
#
# fns prefixed sx_.
# ── repo discovery ────────────────────────────────────────────────────
fn sx_root() {
bind cached = st_get("sx_root", "")
if cached != "" { return cached }
bind d = ut_norm(absolute_path("."))
bind i = 0.0
while i < 48.0 {
if file_exists(d + "/.miao/MIAO") {
st_set("sx_root", d)
return d
}
bind p = ut_parent(d)
if p == d { bind i = 48.0 }
bind d = p
bind i = i + 1.0
}
return ""
}
fn sx_dir() { return sx_root() + "/.miao" }
# relative path of cwd inside the repo ("" at root)
fn sx_prefix() {
bind root = sx_root()
bind cwd = ut_norm(absolute_path("."))
if cwd == root { return "" }
bind rel = str_strip_prefix(cwd, root + "/")
if rel == cwd { return "" }
return rel
}
# ── consecration (init) ───────────────────────────────────────────────
fn sx_init(path) {
bind base = ut_norm(path)
bind d = base + "/.miao"
if file_exists(d + "/MIAO") { return "exists" }
make_dir(d + "/relics/spirit")
make_dir(d + "/relics/pay")
make_dir(d + "/relics/rite")
make_dir(d + "/souls")
make_dir(d + "/talismans")
make_dir(d + "/tmp")
write_file(d + "/MIAO", "miao 1\n")
write_file(d + "/SOUL", "soul: main\n")
write_file(d + "/souls/main", "")
write_file(d + "/altar", "")
return "ok"
}
# ── config ────────────────────────────────────────────────────────────
fn sx_cfg_get(key, def) {
bind lines = ut_read_lines(sx_dir() + "/config")
bind i = 0.0
while i < len(lines) {
bind ln = list_get(lines, i)
if str_starts_with(ln, key + "|") { return str_slice(ln, len(key) + 1.0, 99999.0) }
bind i = i + 1.0
}
return def
}
fn sx_cfg_set(key, val) {
bind lines = ut_read_lines(sx_dir() + "/config")
bind out = ""
bind i = 0.0
while i < len(lines) {
bind ln = list_get(lines, i)
if str_starts_with(ln, key + "|") == false { bind out = out + ln + "\n" }
bind i = i + 1.0
}
write_file(sx_dir() + "/config", out + key + "|" + val + "\n")
return 0.0
}
fn sx_author() {
bind a = sx_cfg_get("author", "")
if a != "" { return a }
bind u = env_get("USERNAME", "")
if u != "" { return u }
bind u2 = env_get("USER", "")
if u2 != "" { return u2 }
return "keeper"
}
# ── the living soul (HEAD) ────────────────────────────────────────────
# returns [type, value]: ["soul", name] or ["rite", id]
fn sx_head() {
bind t = str_trim(ut_read_text(sx_dir() + "/SOUL"))
if str_starts_with(t, "soul: ") { return ["soul", str_slice(t, 6.0, 99999.0)] }
if str_starts_with(t, "rite: ") { return ["rite", str_slice(t, 6.0, 99999.0)] }
return ["soul", "main"]
}
fn sx_set_head_soul(name) {
write_file(sx_dir() + "/SOUL", "soul: " + name + "\n")
return 0.0
}
fn sx_set_head_rite(id) {
write_file(sx_dir() + "/SOUL", "rite: " + id + "\n")
return 0.0
}
fn sx_tip(soul) { return str_trim(ut_read_text(sx_dir() + "/souls/" + soul)) }
fn sx_set_tip(soul, id) {
write_file(sx_dir() + "/souls/" + soul, id + "\n")
return 0.0
}
fn sx_souls() {
bind es = list_dir(sx_dir() + "/souls")
bind out = list_new()
bind i = 0.0
while i < len(es) {
bind out = list_push(out, path_name(list_get(es, i)))
bind i = i + 1.0
}
return ut_sort(out)
}
# the rite id the working tree sits on ("" when the temple is newborn)
fn sx_head_rite() {
bind h = sx_head()
if list_get(h, 0.0) == "rite" { return list_get(h, 1.0) }
return sx_tip(list_get(h, 1.0))
}
# ── talismans (tags) ──────────────────────────────────────────────────
fn sx_talisman_set(name, rite) {
write_file(sx_dir() + "/talismans/" + name, rite + "\n")
return 0.0
}
fn sx_talisman_get(name) { return str_trim(ut_read_text(sx_dir() + "/talismans/" + name)) }
fn sx_talismans() {
bind es = list_dir(sx_dir() + "/talismans")
bind out = list_new()
bind i = 0.0
while i < len(es) {
bind out = list_push(out, path_name(list_get(es, i)))
bind i = i + 1.0
}
return ut_sort(out)
}
# resolve a name: soul | talisman | rite id (full or >=7 char prefix) -> rite id or ""
fn sx_resolve(name) {
if name == "" { return sx_head_rite() }
if file_exists(sx_dir() + "/souls/" + name) { return sx_tip(name) }
if file_exists(sx_dir() + "/talismans/" + name) { return sx_talisman_get(name) }
if file_exists(sx_dir() + "/relics/rite/" + name) { return name }
# prefix search
if len(name) >= 7.0 {
bind es = list_dir(sx_dir() + "/relics/rite")
bind i = 0.0
bind found = ""
while i < len(es) {
bind id = path_name(list_get(es, i))
if str_starts_with(id, name) {
if found != "" { return "" }
bind found = id
}
bind i = i + 1.0
}
return found
}
return ""
}
# ── payload objects (raw bytes, blake3-named) ─────────────────────────
fn sx_pay_put_hex(hex) {
bind tmp = sx_dir() + "/tmp/pay"
file_write_hex(tmp, hex)
bind id = file_hash(tmp)
bind dest = sx_dir() + "/relics/pay/" + id
if file_exists(dest) {
remove_file(tmp)
} else {
rename_file(tmp, dest)
}
return id
}
fn sx_pay_put_text(text) {
bind id = hash_hex(text)
bind dest = sx_dir() + "/relics/pay/" + id
if file_exists(dest) == false { write_file(dest, text) }
return id
}
fn sx_pay_hex(id) {
bind r = file_read_hex(sx_dir() + "/relics/pay/" + id)
match r {
bad(e) => { return "" }
ok(v) => { return r.unwrap() }
}
}
fn sx_pay_text(id) { return ut_read_text(sx_dir() + "/relics/pay/" + id) }
# ── spirit manifests ──────────────────────────────────────────────────
fn sx_spirit_exists(id) { return file_exists(sx_dir() + "/relics/spirit/" + id) }
# returns [strategy, a, b, size, kind, depth]
# whole: a=pay delta: a=base spirit, b=pay (script)
# chunks: a=pay (chunk list), b=count
fn sx_spirit_info(id) {
bind t = ut_read_text(sx_dir() + "/relics/spirit/" + id)
if t == "" { return ["", "", "", 0.0, "", 0.0] }
bind lines = split(t, "\n")
bind head = split(list_get(lines, 0.0), "|")
bind strat = list_get(head, 0.0)
bind a = list_get(head, 1.0)
bind b = list_get(head, 2.0)
bind size = 0.0
bind kind = "bin"
bind depth = 0.0
bind i = 1.0
while i < len(lines) {
bind ln = list_get(lines, i)
if str_starts_with(ln, "size|") { bind size = to_number(str_slice(ln, 5.0, 99.0)) }
if str_starts_with(ln, "kind|") { bind kind = str_slice(ln, 5.0, 99.0) }
if str_starts_with(ln, "depth|") { bind depth = to_number(str_slice(ln, 6.0, 99.0)) }
bind i = i + 1.0
}
return [strat, a, b, size, kind, depth]
}
# ── materialize a spirit back into bytes/text ─────────────────────────
fn sx_mat_text(id) {
bind info = sx_spirit_info(id)
bind strat = list_get(info, 0.0)
if strat == "delta" {
bind base = sx_mat_text(list_get(info, 1.0))
bind script = sx_pay_text(list_get(info, 2.0))
return dy_delta_apply(base, script)
}
bind hx = sx_mat_hex(id)
bind r = hex_text(hx)
match r {
bad(e) => { return "" }
ok(v) => { return r.unwrap() }
}
}
fn sx_mat_hex(id) {
bind info = sx_spirit_info(id)
bind strat = list_get(info, 0.0)
if strat == "whole" { return sx_pay_hex(list_get(info, 1.0)) }
if strat == "chunks" {
bind lst = split(str_trim(sx_pay_text(list_get(info, 1.0))), "\n")
bind parts = list_new()
bind i = 0.0
while i < len(lst) {
bind cid = list_get(lst, i)
if cid != "" { bind parts = list_push(parts, sx_pay_hex(cid)) }
bind i = i + 1.0
}
return hex_concat(parts)
}
if strat == "delta" {
return text_hex(sx_mat_text(id))
}
return ""
}
# write a spirit's bytes to a worktree path
fn sx_checkout_spirit(id, dest) {
bind parent = ut_parent(dest)
if parent != dest {
if file_exists(parent) == false { make_dir(parent) }
}
bind hx = sx_mat_hex(id)
file_write_hex(dest, hx)
return 0.0
}
# ── THE DIVINER: adaptive enshrinement ────────────────────────────────
# Stores the file at abs_path as a spirit; prev_id is the previous spirit
# recorded for the same path ("" if none). Returns [spirit_id, strategy].
fn sx_spirit_put(abs_path, prev_id) {
bind id = file_hash(abs_path)
if id == "" { return ["", "unreadable"] }
if sx_spirit_exists(id) { return [id, "kept"] }
bind hr = file_read_hex(abs_path)
match hr {
bad(e) => { return ["", "unreadable"] }
ok(v) => { bind hr = hr }
}
bind hx = hr.unwrap()
bind size = len(hx) / 2.0
# text divination
bind is_text = false
bind text = ""
if size <= 4194304.0 {
bind tr = hex_text(hx)
if ut_is_bad(tr) == false {
bind is_text = true
bind text = tr.unwrap()
}
}
# 1) try a delta against the previous incarnation of this path
if is_text {
if prev_id != "" {
if sx_spirit_exists(prev_id) {
bind pinfo = sx_spirit_info(prev_id)
bind pkind = list_get(pinfo, 4.0)
bind pdepth = list_get(pinfo, 5.0)
if pkind == "text" {
if pdepth < 8.0 {
bind ptext = sx_mat_text(prev_id)
bind script = dy_delta_make(ptext, text)
if script != "" {
if len(script) < len(text) * 0.45 + 24.0 {
bind pay = sx_pay_put_text(script)
bind m = "delta|" + prev_id + "|" + pay + "\n"
bind m = m + "size|" + size + "\nkind|text\ndepth|" + (pdepth + 1.0) + "\n"
write_file(sx_dir() + "/relics/spirit/" + id, m)
return [id, "delta"]
}
}
}
}
}
}
}
# 2) giant spirits get a distributed body (content-defined chunks)
if size > 262144.0 {
bind chunks = dy_chunks(hx)
bind ids = ""
bind i = 0.0
while i < len(chunks) {
bind cid = sx_pay_put_hex(list_get(chunks, i))
bind ids = ids + cid + "\n"
bind i = i + 1.0
}
bind lpay = sx_pay_put_text(ids)
bind kind = ut_sel(is_text, "text", "bin")
bind m = "chunks|" + lpay + "|" + len(chunks) + "\n"
bind m = m + "size|" + size + "\nkind|" + kind + "\n"
write_file(sx_dir() + "/relics/spirit/" + id, m)
return [id, "chunks " + len(chunks)]
}
# 3) small spirits are enshrined whole
bind pay = sx_pay_put_hex(hx)
bind kind = ut_sel(is_text, "text", "bin")
bind m = "whole|" + pay + "\n" + "size|" + size + "\nkind|" + kind + "\n"
write_file(sx_dir() + "/relics/spirit/" + id, m)
return [id, "whole"]
}
# ── scrolls (tree manifests) and rites (commits) ──────────────────────
# a scroll payload: sorted lines "<spirit-id> <path>"
fn sx_scroll_put(ids, paths) {
bind sp = ut_sort_pair(paths, ids)
bind ps = list_get(sp, 0.0)
bind is = list_get(sp, 1.0)
bind t = ""
bind i = 0.0
while i < len(ps) {
bind t = t + list_get(is, i) + " " + list_get(ps, i) + "\n"
bind i = i + 1.0
}
return sx_pay_put_text(t)
}
# returns [ids, paths]
fn sx_scroll_read(pay) {
bind t = sx_pay_text(pay)
bind ids = list_new()
bind paths = list_new()
if t == "" { return [ids, paths] }
bind lines = split(t, "\n")
bind i = 0.0
while i < len(lines) {
bind ln = list_get(lines, i)
if ln != "" {
bind sp = str_find(ln, " ")
if sp > 0.0 {
bind ids = list_push(ids, str_slice(ln, 0.0, sp))
bind paths = list_push(paths, str_slice(ln, sp + 1.0, 99999.0))
}
}
bind i = i + 1.0
}
return [ids, paths]
}
fn sx_rite_put(scrollpay, parents, author, msg) {
bind t = "scroll " + scrollpay + "\n"
bind i = 0.0
while i < len(parents) {
bind p = list_get(parents, i)
if p != "" { bind t = t + "parent " + p + "\n" }
bind i = i + 1.0
}
bind t = t + "author " + author + "\n"
bind t = t + "time " + floor(now_unix()) + "\n"
bind t = t + "\n" + msg + "\n"
bind id = hash_hex(t)
write_file(sx_dir() + "/relics/rite/" + id, t)
return id
}
# returns [scrollpay, parents(list), author, time, message]
fn sx_rite_read(id) {
bind t = ut_read_text(sx_dir() + "/relics/rite/" + id)
bind scroll = ""
bind parents = list_new()
bind author = ""
bind time = 0.0
bind msg = ""
if t == "" { return [scroll, parents, author, time, msg] }
bind lines = split(t, "\n")
bind i = 0.0
bind in_msg = false
while i < len(lines) {
bind ln = list_get(lines, i)
if in_msg {
bind msg = msg + ln + "\n"
} else if ln == "" {
bind in_msg = true
} else if str_starts_with(ln, "scroll ") {
bind scroll = str_slice(ln, 7.0, 99999.0)
} else if str_starts_with(ln, "parent ") {
bind parents = list_push(parents, str_slice(ln, 7.0, 99999.0))
} else if str_starts_with(ln, "author ") {
bind author = str_slice(ln, 7.0, 99999.0)
} else if str_starts_with(ln, "time ") {
bind time = to_number(str_slice(ln, 5.0, 99.0))
}
bind i = i + 1.0
}
bind msg = str_trim(msg)
return [scroll, parents, author, time, msg]
}
# manifest of a rite -> [ids, paths]; "" rite -> empty
fn sx_rite_manifest(rite) {
if rite == "" { return [list_new(), list_new()] }
bind r = sx_rite_read(rite)
return sx_scroll_read(list_get(r, 0.0))
}
# ── the altar (staging overlay) ───────────────────────────────────────
# altar lines: "<spirit-id>|<path>", spirit-id "-" marks a staged removal
fn sx_altar() {
bind lines = ut_read_lines(sx_dir() + "/altar")
bind ids = list_new()
bind paths = list_new()
bind i = 0.0
while i < len(lines) {
bind ln = list_get(lines, i)
bind sp = str_find(ln, "|")
if sp > 0.0 {
bind ids = list_push(ids, str_slice(ln, 0.0, sp))
bind paths = list_push(paths, str_slice(ln, sp + 1.0, 99999.0))
}
bind i = i + 1.0
}
return [ids, paths]
}
fn sx_altar_write(ids, paths) {
bind t = ""
bind i = 0.0
while i < len(ids) {
bind t = t + list_get(ids, i) + "|" + list_get(paths, i) + "\n"
bind i = i + 1.0
}
write_file(sx_dir() + "/altar", t)
return 0.0
}
fn sx_altar_set(path, id) {
bind a = sx_altar()
bind ids = list_get(a, 0.0)
bind paths = list_get(a, 1.0)
bind at = ut_index_of(paths, path)
if at >= 0.0 {
bind ids = list_set(ids, at, id)
} else {
bind ids = list_push(ids, id)
bind paths = list_push(paths, path)
}
sx_altar_write(ids, paths)
return 0.0
}
fn sx_altar_del(path) {
bind a = sx_altar()
bind ids = list_get(a, 0.0)
bind paths = list_get(a, 1.0)
bind nids = list_new()
bind npaths = list_new()
bind i = 0.0
while i < len(ids) {
if list_get(paths, i) != path {
bind nids = list_push(nids, list_get(ids, i))
bind npaths = list_push(npaths, list_get(paths, i))
}
bind i = i + 1.0
}
sx_altar_write(nids, npaths)
return 0.0
}
# head manifest + altar overlay = the next rite's manifest. returns [ids, paths]
fn sx_overlay() {
bind head = sx_rite_manifest(sx_head_rite())
bind hids = list_get(head, 0.0)
bind hpaths = list_get(head, 1.0)
bind a = sx_altar()
bind aids = list_get(a, 0.0)
bind apaths = list_get(a, 1.0)
bind ids = list_new()
bind paths = list_new()
bind i = 0.0
while i < len(hpaths) {
bind p = list_get(hpaths, i)
bind at = ut_index_of(apaths, p)
if at < 0.0 {
bind ids = list_push(ids, list_get(hids, i))
bind paths = list_push(paths, p)
} else if list_get(aids, at) != "-" {
bind ids = list_push(ids, list_get(aids, at))
bind paths = list_push(paths, p)
}
bind i = i + 1.0
}
# altar entries for brand new paths
bind i = 0.0
while i < len(apaths) {
bind p = list_get(apaths, i)
if ut_index_of(hpaths, p) < 0.0 {
if list_get(aids, i) != "-" {
bind ids = list_push(ids, list_get(aids, i))
bind paths = list_push(paths, p)
}
}
bind i = i + 1.0
}
return [ids, paths]
}
# ── ignore rules ──────────────────────────────────────────────────────
fn sx_ign_rules() {
if st_has("sx_ign") > 0.5 { return st_get("sx_ign", []) }
# cache/ and screenshots/ are droppings of the Ling runtime itself
# (font raster cache, the screenshot() builtin) — never spirits.
bind rules = [".miao", ".git", ".ling-ssh", ".ling-build", "cache", "screenshots"]
bind extra = ut_read_lines(sx_root() + "/.miaoignore")
bind i = 0.0
while i < len(extra) {
bind ln = str_trim(list_get(extra, i))
if ln != "" {
if str_starts_with(ln, "#") == false { bind rules = list_push(rules, ln) }
}
bind i = i + 1.0
}
bind extra2 = ut_read_lines(sx_root() + "/ling.ignore")
bind i = 0.0
while i < len(extra2) {
bind ln = str_trim(list_get(extra2, i))
if ln != "" {
if str_starts_with(ln, "#") == false { bind rules = list_push(rules, ln) }
}
bind i = i + 1.0
}
st_set("sx_ign", rules)
return rules
}
fn sx_ignored(rel) {
bind rules = sx_ign_rules()
bind i = 0.0
while i < len(rules) {
bind pat = list_get(rules, i)
if pat == rel { return true }
if str_ends_with(pat, "/") {
bind d = str_slice(pat, 0.0, len(pat) - 1.0)
if rel == d { return true }
if str_starts_with(rel, pat) { return true }
}
if str_starts_with(pat, "*.") {
if str_ends_with(rel, str_slice(pat, 1.0, 99999.0)) { return true }
}
if str_ends_with(pat, "/*") {
if str_starts_with(rel, str_slice(pat, 0.0, len(pat) - 1.0)) { return true }
}
if str_find(pat, "/") < 0.0 {
if str_find(pat, "*") < 0.0 {
# bare name: match as path segment anywhere
if str_starts_with(rel, pat + "/") { return true }
if str_ends_with(rel, "/" + pat) { return true }
if str_find(rel, "/" + pat + "/") >= 0.0 { return true }
}
}
bind i = i + 1.0
}
return false
}
# ── worktree walk ─────────────────────────────────────────────────────
fn sx_walk_dir(abs, rel, acc) {
bind es = list_dir(abs)
bind i = 0.0
while i < len(es) {
bind e = list_get(es, i)
bind name = path_name(e)
bind rel2 = name
if rel != "" { bind rel2 = rel + "/" + name }
if sx_ignored(rel2) == false {
if is_dir(e) {
bind acc = sx_walk_dir(e, rel2, acc)
} else {
bind acc = list_push(acc, rel2)
}
}
bind i = i + 1.0
}
return acc
}
fn sx_walk() {
return ut_sort(sx_walk_dir(sx_root(), "", list_new()))
}
# ── chronicle (reflog) ────────────────────────────────────────────────
fn sx_log(op, detail) {
bind p = sx_dir() + "/chronicle"
bind old = ut_read_text(p)
write_file(p, old + floor(now_unix()) + "|" + op + "|" + detail + "\n")
return 0.0
}