Vectrum
Sign In
66 lines · 2.7 KB · TypeScript
// The default branch of a repository whose HEAD dangles (its first push had no main). Run with `bun test`.
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdtempSync, rmSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { after, before, test } from "node:test";

const tmp = mkdtempSync(path.join(os.tmpdir(), "vectrum-default-branch-test-"));
process.env.DATA_ROOT = path.join(tmp, "data");

const sh = (cwd: string, ...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8", stdio: "pipe" });

let git: typeof import("./git");
let repo: string;
let work: string;

/** Commits on `branch` in the work tree and pushes it, a second later than the last so dates order them. */
let clock = 1_700_000_000;
function pushBranch(branch: string) {
  sh(work, "checkout", "-q", "-B", branch);
  const date = `${clock++} +0000`;
  execFileSync("git", ["-c", "user.name=a", "-c", "user.email=a@x", "commit", "-q", "--allow-empty", "-m", branch], {
    cwd: work,
    env: { ...process.env, GIT_AUTHOR_DATE: date, GIT_COMMITTER_DATE: date },
  });
  sh(work, "push", "-q", repo, `${branch}:refs/heads/${branch}`);
}

before(async () => {
  git = await import("./git");
  repo = path.join(tmp, "srv.git");
  await git.initBareRepo(repo);
  work = path.join(tmp, "work");
  sh(tmp, "init", "-q", "-b", "start", work);
});

after(() => rmSync(tmp, { recursive: true, force: true }));

test("without main, master wins over a more recently changed branch, and gets pinned", async () => {
  pushBranch("feature");
  await git.pinDefaultBranch(repo);
  assert.equal(await git.defaultBranch(repo), "feature");
  assert.equal(sh(repo, "symbolic-ref", "HEAD").trim(), "refs/heads/main", "HEAD stays dangling without main or master");

  pushBranch("master");
  pushBranch("settings-parity");
  assert.equal(await git.defaultBranch(repo), "master");
  await git.pinDefaultBranch(repo);
  assert.equal(sh(repo, "symbolic-ref", "HEAD").trim(), "refs/heads/master");

  // Once pinned, a later main doesn't take over.
  pushBranch("main");
  await git.pinDefaultBranch(repo);
  assert.equal(await git.defaultBranch(repo), "master");
});

test("the default branch can be changed, but only to a branch that exists", async () => {
  await git.setDefaultBranch(repo, "feature");
  assert.equal(await git.defaultBranch(repo), "feature");
  await git.pinDefaultBranch(repo);
  assert.equal(await git.defaultBranch(repo), "feature");
  await assert.rejects(git.setDefaultBranch(repo, "nope"), git.GitError);
  await assert.rejects(git.setDefaultBranch(repo, "--output=/tmp/x"), git.GitError);
  assert.equal(await git.defaultBranch(repo), "feature");
});