import { type FileStat, PathSpec } from '../../../../types.ts';
import type { Files } from '../../../../workspace/files.ts';
/** Read one virtual path through the session's files. */
export declare function readFile(vfs: Files, path: PathSpec): Promise<Uint8Array>;
/** What stands at a path, a link followed, null when nothing does. */
export declare function statOf(vfs: Files, path: PathSpec): Promise<FileStat | null>;
/**
 * What sits at a path, a link reported as itself, null when nothing does.
 *
 * git lstats the working tree, so a symlink is an entry in its own right: it
 * records mode 120000 and the target string, and reports a link to nothing as
 * untracked rather than as absent.
 */
export declare function lstat(vfs: Files, path: PathSpec): Promise<FileStat | null>;
/** Whether a path names a symlink itself, the lstat a walk asks before it
 * follows anything. */
export declare function isLink(vfs: Files, path: PathSpec): Promise<boolean>;
/**
 * The bytes git stores for one working-tree entry.
 *
 * A symlink's blob is its target string, not what the target holds, so reading
 * through the link would stage a second copy of the target under mode 100644
 * and then report the entry modified forever after (the staged blob and the
 * bytes behind the link never match). The target is namespace state, which is
 * why it arrives on the stat rather than from a read.
 */
export declare function entryBytes(vfs: Files, path: PathSpec, info: FileStat): Promise<Uint8Array>;
/**
 * Materialize one tree entry into the working tree.
 *
 * A 120000 entry is a symlink whose blob is the target string, so it is
 * restored through the namespace rather than written as content: writing the
 * blob would leave a regular file spelling the target.
 *
 * Whatever is already there goes first, whichever kind it is, because git
 * replaces a tree entry rather than merging with it. Two of the four
 * combinations are the ones that corrupt state: writing a regular blob at a
 * path the namespace holds a link for follows the link and lands the content in
 * the file it points at, damaging a path no branch touched while the link
 * stays; and linking over a regular file leaves that file behind the link,
 * ready to reappear when the link goes. The fourth, a link over a link, is the
 * retarget a checkout does when a branch moves where a link points: symlink(2)
 * does not overwrite, so the old name is removed rather than replaced in place.
 * The check is one lstat, which the listing the checkout walked usually
 * answers.
 *
 * The permission bits are part of the entry, not decoration on it. git records
 * exactly one of them, the owner's execute bit, and puts it back in both
 * directions: `chmod -x` on a `100755` path is a modification `restore` undoes,
 * and `chmod +x` on a `100644` one is a modification it undoes the other way.
 * Writing the bytes alone left the bit as the working tree had it, so the file
 * came back unrunnable and `status` went on calling it modified for ever. The
 * write is unconditional rather than probed: a stat to decide costs the same op
 * as the setattr it would save, and the backends git actually runs on apply it
 * natively, so nothing reaches the overlay.
 */
export declare function restoreEntry(vfs: Files, path: PathSpec, mode: string, blob: Uint8Array): Promise<void>;
/**
 * Leave a submodule's working tree alone, but make sure it has one.
 *
 * A 160000 entry names a commit in another repository, which this one does not
 * hold: reading it as a blob is either a miss (an ordinary submodule keeps its
 * objects in its own store) or, when the id does happen to resolve here, an
 * empty string written over the directory. git does neither. It checks out no
 * submodule content at all without `--recurse-submodules`, and all the entry
 * asks of the working tree is that a directory stand at the name: an existing
 * one is left exactly as it is, untracked work included, a regular file or a
 * link is replaced by an empty one, and a missing one is created. Pinned
 * against git 2.50.1.
 *
 * @param vfs the session's files
 * @param path absolute virtual path of the submodule
 */
