52 lines · 2.1 KB
import type { MergeConflict } from "../git/merge";

const MARKER_START = /^<{7}(?: |\n|$)/;
const MARKER_MIDDLE = /^={7}\n?$/;
const MARKER_END = /^>{7}(?: |\n|$)/;
const LINE = /[^\n]*\n|[^\n]+$/g;

/**
 * Resolves every conflict block in merged marker text by keeping the `ours` side, leaving the cleanly
 * merged lines around it untouched. Returns null when the markers don't parse into `expected` blocks
 * (for example when the file itself contained marker-like lines), so the caller can treat it as a conflict.
 */
export function keepOurs(text: string, expected: number): string | null {
  const out: string[] = [];
  let state: "outside" | "ours" | "theirs" = "outside";
  let blocks = 0;
  for (const line of text.match(LINE) ?? []) {
    if (state === "outside") {
      if (MARKER_START.test(line)) state = "ours";
      else if (MARKER_MIDDLE.test(line) || MARKER_END.test(line)) return null;
      else out.push(line);
    } else if (state === "ours") {
      if (MARKER_MIDDLE.test(line)) state = "theirs";
      else if (MARKER_START.test(line) || MARKER_END.test(line)) return null;
      else out.push(line);
    } else if (MARKER_END.test(line)) {
      state = "outside";
      blocks++;
    } else if (MARKER_START.test(line) || MARKER_MIDDLE.test(line)) {
      return null;
    }
  }
  return state === "outside" && blocks === expected ? out.join("") : null;
}

/**
 * Replacement contents that resolve text conflicts in favor of `ours`. Conflicts without marker text
 * already resolve in the merged tree: binary and file/directory keep `ours`, modify/delete keeps the
 * modified file so no edit is lost.
 * Null when some text conflict could not be resolved mechanically.