import git from "fullstacked/git";
Git in the core, built on go-git v6. No git binary is needed. Every directory is a path in the project, resolved like an fs path. Remotes are HTTP(S). Credentials come from a git-auth plugin.
Functions marked stream return a Duplex of progress text (the lines git prints on stderr) that must be consumed for the operation to run:
const progress = await git.clone(
"https://github.com/fullstackedorg/demo.git",
"/demo"
);
progress.on("data", (chunk) => console.log(new TextDecoder().decode(chunk)));
progress.on("close", () => console.log("cloned"));
progress.on("error", (e) => console.error(e));
| Function | Returns | Notes |
|---|---|---|
hasGit(directory) |
Promise<boolean> |
Whether directory is a repository |
init(directory, url) |
Promise<void> |
git init with default branch main, and origin set to url |
setConfig(directory, key, value) |
Promise<void> |
Writes .git/config. key is section.option or section.subsection.option (user.name, remote.origin.url). |
| Function | Returns | Notes |
|---|---|---|
status(directory) |
Promise<GitStatus> |
See below |
add(directory, path) |
Promise<void> |
"." adds everything |
restore(directory, ...files) |
Promise<void> |
Restores the index and the working tree for files |
reset(directory, hard, ...files) |
Promise<void> |
Mixed, or hard when hard is true, optionally for files only |
commit(directory, message, author) |
Promise<string> |
The new commit’s hash. author is { name, email }. |
head(directory) |
Promise<GitHead> |
{ branch, hash, type } |
log(directory) |
Promise<GitCommit[]> |
The last 10 commits from HEAD |
type GitStatus = {
head: GitHead;
staged: { [change: string]: string[] }; // e.g. { modified: ["a.ts"], added: ["b.ts"] }
unstaged: { [change: string]: string[] };
untracked: string[];
};
// change: "added" | "deleted" | "modified" | "copied" | "renamed" | "unmodified" | "untracked" | "unmerged" | "unknown"
type GitCommit = {
hash: string;
author: { name: string; email: string };
date: string; // "Mon Jan 2 15:04:05 2006 -0700"
message: string;
};
| Function | Returns | Notes |
|---|---|---|
clone(url, directory, opts?) |
stream | If directory exists, clones into directory/<repo name>. Empty repositories are allowed. A failed clone removes the directory. |
pull(directory, tunnel?) |
stream | Pulls the current branch from origin. “Already up to date” isn’t an error. |
fetch(directory, tunnel?) |
stream | Updates every remote-tracking branch and tag. The working tree is left alone. |
push(directory, tunnel?) |
stream | Pushes to origin |
checkout(directory, ref, create?, tunnel?) |
stream | ref is a branch, tag or commit. A branch or tag that exists only on the remote is fetched first. create: true makes a new branch. An unknown ref fails with cannot find branch, tag or commit "<ref>". |
merge(directory, branch) |
Promise<void> |
go-git’s merge (fast-forward), then a hard reset of the working tree to the result |
branch(directory) |
Promise<GitBranch[]> |
{ name, remote, local }[], local branches merged with origin’s (needs the network) |
tags(directory) |
Promise<GitTag[]> |
{ name, hash, remote, local }[], also from origin (needs the network) |
clone’s opts is { tunnel?: string; proxy?: { url: string; headers: Record<string, string> } }. For backward compatibility, a plain string is taken as the tunnel name.
tunnel: the name of a registered tunnel. The git HTTP traffic goes through it.proxy: a reverse proxy URL and extra headers. They are saved in the clone’s .git/config (http.proxy, http.extraHeader), so later pulls and pushes use them too.Before a clone, pull, push or fetch, the core sends a HEAD request to the remote’s host, so an unreachable host fails fast with a clear error.
The core asks for credentials only when a remote answers that authentication is required. It calls the context’s git-auth plugin with the host and expects { username, password } (a token goes in password), then retries once. Credentials are cached per host for the context’s lifetime and dropped if they fail.
import plugin from "fullstacked/plugin";
await plugin.register("git-auth", {
callback: async (host) => {
if (host === "github.com")
return {
username: "x-access-token",
password: process.env.GITHUB_TOKEN
};
return {};
}
});
A context can have one git-auth plugin. With none, an authenticated remote fails with no git auth plugins. The FullStacked app’s shell registers one that prompts in the terminal and stores credentials in /user_data/.
createGitAuthManager() was replaced by the git-auth plugin. Calling it only logs a warning.