cmd_status.v

3.6 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		}))52		return 053	}54	if unfinished {55		warn(snapshot.restore_unfinished)56	}57	if !changes.dirty {58		ok('no changes')59		if changes.ignore_changed {60			note('${snapshot.ignore_file} changed since [${head.id}]')61		}62		return 063	}64	for path in changes.added {65		println('  ${fmt.green('+')} ${path}')66	}67	for path in changes.modified {68		println('  ${fmt.amber('~')} ${path}')69	}70	for path in changes.removed {71		println('  ${fmt.red('-')} ${path}')72	}73	note('${changes.added.len} added  ·  ${changes.modified.len} modified  ·  ${changes.removed.len} removed')74	if changes.ignore_changed {75		warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')76	}77	return 078}7980pub struct Changes {81pub:82	dirty          bool83	added          []string84	modified       []string85	removed        []string86	ignore_changed bool87}8889struct StatusJson {90	head_id            string91	head_msg           string92	dirty              bool93	modified           []string94	added              []string95	deleted            []string96	ignore_changed     bool97	restore_unfinished bool98	saves              int99	size_bytes         u64100	head_flagged       bool101}102103pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {104	before := snapshot.index_files(history.files_of(store, head.id)!)105	after := snapshot.index_files(snapshot.walk(store, store.root, snapshot.load(store.root))!)106	mut added := []string{}107	mut modified := []string{}108	mut removed := []string{}109	for path in after.keys() {110		if path !in before {111			added << path112		} else if before[path].hash != after[path].hash113			|| before[path].executable != after[path].executable {114			modified << path115		}116	}117	for path in before.keys() {118		if path !in after {119			removed << path120		}121	}122	added.sort()123	modified.sort()124	removed.sort()125	ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified126	return Changes{127		dirty:          added.len + modified.len + removed.len > 0128		added:          added129		modified:       modified130		removed:        removed131		ignore_changed: ignore_changed132	}133}