conflictAt

tcmts.cli.merge.conflictAt · #2iabpvj6 · term · runs anywhere

The conflict at a name on this branch (in kind's space, or whichever is conflicted), with the side each definition came from where a merge kept it (recordConflicts), followed through what re-derived it since (settling the merge, resolving another conflict): undefined if the name isn't conflicted.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { stateKey } from "#codebase/tcmts/cli/diff";
import { conflictSetting } from "#codebase/tcmts/cli/merge";
import type { Conflict, ConflictRecord } from "#codebase/tcmts/cli/merge";
import type { DefKind } from "#codebase/tcmts/core/types";

export function conflictAt(cb: Codebase, path: string, kind?: DefKind): Conflict | undefined {
  for (const k of kind ? [kind] : (["term", "type", "file"] as DefKind[])) {
    const hs = cb.store.hashesAt(path, k);
    if (hs.length < 2) continue;
    const key = stateKey(path, cb.store.getDef(hs[0]!)!.kind);
    const stored = cb.store.getSetting(conflictSetting(cb, key));
    const rec = stored ? (JSON.parse(stored) as ConflictRecord) : undefined;
    const sideOf = (h: string) =>
      (["ours", "theirs"] as const).find((s) =>
        rec?.[s].hashes.some((x) => x === h || cb.store.patched(x) === h),
      );
    const rank = (s: string | undefined) => (s === "ours" ? 0 : s === "theirs" ? 1 : 2);
    const sides = hs
      .map((hash) => {
        const side = sideOf(hash);
        return side ? { hash, side, label: rec![side].label } : { hash };
      })
      .sort((a, b) => rank(a.side) - rank(b.side));
    return { key, path, ...(rec?.base ? { base: rec.base } : {}), sides };
  }
  return undefined;
}

uses

used by