@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
104 lines • 4.95 kB
JavaScript
import { PathSpec, FileType } from "../../../../types.js";
import { IOResult } from "../../../../io/types.js";
import { isEexist, isErofs } from "../../../../errors/fs.js";
import { FlagView } from "../../../spec/flag_view.js";
import { discover } from "./discover.js";
import { CannotMkdirError, ConfigLockError, GitError, InitReadOnlyError, NoWorkingDirectoryError, } from "./errors.js";
import { ensureDir, readOptional, statOf, takeLock, writeFile } from "./io.js";
import { DEFAULT_BRANCH } from "./constants.js";
import { validRefName } from "./refs.js";
import { startPoint, requireVfs } from "./util.js";
/** Write a new git directory's skeleton, keeping what is there. */
export async function layOut(vfs, gitdir, branch, config) {
for (const directory of ['objects/info', 'objects/pack', 'refs/heads', 'refs/tags', 'info'])
await ensureDir(vfs, gitdir.join(directory));
const files = {
HEAD: `ref: refs/heads/${branch}\n`,
config,
description: "Unnamed repository; edit this file 'description' to name the repository.\n",
};
for (const [name, text] of Object.entries(files)) {
const path = gitdir.join(name);
if ((await readOptional(vfs, path)) === null)
await writeFile(vfs, path, new TextEncoder().encode(text));
}
}
/**
* The git directory an `init` line names: `--git-dir`, the directory itself
* under `--bare`, and its `.git` otherwise.
*/
function namedGitdir(fl, texts) {
const start = startPoint(fl);
const explicit = fl.asPath('git_dir');
if (explicit !== undefined)
return explicit;
const target = PathSpec.fromStrPath(texts[0] ?? '.', undefined, start);
return fl.asBool('bare') ? target : target.join('.git');
}
/**
* Initialize through the dispatcher; no host templates, hooks or branch advisory.
*
* Reinitializing takes the config's lock as git does, so a read-only mount
* refuses a re-init too, in git's words for where it stopped: the directory an operand
* names, the config's lock, or the first other directory it had to make
* (pinned against git 2.47.3). With no templates, a bare repository in an
* existing directory stops at `objects` where git stops at its first template
* directory.
*/
export async function init(_accessor, paths, texts, ctx) {
const fl = new FlagView(ctx.flags);
const vfs = requireVfs(ctx);
const start = startPoint(fl);
const here = await statOf(vfs, start);
if (here?.type !== FileType.DIRECTORY)
throw new NoWorkingDirectoryError(start.rawPath, here === null ? 'ENOENT' : 'ENOTDIR');
const target = PathSpec.fromStrPath(texts[0] ?? '.', undefined, start);
if (target.walkError === 'ENOENT')
throw new CannotMkdirError(target.rawPath, 'No such file or directory');
const bare = fl.asBool('bare');
let gitdir = namedGitdir(fl, texts);
const branch = fl.asStr('initial_branch', DEFAULT_BRANCH);
// The ref rules alone: unlike a branch made later, the first one may be
// named HEAD or start with a dash (pinned against git 2.47.3 and 2.50.1).
if (!validRefName(`refs/heads/${branch}`))
throw new GitError(`invalid initial branch name: '${branch}'`);
const info = await statOf(vfs, gitdir);
if (info !== null &&
(info.type !== FileType.DIRECTORY || (await readOptional(vfs, gitdir.join('HEAD'))) !== null)) {
const location = await discover(vfs, target, gitdir, fl.asPath('work_tree') ?? null);
gitdir = location.commondir;
}
const existing = (await readOptional(vfs, gitdir.join('HEAD'))) !== null;
const [typed] = texts;
const made = typed !== undefined && (await statOf(vfs, target)) === null;
const settings = gitdir.join('config');
try {
await layOut(vfs, gitdir, branch, `[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = ${bare ? 'true' : 'false'}\n`);
if (existing)
await takeLock(vfs, settings);
}
catch (err) {
const path = err.virtualPath;
const locked = path === `${settings.virtual}.lock`;
if (locked && isEexist(err))
throw new ConfigLockError(settings.virtual, 'EEXIST');
if (!isErofs(err))
throw err;
if (made)
throw new CannotMkdirError(typed);
if (locked)
throw new ConfigLockError(settings.virtual, 'EROFS');
throw new InitReadOnlyError(path ?? gitdir.virtual);
}
const text = existing
? `Reinitialized existing Git repository in ${gitdir.virtual}/\n`
: `Initialized empty Git repository in ${gitdir.virtual}/\n`;
const warning = existing && fl.asStr('initial_branch')
? `warning: re-init: ignored --initial-branch=${branch}\n`
: '';
return [
new TextEncoder().encode(fl.asBool('quiet') ? '' : text),
new IOResult({ stderr: new TextEncoder().encode(warning) }),
];
}
//# sourceMappingURL=init.js.map