mergeable

tcmts.cli.contributions.mergeable · #s5tyuwmx · term · runs on the server

uses tcmts.cli.merge.silently, which uses process

Whether a contribution merges into its target, settled as the merge settles it: a name both sides edited where the edits are apart is merged as text, not a conflict.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import type { Mergeable } from "#codebase/tcmts/cli/contributions";
import { keyPath } from "#codebase/tcmts/cli/diff";
import { isBytes } from "#codebase/tcmts/cli/files";
import { mergeShown, planMerge, silently } from "#codebase/tcmts/cli/merge";
import type { Contribution } from "#codebase/tcmts/store";

export function mergeable(cb: Codebase, c: Contribution): Mergeable {
  const plan = planMerge(cb, c.target, c.source);
  if (plan.kind !== "merge") return { kind: plan.kind };
  const one = (s: Set<string> | undefined) =>
    s?.size === 1 ? cb.store.getDef([...s][0]!) : undefined;
  const asText: string[] = [];
  const names: string[] = [];
  for (const key of plan.result.conflicts) {
    const [b, o, t] = [plan.base, plan.before, plan.theirs].map((s) => one(s.get(key)));
    const apart =
      !!b &&
      !!o &&
      !!t &&
      o.kind === b.kind &&
      t.kind === b.kind &&
      !(b.kind === "file" && [b, o, t].some((r) => isBytes(r))) &&
      silently(() => {
        try {
          return !mergeShown(cb, keyPath(key), b, o, t).conflicts;
        } catch {
          return false;
        }
      });
    (apart ? asText : names).push(key);
  }
  return names.length
    ? { kind: "conflicts", names }
    : { kind: "clean", ...(asText.length ? { asText } : {}) };
}

uses

used by