123module cli45import json6import src.fmt7import src.history8import src.snapshot9import src.storage1011pub fn cmd_status(raw []string) int {12 args := parse('status', raw, Options{13 flags: ['json']14 }) or { return usage_error(err.msg()) }15 store := open_store() or { return refuse(args.json(), err.msg()) }16 defer {17 store.close()18 }19 head := history.head(store) or {20 return refuse(args.json(), 'this repo has no saves yet — run "koh save \\"first save\\""')21 }22 changes := compare_with_head(store, head) or { return refuse(args.json(), err.msg()) }2324 if args.json() {25 emit_json(json.encode(StatusJson{26 head_id: head.id27 head_msg: head.message28 dirty: changes.dirty29 modified: changes.modified30 added: changes.added31 deleted: changes.removed32 ignore_changed: changes.ignore_changed33 }))34 return 035 }36 if !changes.dirty {37 ok('no changes')38 if changes.ignore_changed {39 note('${snapshot.ignore_file} changed since [${head.id}]')40 }41 return 042 }43 for path in changes.added {44 println(' ${fmt.green('+')} ${path}')45 }46 for path in changes.modified {47 println(' ${fmt.amber('~')} ${path}')48 }49 for path in changes.removed {50 println(' ${fmt.red('-')} ${path}')51 }52 note('${changes.added.len} added · ${changes.modified.len} modified · ${changes.removed.len} removed')53 if changes.ignore_changed {54 warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')55 }56 return 057}5859pub struct Changes {60pub:61 dirty bool62 added []string63 modified []string64 removed []string65 ignore_changed bool66}6768struct StatusJson {69 head_id string70 head_msg string71 dirty bool72 modified []string73 added []string74 deleted []string75 ignore_changed bool76}7778pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {79 before := snapshot.index_files(history.files_of(store, head.id)!)80 after := snapshot.index_files(snapshot.walk(store, store.root, snapshot.load(store.root))!)81 mut added := []string{}82 mut modified := []string{}83 mut removed := []string{}84 for path in after.keys() {85 if path !in before {86 added << path87 } else if before[path].hash != after[path].hash88 || before[path].executable != after[path].executable {89 modified << path90 }91 }92 for path in before.keys() {93 if path !in after {94 removed << path95 }96 }97 added.sort()98 modified.sort()99 removed.sort()100 ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified101 return Changes{102 dirty: added.len + modified.len + removed.len > 0103 added: added104 modified: modified105 removed: removed106 ignore_changed: ignore_changed107 }108}