cmd_status.v

3.1 KB · raw

1//! `koh status` — what the working tree has that head does not.23module 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	unfinished := snapshot.restore_pending(store)23	changes := compare_with_head(store, head) or {24		// Whatever stopped the restore may also stop this walk.25		reason := if unfinished {26			'${err.msg()} — ${snapshot.restore_unfinished}'27		} else {28			err.msg()29		}30		return refuse(args.json(), reason)31	}3233	if args.json() {34		emit_json(json.encode(StatusJson{35			head_id:            head.id36			head_msg:           head.message37			dirty:              changes.dirty38			modified:           changes.modified39			added:              changes.added40			deleted:            changes.removed41			ignore_changed:     changes.ignore_changed42			restore_unfinished: unfinished43		}))44		return 045	}46	if unfinished {47		warn(snapshot.restore_unfinished)48	}49	if !changes.dirty {50		ok('no changes')51		if changes.ignore_changed {52			note('${snapshot.ignore_file} changed since [${head.id}]')53		}54		return 055	}56	for path in changes.added {57		println('  ${fmt.green('+')} ${path}')58	}59	for path in changes.modified {60		println('  ${fmt.amber('~')} ${path}')61	}62	for path in changes.removed {63		println('  ${fmt.red('-')} ${path}')64	}65	note('${changes.added.len} added  ·  ${changes.modified.len} modified  ·  ${changes.removed.len} removed')66	if changes.ignore_changed {67		warn('${snapshot.ignore_file} changed since [${head.id}] — the next save records the new rules')68	}69	return 070}7172pub struct Changes {73pub:74	dirty          bool75	added          []string76	modified       []string77	removed        []string78	ignore_changed bool79}8081struct StatusJson {82	head_id            string83	head_msg           string84	dirty              bool85	modified           []string86	added              []string87	deleted            []string88	ignore_changed     bool89	restore_unfinished bool90}9192pub fn compare_with_head(store &storage.Store, head history.Save) !Changes {93	before := snapshot.index_files(history.files_of(store, head.id)!)94	after := snapshot.index_files(snapshot.walk(store, store.root, snapshot.load(store.root))!)95	mut added := []string{}96	mut modified := []string{}97	mut removed := []string{}98	for path in after.keys() {99		if path !in before {100			added << path101		} else if before[path].hash != after[path].hash102			|| before[path].executable != after[path].executable {103			modified << path104		}105	}106	for path in before.keys() {107		if path !in after {108			removed << path109		}110	}111	added.sort()112	modified.sort()113	removed.sort()114	ignore_changed := snapshot.ignore_file in added || snapshot.ignore_file in modified115	return Changes{116		dirty:          added.len + modified.len + removed.len > 0117		added:          added118		modified:       modified119		removed:        removed120		ignore_changed: ignore_changed121	}122}