cmd_status.v

3.8 KB · raw

1//! `koh status` — what the working tree has that head does not.23module cli45import json26import os7import src.events8import src.fmt9import src.history10import src.snapshot11import src.storage1213pub fn cmd_status(raw []string) int {14	args := parse('status', raw, Options{15		flags: ['json']16	}) or { return usage_error(err.msg()) }17	store := open_store() or { return refuse(args.json(), err.msg()) }18	defer {19		store.close()20	}21	head := history.head(store) or {22		return refuse(args.json(), 'this repo has no saves yet — run "koh save \\"first save\\""')23	}24	unfinished := snapshot.restore_pending(store)25	changes := compare_with_head(store, head) or {26		// Whatever stopped the restore may also stop this walk.27		reason := if unfinished {28			'${err.msg()} — ${snapshot.restore_unfinished}'29		} else {30			err.msg()31		}32		return refuse(args.json(), reason)33	}3435	if args.json() {36		line := history.line(store) or { return refuse(true, err.msg()) }37		flags := events.latest_flags(store) or { return refuse(true, err.msg()) }38		head_flag := flags[head.id] or { events.Flag{} }39		emit_json(json2.encode(StatusJson{40			head_id:            head.id41			head_msg:           head.message42			dirty:              changes.dirty43			modified:           changes.modified44			added:              changes.added45			deleted:            changes.removed46			ignore_changed:     changes.ignore_changed47			restore_unfinished: unfinished48			saves:              line.len49			size_bytes:         os.file_size(os.join_path(store.root, storage.file_name))50			head_flagged:       head_flag.flagged51			unsafe_paths:       changes.unsafe_paths52		}))53		return 054	}55	if unfinished {56		warn(snapshot.restore_unfinished)57	}58	for path in changes.unsafe_paths {59		warn('not saved: unsafe name ${path}')60	}61	if !changes.dirty {62		ok('no changes')63		if changes.ignore_changed {64			note('${snapshot.ignore_file} changed since [${head.id}]')65		}66		return 067	}68	for path in changes.added {69		println('  ${fmt.green('+')} ${path}')70	}71	for path in changes.modified {72		println('  ${fmt.amber('~')} ${path}')73	}74	for path in changes.removed {75		println('  ${fmt.red('-')} ${path}')76	}77	note('${changes.added.len} added  ·  ${changes.modified.len} modified  ·  ${changes.removed.len} removed')78	if changes.ignore_changed {79		warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')80	}81	return 082}8384pub struct Changes {85pub:86	dirty          bool87	added          []string88	modified       []string89	removed        []string90	ignore_changed bool91	unsafe_paths   []string92}9394struct StatusJson {95	head_id            string96	head_msg           string97	dirty              bool98	modified           []string99	added              []string100	deleted            []string101	ignore_changed     bool102	restore_unfinished bool103	saves              int104	size_bytes         u64105	head_flagged       bool106	unsafe_paths       []string107}108109pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {110	before := snapshot.index_files(history.files_of(store, head.id)!)111	walked := snapshot.walk_report(store, store.root, snapshot.load(store.root))!112	after := snapshot.index_files(walked.files)113	mut added := []string{}114	mut modified := []string{}115	mut removed := []string{}116	for path in after.keys() {117		if path !in before {118			added << path119		} else if before[path].hash != after[path].hash120			|| before[path].executable != after[path].executable {121			modified << path122		}123	}124	for path in before.keys() {125		if path !in after {126			removed << path127		}128	}129	added.sort()130	modified.sort()131	removed.sort()132	ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified133	return Changes{134		dirty:          added.len + modified.len + removed.len > 0135		added:          added136		modified:       modified137		removed:        removed138		ignore_changed: ignore_changed139		unsafe_paths:   walked.unsafe_paths140	}141}