socraticode
Version:
SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.
65 lines • 3.16 kB
JavaScript
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Giancarlo Erra - Altaire Limited
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { projectIdFromPath } from "../config.js";
const exec = promisify(execFile);
/** Resolve an observed branch through the existing project-identity rules. */
export function gitProjectId(projectPath, state) {
return projectIdFromPath(projectPath, state.ref?.replace(/^refs\/heads\//, "") ?? null);
}
/** Capture storage identity and a guard that rejects subsequent checkout changes. */
export function gitRefreshTarget(projectPath, state, allowCreate) {
const projectId = gitProjectId(projectPath, state);
return {
projectId,
allowCreate,
/** Reject a changed ref, commit, or configured identity before continuing. */
async assertCurrent() {
const current = await readGitState(projectPath);
if (!sameGitState(state, current) || gitProjectId(projectPath, current) !== projectId) {
throw new Error("Git checkout changed during indexing; refresh remains pending for the current checkout.");
}
},
};
}
/** Run a bounded Git query and preserve an actionable failure for the caller. */
async function git(projectPath, args) {
try {
const { stdout } = await exec("git", args, {
cwd: projectPath,
encoding: "utf8",
timeout: 5_000,
maxBuffer: 64 * 1024,
windowsHide: true,
});
return stdout.trim();
}
catch (err) {
const error = err;
if (error.code === "ENOENT")
throw new Error("Git refresh unavailable: Git or the checkout directory could not be found.");
if (error.killed)
throw new Error("Git refresh failed: Git did not respond within 5 seconds.");
throw new Error(`Git refresh failed: ${error.stderr?.trim() || error.message}`);
}
}
/** Read both ref and commit so a same-commit branch switch is still a change. */
export async function readGitState(projectPath) {
if (await git(projectPath, ["rev-parse", "--is-inside-work-tree"]) !== "true") {
throw new Error("Git refresh unavailable: this project is not a Git working tree.");
}
// Verify the ref did not move while the commit was being read. A checkout
// changing repeatedly is pending, never an invented stable observation.
const ref = await git(projectPath, ["rev-parse", "--symbolic-full-name", "HEAD"]);
const head = await git(projectPath, ["rev-parse", "--verify", "HEAD"]);
const refAfter = await git(projectPath, ["rev-parse", "--symbolic-full-name", "HEAD"]);
if (ref !== refAfter)
throw new Error("Git checkout changed while its state was being read; refresh will retry.");
return { ref: ref === "HEAD" ? null : ref, head };
}
/** Match both ref and commit; two absent observations are not synchronized. */
export function sameGitState(left, right) {
return left !== undefined && right !== undefined && left.ref === right.ref && left.head === right.head;
}
//# sourceMappingURL=git-state.js.map