startMerge

tcmts.studio.startMerge · #pi7dfxv3 · term · runs on the server

uses node:child_process

Merge the branch theirs into the studio's own, as tcm merge does (on main, that lands it: main merged into it first, there, then its tests), run on, what it says kept as it comes (mergeRuns). squash: what came in as one entry (as sessions land); message: what the entry is for. One at a time. The run's id.

import { HttpError } from "#codebase/tcmts/server";
import { branchingCodebase, mergeRuns, tcmEnv } from "#codebase/tcmts/studio";
import type { MergeRun } from "#codebase/tcmts/studio";
import { spawn } from "node:child_process";
import { stripVTControlCharacters } from "node:util";

export async function startMerge(
  theirs: string,
  squash: boolean,
  message: string,
): Promise<string> {
  const cb = branchingCodebase();
  if (mergeRuns.some((r) => r.ended === undefined))
    throw new HttpError(409, "A merge is running already: one at a time.");
  if (!cb.store.hasBranch(theirs)) throw new HttpError(404, `There's no branch named ${theirs}.`);
  const into = cb.store.branch().name;
  if (theirs === into) throw new HttpError(400, `${theirs} is the branch it would merge into.`);
  const env: NodeJS.ProcessEnv = { ...tcmEnv(), NO_COLOR: "1" };
  const run: MergeRun = {
    id: Math.random().toString(36).slice(2, 10),
    theirs,
    into,
    started: Date.now(),
    lines: [],
  };
  mergeRuns.push(run);
  while (mergeRuns.length > 5) mergeRuns.shift();
  const args = [
    "merge",
    theirs,
    ...(squash ? ["--squash"] : []),
    ...(message.trim() ? ["--message", message.trim()] : []),
  ];
  const child = spawn(process.execPath, [process.argv[1]!, ...args], { cwd: cb.lay.root, env });
  let rest = "";
  const heard = (chunk: Buffer) => {
    rest += stripVTControlCharacters(chunk.toString("utf8"));
    const lines = rest.split("\n");
    rest = lines.pop()!;
    run.lines.push(...lines);
    if (run.lines.length > 2000) run.lines.splice(0, run.lines.length - 2000);
  };
  child.stdout.on("data", heard);
  child.stderr.on("data", heard);
  child.on("close", (code) => {
    if (rest) run.lines.push(rest);
    run.code = code ?? 1;
    run.ended = Date.now();
  });
  return run.id;
}

uses

used by