Git integration
This API is available since Optique 0.9.0.
The @optique/git package provides async value parsers for validating Git references (branches, tags, commits, remotes) using isomorphic-git. These parsers validate input against an actual Git repository, ensuring that users can only specify existing references.
import { gitBranch, gitTag, gitCommit, gitRef, gitRemote, gitRemoteBranch } from "@optique/git";
import { option, argument } from "@optique/core/primitives";Installation
deno add jsr:@optique/gitnpm add @optique/gitpnpm add @optique/gityarn add @optique/gitbun add @optique/gitGetting started
Import the parsers from @optique/git and use them with Optique primitives:
import { gitBranch } from "@optique/git";
import { argument } from "@optique/core/primitives";
const parser = argument(gitBranch());
// Accepts: "main", "develop", "feature/my-feature"
// Rejects: "nonexistent-branch"gitBranch()
Validates that input matches an existing local branch name in the repository.
import { gitBranch } from "@optique/git";
import { argument } from "@optique/core/primitives";
const branchParser = argument(gitBranch());Options
import type { GitParserOptions } from "@optique/git";Example
import { gitBranch } from "@optique/git";
import { option } from "@optique/core/primitives";
// Branch argument for git checkout-like command
const checkoutParser = option("-b", "--branch", gitBranch());
// Usage: myapp checkout --branch feature/my-featuregitTag()
Validates that input matches an existing tag name in the repository.
import { gitTag } from "@optique/git";
import { option } from "@optique/core/primitives";
const tagParser = option("-t", "--tag", gitTag());
// Usage: myapp release --tag v1.0.0Example with custom options
import { gitTag } from "@optique/git";
import { option } from "@optique/core/primitives";
const versionParser = option("--release", gitTag({
metavar: "VERSION",
}));gitRemote()
Validates that input matches an existing remote name in the repository.
import { gitRemote } from "@optique/git";
import { option } from "@optique/core/primitives";
const remoteParser = option("--remote", gitRemote());
// Usage: myapp fetch --remote origingitRemoteBranch()
Validates that input matches an existing branch on a specific remote.
import { gitRemoteBranch } from "@optique/git";
import { option } from "@optique/core/primitives";
const remoteBranchParser = option("--branch", gitRemoteBranch("origin"));
// Usage: myapp pull --branch mainDynamic remote with dependencies
Use the dependency system to validate branches against a user-specified remote. The gitRemoteBranch() parser works with async factory support in derived parsers:
import { gitRemote, gitRemoteBranch } from "@optique/git";
import { dependency } from "@optique/core/dependency";
import { object } from "@optique/core/constructs";
import { option } from "@optique/core/primitives";
// Wrap gitRemote() as a dependency source
const remoteParser = dependency(gitRemote());
// Create a derived parser that validates branches against the selected remote
const branchParser = remoteParser.derive({
metavar: "BRANCH",
mode: "async",
factory: (remote) => gitRemoteBranch(remote),
defaultValue: () => "origin",
});
const pullCommand = object({
remote: option("--remote", remoteParser),
branch: option("--branch", branchParser),
});
// Now --branch validates against the remote specified by --remote:
// myapp pull --remote upstream --branch feature/new
// → validates that "feature/new" exists on "upstream"Since gitRemoteBranch() returns an async parser, the derived parser automatically becomes async. The dependency system handles the mode combination seamlessly.
gitCommit()
Validates that input is a valid commit SHA (full or shortened) that exists in the repository. Returns the resolved full OID.
import { gitCommit } from "@optique/git";
import { option } from "@optique/core/primitives";
const commitParser = option("--commit", gitCommit());
// Usage: myapp revert --commit abc1234SHA formats supported
The parser accepts various SHA formats:
- Full 40-character SHA:
d670460b4b4aece5915caf5c68d12f560a9fe3e4 - Shortened SHAs (4+ characters):
d670460,d670460b4b4a
const result = await parseAsync(parser, ["-c", "550e840"]);
if (result.success) {
console.log(result.value); // Full 40-char OID
}gitRef()
A flexible parser that accepts any valid Git reference: branches, tags, or commits. Returns the resolved commit OID.
import { gitRef } from "@optique/git";
import { argument } from "@optique/core/primitives";
const refParser = argument(gitRef());
// Accepts: branch names, tags, or commit SHAs
// Returns: resolved commit OIDcreateGitParsers()
A factory function for creating multiple Git parsers with shared configuration.
import { createGitParsers } from "@optique/git";
import { option } from "@optique/core/primitives";
const git = createGitParsers({
dir: "/path/to/repo",
});
const parser = option("--branch", git.branch());
const tagParser = option("--tag", git.tag());
const commitParser = option("--commit", git.commit());Example with custom options per parser
import { createGitParsers } from "@optique/git";
const git = createGitParsers({
dir: "/path/to/repo",
metavar: "REF",
});
// Override metavar for specific parser
const branchParser = git.branch({ metavar: "BRANCH_NAME" });Async mode
All Git parsers operate in async mode because they perform I/O operations to read the Git repository:
import { gitBranch } from "@optique/git";
import { argument } from "@optique/core/primitives";
const parser = argument(gitBranch());
// parser.mode === "async"Use parseAsync() with async parsers:
const result = await parseAsync(parser, ["main"]);
if (result.success) {
console.log(result.value); // "main"
}Suggestions
Git parsers provide intelligent completion suggestions:
import { gitBranch, gitTag, gitRef } from "@optique/git";
import { argument } from "@optique/core/primitives";
// Suggests existing branches
const branchParser = argument(gitBranch());
// Completing "fe" suggests: "feature/*", "fix/*", etc.
// Suggests existing tags
const tagParser = argument(gitTag());
// Completing "v1" suggests: "v1.0.0", "v1.1.0", etc.
// Suggests both branches and tags
const refParser = argument(gitRef());
// Completing "v" suggests tags; completing "fe" suggests branchesError handling
Git parsers provide clear error messages:
$ myapp --branch nonexistent
Error: Branch nonexistent does not exist. Available branches: main, develop, feature/*.
$ myapp --tag v999.0.0
Error: Tag v999.0.0 does not exist. Available tags: v1.0.0, v2.0.0.
$ myapp --commit abc
Error: Invalid commit SHA: abc. Provide an abbreviated (4+) or full (40) hexadecimal commit SHA.
$ myapp --ref nonexistent-ref
Error: Reference nonexistent-ref does not exist. Provide a valid branch, tag, or commit SHA.Custom error messages
You can customize error messages using the errors option with the Message type from @optique/core/message:
import { gitBranch } from "@optique/git";
import { message, valueSet } from "@optique/core/message";
const parser = gitBranch({
errors: {
notFound: (input, available) =>
message`Branch ${input} not found. Available: ${
valueSet(available ?? [], "none")
}`,
listFailed: (dir) =>
message`Cannot read git repository at ${dir}`,
}
});Error types
The errors option supports the following error types:
notFound(input, available?)— Called when the git reference is not found. Provides the invalid input and optionally a list of available references.listFailed(dir)— Called when listing git references fails, typically when the directory is not a valid git repository.invalidFormat(input)— Called for commit SHA validation failures when the input format is invalid (e.g., too short).remoteNotFound(remote, availableRemotes)— Called bygitRemoteBranch()when the named remote does not exist.
Example with gitCommit
import { gitCommit } from "@optique/git";
import { message } from "@optique/core/message";
const parser = gitCommit({
errors: {
invalidFormat: (input) =>
message`${input} must be 4-40 characters.`,
notFound: (input) =>
message`Commit ${input} not found in repository.`,
}
});Example with createGitParsers
import { createGitParsers } from "@optique/git";
import { message } from "@optique/core/message";
const git = createGitParsers({
errors: {
notFound: (input, available) =>
message`${input} is not a valid reference.`,
}
});
const branchParser = git.branch();
const tagParser = git.tag();
const commitParser = git.commit();Metavar defaults
Each parser uses an appropriate default metavar for help text:
| Parser | Default metavar |
|---|---|
gitBranch() | "BRANCH" |
gitTag() | "TAG" |
gitRemote() | "REMOTE" |
gitRemoteBranch() | "BRANCH" |
gitCommit() | "COMMIT" |
gitRef() | "REF" |
Override with the metavar option:
import { gitBranch, gitTag } from "@optique/git";
const branchParser = gitBranch({ metavar: "BRANCH_NAME" });
const tagParser = gitTag({ metavar: "RELEASE_VERSION" });Exported utilities
The package also re-exports several utilities from isomorphic-git for advanced use cases:
import { expandOid, listBranches, listTags, listRemotes, readObject, resolveRef } from "@optique/git";expandOid()— Expand short SHAs to full OIDslistBranches()— List all local brancheslistTags()— List all tagslistRemotes()— List all remotesreadObject()— Read a Git objectresolveRef()— Resolve a ref to its OID
Complete example
A Git-like CLI application using Git parsers:
const result = await parseAsync(app, ["checkout", "-b", "develop", "main"]);
if (result.success) {
console.log(result.value);
}