testMergesFollowHistory

tcmts.cli.test.e2e.history.testMergesFollowHistory · #wnvl5bfh · term

Merges follow history: fast-forwards, recorded merges, and remotes that move forward only.

import { tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import { codebase } from "#codebase/tcmts/cli/test/e2e/history";
import assert from "node:assert/strict";

export const testMergesFollowHistory = () => {
  const root = tempDir("history");
  const a = codebase(root, "c");
  a.tcm("init");
  a.write(`export const base = 1;\n`);
  a.tcm("add");
  a.tcm("branch", "feature");
  a.tcm("switch", "feature");
  a.write(`export const feature = 2;\n`);
  a.tcm("add");
  a.tcm("switch", "main");
  // main hasn't moved: merging is moving it to feature's head, with no merge entry
  assert.match(a.tcm("merge", "feature"), /fast-forwarded main to feature/);
  assert.equal(a.entries()[0], (a.tcm("switch", "feature"), a.entries()[0]));
  a.tcm("switch", "main");

  // both moved: a merge entry with both heads as parents, and merging again brings nothing (into a branch of main's:
  // on main, a branch lands, main coming into it first)
  a.tcm("branch", "m");
  a.tcm("switch", "m");
  a.write(`export const onMain = 3;\n`);
  a.tcm("add");
  a.tcm("switch", "feature");
  a.write(`export const onFeature = 4;\n`);
  a.tcm("add");
  const featureHead = a.entries()[0]!;
  a.tcm("switch", "m");
  assert.match(a.tcm("merge", "feature"), /merged cleanly/);
  assert.match(a.tcm("history", "1"), new RegExp(`merge feature  merged #${featureHead}`));
  assert.match(a.tcm("merge", "feature"), /already has everything in feature: no changes/);
  a.tcm("switch", "main");

  // another codebase changes too; pulling merges (from the common ancestor), and pushing that back is a
  // fast-forward for the first, which then pulls nothing new to merge
  const b = codebase(root, "d");
  b.tcm("init");
  b.tcm("remote", "add", "origin", a.dir);
  b.tcm("pull");
  b.write(`export const fromB = 5;\n`);
  b.tcm("add");
  a.write(`export const fromA = 6;\n`);
  a.tcm("add");
  assert.throws(() => b.tcm("push"), /has changes you don't have/);
  assert.match(b.tcm("pull"), /merged origin\/main into main[\s\S]*\+ fromA[\s\S]*merged cleanly/);
  assert.match(b.tcm("push"), /pushed origin\/main/);
  assert.deepEqual(a.entries(), b.entries(), "the push brought b's history, merge included");
  assert.match(a.tcm("ls"), /fromB/);
}

uses