testBranchesSharePatches

tcmts.store.test.testBranchesSharePatches · #i7rzabhk · term

Branches share patches: one made from another reads what that had through the same set, not a copy; what each records after is its own; a version coming back on one hides the patch out of it beneath, for that one alone; a merge's work set aside and put back is as it was; a deep stack of sets reads as a flat one; and a branch deleted takes the sets only it read.

import { PATCH_DEPTH } from "#codebase/tcmts/store";
import { open } from "#codebase/tcmts/store/test/history";
import assert from "node:assert/strict";

export const testBranchesSharePatches = () => {
  const h = (tag: string) => tag.padEnd(52, "z");
  const [a, b, c, d, e, f] = ["a", "b", "c", "d", "e", "f"].map(h);
  const s = open();
  const count = (table: string) =>
    (s.db.prepare(`SELECT count(*) AS n FROM ${table}`).get() as { n: number }).n;
  s.addPatch(a!, b!);
  s.createBranch("side");
  assert.equal(count("patches"), 1, "side reads main's patch, not a copy of it");

  s.addPatch(b!, c!);
  s.switchBranch("side");
  assert.equal(s.patched(a!), b, "side doesn't see main's later patch");
  s.addPatch(b!, d!);
  assert.equal(s.patched(a!), d);
  s.switchBranch("main");
  assert.equal(s.patched(a!), c);

  // a coming back on main: what replaced it then (in the set side shares) doesn't, on main
  s.addPatch(c!, a!);
  assert.equal(s.patched(a!), a);
  assert.equal(s.patched(b!), a);
  assert.deepEqual(s.patchesOf(), [
    { old: b, new: c },
    { old: c, new: a },
  ]);
  s.switchBranch("side");
  assert.equal(s.patched(a!), d);

  // a merge's work, set aside and put back
  const work = s.workOf("side");
  s.addPatch(e!, f!);
  assert.equal(s.patched(e!), f);
  s.setWorkOf("side", work);
  assert.equal(s.patched(e!), e);
  assert.equal(s.patched(a!), d);

  // every branch made freezes another set; past PATCH_DEPTH of them, a branch reads a flat copy, the same
  s.switchBranch("main");
  const made = Array.from(
    { length: PATCH_DEPTH + 3 },
    (_, i) => [h(`m${i}a`), h(`m${i}b`)] as const,
  );
  made.forEach(([o, n], i) => {
    s.addPatch(o, n);
    s.createBranch(`b${i}`);
  });
  const depth = (branch: string) =>
    (
      s.db
        .prepare(
          "WITH RECURSIVE chain(id) AS (SELECT patches FROM branches WHERE name = ? UNION ALL SELECT s.base FROM patch_sets s JOIN chain ON s.id = chain.id WHERE s.base IS NOT NULL) SELECT count(*) AS n FROM chain",
        )
        .get(branch) as { n: number }
    ).n;
  for (const branch of ["main", `b${made.length - 1}`]) {
    assert.ok(depth(branch) <= PATCH_DEPTH + 1, `${branch} reads through ${depth(branch)} sets`);
    s.switchBranch(branch);
    for (const [o, n] of made) assert.equal(s.patched(o), n);
    assert.equal(s.patched(a!), a);
  }

  // a branch deleted takes what only it read
  s.switchBranch("main");
  const sets = count("patch_sets");
  s.deleteBranch("side");
  assert.ok(count("patch_sets") < sets, `${count("patch_sets")} < ${sets}`);
  assert.equal(s.patched(a!), a);
}

uses