diff
uses tcmts.cli.style.dim, which uses node:util
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { diffNamespaces, printDiff, stateOf } from "#codebase/tcmts/cli/diff"; import { causalArg, isHistoryRef, stateAt } from "#codebase/tcmts/cli/main"; import { conflictAt, conflictPairs, diffDefinitions } from "#codebase/tcmts/cli/merge"; import { dim } from "#codebase/tcmts/cli/style"; import { UsageError } from "#codebase/tcmts/cli/ui"; import { shortHash } from "#codebase/tcmts/core/hash"; export function diff(cb: Codebase, args: string[]) { const now = stateOf(cb.store.names(undefined, true)); const irOf = (h: string) => cb.store.getDef(h)!.ir; // (and the text versions each side shows: a definition can read differently with the same code) const textsAt = (causal: string) => cb.store.textsAt(cb.store.causal(causal)!.namespace); if (args.length === 0) { const head = cb.store.causal(cb.store.head())!; if (!head.parents.length) return console.log(dim("no changes yet")); const before = stateAt(cb, head.parents[0]!); const texts = { before: textsAt(head.parents[0]!), after: cb.store.texts() }; return printDiff( cb, diffNamespaces(before, now, irOf, texts), before, now, `changes from \`${head.command}\``, ); } // two definitions by #hash, or a conflicted name's: their code, line by line const defs = args.map((a) => (a.startsWith("#") ? cb.store.hashesWithPrefix(a.slice(1)) : [])); if (args.length === 2 && defs.every((d) => d.length === 1)) { const [a, b] = defs.map((d) => d[0]!); return diffDefinitions(cb, [ { from: a!, to: b!, title: `${shortHash(a!)} → ${shortHash(b!)}` }, ]); } const conflict = args.length === 1 && !cb.store.hasBranch(args[0]!) ? conflictAt(cb, args[0]!) : undefined; if (conflict) return diffDefinitions(cb, conflictPairs(conflict)); if (args.length === 1 && isHistoryRef(args[0]!) && !cb.store.hasBranch(args[0]!)) { const c = causalArg(cb, args[0]!); const before = stateAt(cb, c); const label = /@\d/.test(args[0]!) ? args[0]! : shortHash(c); const texts = { before: textsAt(c), after: cb.store.texts() }; return printDiff( cb, diffNamespaces(before, now, irOf, texts), before, now, `changes since ${label}`, ); } const [from, to] = args.length === 1 ? [cb.store.branch().name, args[0]!] : [args[0]!, args[1]!]; const side = (ref: string) => { if (cb.store.hasBranch(ref)) return stateOf(cb.store.names(ref)); if (isHistoryRef(ref)) return stateAt(cb, causalArg(cb, ref)); if (!cb.store.hasBranch(ref)) throw new UsageError( `no branch named '${ref}' (or history entry: a number or #hash, see \`tcm history\`)`, ); return stateOf(cb.store.names(ref)); }; const sideTexts = (ref: string) => cb.store.hasBranch(ref) ? cb.store.texts(ref) : textsAt(causalArg(cb, ref)); const [before, after] = [side(from), side(to)]; const texts = { before: sideTexts(from), after: sideTexts(to) }; printDiff(cb, diffNamespaces(before, after, irOf, texts), before, after, `${from} → ${to}`); }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.diff.diffNamespaces
- tcmts.cli.diff.printDiff
- tcmts.cli.diff.stateOf
- tcmts.cli.main.causalArg
- tcmts.cli.main.isHistoryRef
- tcmts.cli.main.stateAt
- tcmts.cli.merge.conflictAt
- tcmts.cli.merge.conflictPairs
- tcmts.cli.merge.diffDefinitions
- tcmts.cli.style.dim
- tcmts.cli.ui.UsageError
- tcmts.core.hash.shortHash