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	mut hashing := reading()115	walked := snapshot.walk_report(store, store.root, snapshot.load(store.root), mut hashing)!116	after := snapshot.index_files(walked.files)117	mut added := []string{}118	mut modified := []string{}119	mut removed := []string{}120	for path in after.keys() {121		if path !in before {122			added << path123		} else if before[path].hash != after[path].hash124			|| before[path].executable != after[path].executable {125			modified << path126		}127	}128	for path in before.keys() {129		if path !in after {130			removed << path131		}132	}133	added.sort()134	modified.sort()135	removed.sort()136	ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified137	return Changes{138		dirty:          added.len + modified.len + removed.len > 0139		added:          added140		modified:       modified141		removed:        removed142		ignore_changed: ignore_changed143		unsafe_paths:   walked.unsafe_paths144	}145}