export declare function keepGitlink(vfs: Files, path: PathSpec): Promise<void>;
/**
 * Take a submodule's directory away, or say why it stays.
 *
 * The other direction of `keepGitlink`, and it is not an unlink: what stands at
 * a 160000 entry is a directory, so git calls `rmdir` and warns rather than
 * failing when that cannot be done. An empty one goes; one still holding a
 * checked-out submodule, or anything else untracked, stays and is named; a
 * regular file or a link at the name is the `ENOTDIR` wording of the same
 * warning; a name with nothing at it is silent. The switch itself succeeds
 * either way, which is the whole point of warning instead of throwing. Pinned
 * against git 2.50.1.
 *
 * @param vfs the session's files
 * @param path absolute virtual path of the submodule
 * @param name the path as git prints it, repository-relative
 * @returns the warning line to write, null when there is nothing to say
 */
export declare function dropGitlink(vfs: Files, path: PathSpec, name: string): Promise<string | null>;
/** Read a byte range of one virtual path. */
export declare function readRange(vfs: Files, path: PathSpec, offset: number, size: number): Promise<Uint8Array>;
/**
 * Read a path that a repository may legitimately not have.
 *
 * `packed-refs` and `HEAD`-adjacent files are absent in perfectly valid
 * repositories, so a miss is an answer rather than an error.
 */
export declare function readOptional(vfs: Files, path: PathSpec): Promise<Uint8Array | null>;
/** List a directory, empty when it does not exist. */
export declare function readNames(vfs: Files, path: PathSpec): Promise<string[]>;
/**
 * Create a directory and every missing directory above it.
 *
 * Written out rather than delegated to `mkdir -p` because the parents flag is a
 * per-backend capability: the ops factory only wires `parents: true` for
 * backends that declare it, so a plain `mkdir` of `objects/ab` fails on the
 * rest.
 *
 * Existence is probed with a point stat, which on a prefix store misses a
 * directory that has no object of its own. That false negative is harmless here
 * and the reason this does not need the two-channel stat: on such a store a
 * directory is the set of keys under it, so creating one again costs a no-op
 * rather than an error.
 */
export declare function ensureDir(vfs: Files, path: PathSpec): Promise<void>;
/**
 * Whether a path names a directory, false when nothing is there.
 *
 * Asked with a stat rather than inferred from a failed read: a read of a
 * directory misses on a keyed store and raises EISDIR on disk, so "the read did
 * not work" says nothing about what the path is.
 */
export declare function isDirectory(vfs: Files, path: PathSpec): Promise<boolean>;
/** Whether a point lookup finds anything at a path. */
export declare function exists(vfs: Files, path: PathSpec): Promise<boolean>;
/**
 * Take a file's lock and let it go, as git does before rewriting it.
 *
 * git creates `<path>.lock` before it rewrites the index or the config, even
 * when nothing in them changes, so a read-only repository refuses the verb there
 * before it looks any further. Creating the lock and removing it is that
 * refusal, and it leaves the file itself alone, so a write another session makes
 * to it is never undone. A lock already there is another writer's, which git
 * refuses and so does this (EEXIST), untouched. The look and the create are two
 * ops, since the dispatcher has no exclusive create.
 */
export declare function takeLock(vfs: Files, path: PathSpec): Promise<void>;
/** Write one virtual path, creating the directories above it. */
export declare function writeFile(vfs: Files, path: PathSpec, data: Uint8Array): Promise<void>;
/**
 * Delete one virtual path, tolerating one that is already gone.
 *
 * A miss is an answer rather than an error for every caller here: unstaging a
 * path deletes whatever ref or lock may or may not exist, and a checkout removes
 * files the other branch does not have.
 */
export declare function removeFile(vfs: Files, path: PathSpec): Promise<void>;
/**
 * The nearest component above an entry that is not a directory.
 *
 * An entry's path is only a way through the working tree while every component
 * above it is a directory. Anything else standing on one -- a symlink, a
 * regular file, tracked or not -- is not a way through, and the two directions
 * take it differently. Writing the entry *replaces* it with the directory the
 * entry needs, leaving whatever a link pointed at exactly as it was; removing
 * the entry does nothing at all, because the path never led there. Both halves
 * are what git does, probed against git 2.50.1.
 *
 * The lstat is asked before the stat, and the order is the whole point:
 * `stat` dereferences, so a link to a directory stats as a directory and the
 * walk would carry on straight through it.
 *
 * An exact-path lookup cannot see any of this, since what is in the way sits
 * above the name being looked up rather than on it.
 *
 * @param vfs the session's files
 * @param worktree absolute virtual path of the working tree root
 * @param name the entry, repository-relative
 * @returns the absolute virtual path of the nearest such component, null when
 *   every component above the entry is a directory
 */
