cmd_status.v

4.1 KB · raw

1//! `koh status` — where things stand: the save you are on, and what the working tree has that it2//! does not.34module cli56import json27import os8import src.events9import src.fmt10import src.history11import src.snapshot12import src.storage1314pub fn cmd_status(raw []string) int {15	args := parse('status', raw, Options{16		flags: ['json']17	}) or { return usage_error(err.msg()) }18	store := open_store() or { return refuse(args.json(), err.msg()) }19	defer {20		store.close()21	}22	head := history.head(store) or {23		return refuse(args.json(), 'this repo has no saves yet — run "koh save \\"first save\\""')24	}25	unfinished := snapshot.restore_pending(store)26	changes := compare_with_head(store, head) or {27		// Whatever stopped the restore may also stop this walk.28		reason := if unfinished {29			'${err.msg()} — ${snapshot.restore_unfinished}'30		} else {31			err.msg()32		}33		return refuse(args.json(), reason)34	}3536	if args.json() {37		line := history.line(store) or { return refuse(true, err.msg()) }38		flags := events.latest_flags(store) or { return refuse(true, err.msg()) }39		head_flag := flags[head.id] or { events.Flag{} }40		emit_json(json2.encode(StatusJson{41			head_id:            head.id42			head_msg:           head.message43			dirty:              changes.dirty44			modified:           changes.modified45			added:              changes.added46			deleted:            changes.removed47			ignore_changed:     changes.ignore_changed48			restore_unfinished: unfinished49			saves:              line.len50			size_bytes:         os.file_size(os.join_path(store.root, storage.file_name))51			head_flagged:       head_flag.flagged52			unsafe_paths:       changes.unsafe_paths53		}))54		return 055	}56	saves := history.save_count(store) or { return refuse(false, err.msg()) }57	note('at [${head.id}] ${fmt.fit(head.message.all_before('\n'), 50)} · ${ago(head.created_at)} · ${fmt.plural(int(saves), 'save')}')58	if unfinished {59		warn(snapshot.restore_unfinished)60	}61	for path in changes.unsafe_paths {62		warn('not saved: unsafe name ${path}')63	}64	if !changes.dirty {65		ok('no changes')66		if changes.ignore_changed {67			note('${snapshot.ignore_file} changed since [${head.id}]')68		}69		return 070	}71	for path in changes.added {72		println('  ${fmt.green('+')} ${path}')73	}74	for path in changes.modified {75		println('  ${fmt.amber('~')} ${path}')76	}77	for path in changes.removed {78		println('  ${fmt.red('-')} ${path}')79	}80	note('${changes.added.len} added  ·  ${changes.modified.len} modified  ·  ${changes.removed.len} removed')81	if changes.ignore_changed {82		warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')83	}84	return 085}8687pub struct Changes {88pub:89	dirty          bool90	added          []string91	modified       []string92	removed        []string93	ignore_changed bool94	unsafe_paths   []string95}9697struct StatusJson {98	head_id            string99	head_msg           string100	dirty              bool101	modified           []string102	added              []string103	deleted            []string104	ignore_changed     bool105	restore_unfinished bool106	saves              int107	size_bytes         u64108	head_flagged       bool109	unsafe_paths       []string110}111112pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {113	before := snapshot.index_files(history.files_of(store, head.id)!)114	walked := snapshot.walk_report(store, store.root, snapshot.load(store.root))!115	after := snapshot.index_files(walked.files)116	mut added := []string{}117	mut modified := []string{}118	mut removed := []string{}119	for path in after.keys() {120		if path !in before {121			added << path122		} else if before[path].hash != after[path].hash123			|| before[path].executable != after[path].executable {124			modified << path125		}126	}127	for path in before.keys() {128		if path !in after {129			removed << path130		}131	}132	added.sort()133	modified.sort()134	removed.sort()135	ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified136	return Changes{137		dirty:          added.len + modified.len + removed.len > 0138		added:          added139		modified:       modified140		removed:        removed141		ignore_changed: ignore_changed142		unsafe_paths:   walked.unsafe_paths143	}144}