export declare function blockingAncestor(vfs: Files, worktree: PathSpec, name: string): Promise<PathSpec | null>;
/**
 * Move one virtual path, file or directory, to another name.
 *
 * The mount's own rename, so a directory moves with everything under it,
 * tracked or not, which is what `git mv` does with a directory. The
 * destination's directory is not created: git's rename fails when it is
 * missing, and the caller words that failure.
 */
export declare function renamePath(vfs: Files, source: PathSpec, target: PathSpec): Promise<void>;
/**
 * Ask of every destination first what the write loop would meet later.
 *
 * `removeTree` refuses a directory holding a mount, but the loop reaches one
 * entry at a time, so a refusal there leaves the entries already written
 * standing on the target's content with HEAD and the index still where they
 * were. Asking first is the shape every other collision check in these verbs
 * already has: name what is in the way and change nothing. The condition
 * mirrors the write loop's exactly, a link included, so a destination the loop
 * would not clear is not refused here either.
 *
 * @param vfs the session's files
 * @param worktree absolute virtual path of the working tree root
 * @param names repository-relative paths about to be written
 */
export declare function refuseReplacedMounts(vfs: Files, worktree: PathSpec, names: readonly string[]): Promise<void>;
/**
 * Refuse a removal that would take a nested mount with it.
 *
 * A mount nested inside the working tree is served by another VFS, and
 * `readdir` merges it into the parent's listing, so a walk that empties a
 * directory walks straight into the child backend and unlinks what is in it.
 * No branch ever recorded any of that, and the `rmdir` that follows takes the
 * mount root itself. Asking the mount table is the only way to see the
 * boundary: the parent backend cannot.
 *
 * A mount this session cannot see still blocks the removal: `submounts`
 * reports it by the nearest folder above it the session sees, which is not a
 * mount root, so only a visible one is named, naming a hidden mount being what
 * the hide exists to prevent.
 *
 * @param vfs the session's files
 * @param path absolute virtual path about to be removed
 */
export declare function refuseMount(vfs: Files, path: PathSpec): void;
/**
 * Delete a path and everything under it, tracked or not.
 *
 * git replaces a tree entry rather than merging with it, so a directory
 * standing where the source keeps a file goes entirely. That is one of the few
 * places git removes a file it never tracked: an untracked child keeps the
 * directory alive after the tracked ones are gone, and restoring the file over
 * it would otherwise fail with the index already changed.
 *
 * A file and an absent path both walk out through the same two steps, since
 * `readdir` reads a non-directory as nothing there and `rmdir` refuses it.
 *
 * A child that is a symlink is unlinked, never descended into. Its lstat has
 * to say so, because `readdir` dereferences: a link to a directory lists
 * that directory's contents, and recursing on them deletes a tree outside the
 * one being replaced. `rm -r` does not follow a link either, so a branch
 * recording a file where the working tree has a directory takes the link away
 * with the directory and leaves whatever it pointed at exactly as it was.
 * Pinned against git 2.50.1.
 *
 * @param vfs the session's files
 * @param path absolute virtual path to clear
 */
export declare function removeTree(vfs: Files, path: PathSpec): Promise<void>;
/**
 * Drop the directories a deletion left empty, up to a root.
 *
 * git removes a directory the moment its last tracked file is deleted or
 * restored away, so `rm -r docs` leaves no `docs/` behind. The walk stops at the
 * first directory that still holds something and never touches `stop` itself.
 *
 * @param vfs the session's files
 * @param path the file that was removed
 * @param stop the working tree root
 */
export declare function removeEmptyParents(vfs: Files, path: PathSpec, stop: PathSpec): Promise<void>;
//# sourceMappingURL=io.d.ts.map