import{Repository,BranchType,RemoteCallbacks,PushOptions}from'@napi-rs/simple-git'Repository.init('/path/to/repo')// init a git repositoryconstrepo=newRepository('/path/to/repo')// Open an existed repo// Last-modified commit time of `build.rs` in milliseconds since the Unix epoch.// Returns a `number`; throws when no commit in history touched the path.constlastModified=repo.getFileLatestModifiedDate('build.rs')console.log(newDate(lastModified))// 2022-03-13T12:47:47.920Z// Async version of `getFileLatestModifiedDate`, also a `number` (rejects if no history).constlastModifiedAsync=awaitrepo.getFileLatestModifiedDateAsync('build.rs')console.log(lastModifiedAsync)// 1647175667920// Null-safe alternative: a `Date`, or `null` (never throws) when no commit ever// touched the path.constlastModifiedDate=repo.getFileLastModifiedDate('build.rs')if(lastModifiedDate)console.log(lastModifiedDate)// 2022-03-13T12:47:47.920Z// Enriched metadata for the last commit that touched a file.// Returns `null` (does **not** throw) when the path has no commit history.constmod=repo.getFileLatestModified('build.rs')if(mod){console.log(mod.authorName,mod.authorEmail)// 'LongYinan' 'github@lyn.one'console.log(mod.committerTime)// a `Date`, identical to getFileLastModifiedDate('build.rs')console.log(mod.commitId,mod.summary)}// Bulk: resolve many files in a single history walk (early-exits once all are found).// Every input path is present as a key; a never-committed path maps to `null`.constmods=repo.getFilesLatestModified(['build.rs','Cargo.toml'])console.log(mods['build.rs']?.committerName)console.log(mods['Cargo.toml']?.committerTime)// a `Date`// Empty input returns `{}`:console.log(repo.getFilesLatestModified([]))// {}// ---- Working-tree status (like `git status`) ----constchanges=repo.statuses()// => FileStatus[]for(constfileofchanges){console.log(file.path,file.isWtModified,file.isIndexNew)}console.log(repo.statusFile('README.md').isWtModified)// status of a single pathconstscanned=awaitrepo.statusesAsync({includeIgnored: true})// off-thread scan// ---- Config + default signature ----constconfig=repo.config()// => Config (system + global + repo, prioritized)config.setString('user.name','LongYinan')console.log(config.getString('user.name'))// 'LongYinan'console.log(config.getBoolean('core.bare'))// falseconstsig=repo.signature()// built from user.name / user.emailconsole.log(sig.name(),sig.email())// 'LongYinan' 'github@lyn.one'// ---- Stage from the working tree and commit ----constindex=repo.index()// => Index (the staging area)index.addPath('file.txt')index.write()consttreeOid=index.writeTree()// OID of the staged treeconsttree=repo.findTree(treeOid)!constparent=repo.head().target()!// current tip OIDconstcommitId=repo.commit('HEAD',sig,sig,'commit from workdir',tree,[parent])console.log(commitId)// 40-char hex OIDconsole.log(repo.blob(Buffer.from('hello')))// write a blob straight to the ODB => OID// ---- Branches ----consttip=repo.findCommit(parent)!constbranch=repo.branch('feature',tip,false)// => Branchconsole.log(branch.name(),branch.isHead())// 'feature' falsefor(constbofrepo.branches(BranchType.Local))console.log(b.name())repo.findBranch('feature',BranchType.Local)?.delete()// ---- Checkout / HEAD / refs ----repo.checkoutTree(tip.asObject(),{force: true})// update workdir to a tree-ishrepo.setHead('refs/heads/feature')// move HEAD (without touching the workdir)repo.checkoutHead({force: true})// sync the workdir to HEADrepo.setHeadDetached(tip.id())// detach HEAD at a commitrepo.reference('refs/heads/tmp',tip.id(),true,'create tmp')// a direct refrepo.referenceSymbolic('refs/heads/alias','refs/heads/main',true,'alias main')// ---- Blame ----for(consthunkofrepo.blameFile('build.rs')){console.log(hunk.finalStartLine,hunk.linesInHunk,hunk.finalCommitId,hunk.finalAuthorName)}console.log(repo.blameLine('build.rs',10)?.finalAuthorName)// hunk for line 10, or null// ---- Push ----constremote=repo.findRemote('origin')!constcallbacks=newRemoteCallbacks()// Per-ref result: one object per updated reference..pushUpdateReference(({ refname, status })=>{console.log(refname,status)// 'refs/heads/main' null (null === accepted)})// Pack-transfer progress: a single PushTransferProgress object..pushTransferProgress(({ current, total, bytes })=>{console.log(`${current}/${total} objects, ${bytes} bytes`)})remote.push(['refs/heads/main'],newPushOptions().remoteCallback(callbacks))// ---- Async (off-main-thread) network + commit ----// Clone, commit, fetch and push also have async variants that run their git// work on a worker thread and return a Promise. `commitAsync` mirrors `commit`.constcloned=awaitRepository.cloneAsync('https://example.com/repo.git','/tmp/clone')constasyncOid=awaitrepo.commitAsync('HEAD',sig,sig,'async commit',tree,[parent])// fetchAsync / pushAsync accept data-only Fetch/Push options. They do NOT accept// RemoteCallbacks (JS callbacks can't run off the main thread) — use the sync// fetch()/push() when you need credential or progress callbacks.awaitremote.fetchAsync(['refs/heads/main:refs/remotes/origin/main'])awaitremote.pushAsync(['refs/heads/main'],newPushOptions().packbuilderParallelism(1))// ---- Tags & diff ----// tagForeach hands the callback a single { id, nameBytes } object per tag.repo.tagForeach(({ id, nameBytes })=>{console.log(id,nameBytes.toString('utf8'))// '<40-hex>' 'refs/tags/v1.0.0'returntrue// return false to stop iterating})// DiffOptions.showUnmodified pulls unmodified files into the diff so they show// up in `deltas()` (with an Unmodified status) instead of being skipped.constheadTree=repo.head().peelToTree()repo.diffTreeToWorkdir(headTree,{showUnmodified: true})exportclassRepository{staticinit(p: string): Repositoryconstructor(gitDir: string)/** * Asynchronous variant of `clone`, performed off the main thread. Resolves * with a ready-to-use `Repository` once the clone completes. */staticcloneAsync(url: string,path: string,signal?: AbortSignal|undefined|null): Promise<Repository>/** Retrieve and resolve the reference pointed at by HEAD. */head(): Reference/** * Last-modified commit time of `filepath` in milliseconds since the Unix * epoch. Throws when no commit in history touched the path. For a * `null`-on-missing `Date`, use `getFileLastModifiedDate`. */getFileLatestModifiedDate(filepath: string): numbergetFileLatestModifiedDateAsync(filepath: string,signal?: AbortSignal|undefined|null): Promise<number>/** * Last-modified commit time of `filepath` as a `Date`, or `null` when no * commit in history touched the path (never throws for the missing case). */getFileLastModifiedDate(filepath: string): Date|nullgetFileLastModifiedDateAsync(filepath: string,signal?: AbortSignal|undefined|null): Promise<Date|null>/** * Last commit that modified `filepath`, with author/committer identity. * Returns `null` when no commit in history touched the path. */getFileLatestModified(filepath: string): FileModification|nullgetFileLatestModifiedAsync(filepath: string,signal?: AbortSignal|undefined|null): Promise<FileModification|null>/** * Resolve the last commit that modified each of `filepaths` in a single * history walk. Every input path is a key; never-committed paths map to `null`. */getFilesLatestModified(filepaths: Array<string>): Record<string,FileModification|null>getFilesLatestModifiedAsync(filepaths: Array<string>,signal?: AbortSignal|undefined|null): Promise<Record<string,FileModification|null>>/** Repository config view (system + global + repo, prioritized). */config(): Config/** Default signature built from `user.name` / `user.email`. */signature(): Signature/** List the branches in the repository, optionally filtered by type. */branches(filter?: BranchType|undefined|null): Array<Branch>/** Lookup a branch by name and type; `null` when it does not exist. */findBranch(name: string,branchType: BranchType): Branch|null/** Create a new branch pointing at a target commit. */branch(branchName: string,target: Commit,force: boolean): Branch/** Check out a tree-ish into the working directory (does not move HEAD). */checkoutTree(treeish: GitObject,options?: CheckoutOptions|undefined|null): void/** Update the index and the working tree to match HEAD. */checkoutHead(options?: CheckoutOptions|undefined|null): void/** Update the working tree to match the index. */checkoutIndex(options?: CheckoutOptions|undefined|null): void/** Make HEAD point to the reference named `refname`. */setHead(refname: string): void/** Detach HEAD directly at the commit with the given OID. */setHeadDetached(oid: string): void/** Create a new direct reference named `name` pointing at `oid`. */reference(name: string,oid: string,force: boolean,logMessage: string): Reference/** Create a new symbolic reference named `name` pointing at `target`. */referenceSymbolic(name: string,target: string,force: boolean,logMessage: string): Reference/** Create a new commit; `parents` is an optional list of parent OID hex strings. */commit(updateRef: string|undefined|null,author: Signature,committer: Signature,message: string,tree: Tree,parents?: Array<string>|undefined|null): string/** Asynchronous variant of `commit`, performed off the main thread. Resolves with the new commit's OID hex. */commitAsync(updateRef: string|undefined|null,author: Signature,committer: Signature,message: string,tree: Tree,parents?: Array<string>|undefined|null,signal?: AbortSignal|undefined|null): Promise<string>/** Get the index (staging area) for this repository. */index(): Index/** Write an in-memory buffer to the object database as a blob; returns its OID hex. */blob(data: Uint8Array): string/** Read a file and write its content to the object database as a blob; returns its OID hex. */blobPath(path: string): string/** List the working-tree and index status of files (like `git status`). */statuses(options?: StatusOptions|undefined|null): Array<FileStatus>/** Status of a single file by its workdir-relative path. */statusFile(path: string): FileStatus/** Asynchronous variant of `statuses`, computed off the main thread. */statusesAsync(options?: StatusOptions|undefined|null,signal?: AbortSignal|undefined|null): Promise<Array<FileStatus>>/** Blame `path`: who last changed each line, as ordered hunks. */blameFile(path: string,options?: BlameOptions|undefined|null): Array<BlameHunk>/** Blame `path` and return only the hunk covering `lineNo` (1-based), or `null`. */blameLine(path: string,lineNo: number,options?: BlameOptions|undefined|null): BlameHunk|null/** Asynchronous variant of `blameFile`, computed off the main thread. */blameFileAsync(path: string,options?: BlameOptions|undefined|null,signal?: AbortSignal|undefined|null): Promise<Array<BlameHunk>>/** Create an annotated tag (and its ref); returns the new tag object's OID. */tag(name: string,target: GitObject,tagger: Signature,message: string,force: boolean): string/** Create an annotated tag object WITHOUT a ref; returns its OID. */tagAnnotation(name: string,target: GitObject,tagger: Signature,message: string): string/** Lookup a tag object by OID; `null` when it does not exist. */findTag(oid: string): Tag|null/** Lookup a tag object by hash prefix; `null` when no tag matches. */findTagByPrefix(prefixHash: string): Tag|null/** Read libgit2's merge message (`.git/MERGE_MSG`). */mergeMessage(): string/** Remove the merge message (`.git/MERGE_MSG`). */removeMergeMessage(): void/** Add a remote with the provided fetch refspec to the configuration. */remoteWithFetch(name: string,url: string,refspec: string): Remote}/** * Last commit that modified a file, with author/committer identity. * All times are `Date`s (UTC; timezone offset ignored). */exportinterfaceFileModification{/** 40-char lowercase hex OID of the last commit that modified the file. */commitId: string/** Commit summary (first line). Undefined if absent or not valid UTF-8. */summary?: string/** Author name. Undefined if not valid UTF-8. */authorName?: string/** Author email. Undefined if not valid UTF-8. */authorEmail?: string/** Author time, as a `Date`. */authorTime: Date/** Committer name. Undefined if not valid UTF-8. */committerName?: string/** Committer email. Undefined if not valid UTF-8. */committerEmail?: string/** Committer time, as a `Date`. Identical to `getFileLastModifiedDate`. */committerTime: Date/** * The commit that FIRST added this file (its creation), or `undefined` for a * glob, or a directory/submodule path at HEAD. A delete-then-re-add returns * the ORIGINAL add; merge commits are included; no rename-follow. */created?: CommitInfo}/** * A single commit's identity/time metadata, reused for the commit that CREATED * a file (`FileModification.created`). Same 8 fields as `FileModification`'s * commit metadata. All times are `Date`s (UTC; timezone offset ignored). */exportinterfaceCommitInfo{/** 40-char lowercase hex OID of the commit that first added the file. */commitId: string/** Commit summary (first line). Undefined if absent or not valid UTF-8. */summary?: string/** Author name. Undefined if not valid UTF-8. */authorName?: string/** Author email. Undefined if not valid UTF-8. */authorEmail?: string/** Author time, as a `Date`. */authorTime: Date/** Committer name. Undefined if not valid UTF-8. */committerName?: string/** Committer email. Undefined if not valid UTF-8. */committerEmail?: string/** Committer time, as a `Date`. */committerTime: Date}/** * A git configuration store. Obtain one with `repo.config()` (system + global + * repository, prioritized) or `Config.openDefault()` (system/global/XDG only). */exportclassConfig{/** Open global, XDG and system config into one prioritized object. */staticopenDefault(): Config/** Get a string config value (highest-priority occurrence wins). */getString(name: string): stringgetBoolean(name: string): booleangetNumber(name: string): number/** i64 value as a `bigint` (no >2^53 truncation). */getBigInt(name: string): bigint/** Set a value in the highest-level config file (usually the local one). */setString(name: string,value: string): voidsetBoolean(name: string,value: boolean): voidsetNumber(name: string,value: number): void/** Set an i64 value from a `bigint`; throws if it doesn't fit in i64. */setBigInt(name: string,value: bigint): void/** Delete a variable from the highest-level config file. */removeEntry(name: string): void/** Create a read-only point-in-time snapshot of this configuration. */snapshot(): Config/** List entries, optionally filtered by a glob pattern. */entries(glob?: string|undefined|null): Array<ConfigEntry>}/** * A single configuration entry: its fully-qualified name, value, and the * level (file) it was read from. */exportinterfaceConfigEntry{name: stringvalue: stringlevel: ConfigLevel}/** * The priority level a configuration entry or file applies to. Higher levels * take precedence; `Local` (the repository's own `.git/config`) is where * `set_*`/`remove_entry` write by default. */exportdeclareconstenumConfigLevel{/** System-wide on Windows, for compatibility with portable git */ProgramData=0,/** System-wide configuration file, e.g. /etc/gitconfig */System=1,/** XDG-compatible configuration file, e.g. ~/.config/git/config */Xdg=2,/** User-specific configuration, e.g. ~/.gitconfig */Global=3,/** Repository specific config, e.g. $PWD/.git/config */Local=4,/** Worktree specific configuration file, e.g. $GIT_DIR/config.worktree */Worktree=5,/** Application specific configuration file */App=6,/** Highest level available */Highest=7}/** * A git index (the staging area). Obtain one with `repo.index()`. Mutating * methods change memory only; call `write()` to persist it or `writeTree()` to * write its current state to the object database as a tree. */exportclassIndex{/** Add or update an index entry from a file on disk. */addPath(path: string): void/** Add or update entries matching files in the working directory. */addAll(pathspecs?: Array<string>|undefined|null,force?: boolean|undefined|null): void/** Update all index entries to match the working directory. */updateAll(pathspecs?: Array<string>|undefined|null): void/** Remove an index entry corresponding to a file on disk. */removePath(path: string): void/** Get the count of entries currently in the index. */size(): number/** Write the in-memory index back to disk using an atomic file lock. */write(): void/** Write the index as a tree to the object database and return its OID. */writeTree(): string}/** * A git branch — a thin wrapper around an underlying reference; the full * reference name is available via `referenceName`. */exportclassBranch{/** Name of the local or remote branch (`null` if not valid utf-8). */name(): string|null/** Whether the current local branch is pointed at by HEAD. */isHead(): boolean/** Full reference name backing this branch (e.g. `refs/heads/main`). */referenceName(): string|null/** Delete this branch reference. */delete(): void/** The configured upstream tracking branch, or `null`. */upstream(): Branch|null/** The reference backing this branch as a live `Reference`. */get(): Reference}/** An enumeration for the possible types of branches. */exportdeclareconstenumBranchType{/** A local branch not on a remote. */Local=0,/** A branch for a remote. */Remote=1}exportclassPushOptions{constructor()/** Set the callbacks to use for the push operation. */remoteCallback(callback: RemoteCallbacks): this
/** Set the proxy options to use for the push operation. */proxyOptions(options: ProxyOptions): this
/** Number of packbuilder worker threads (0 = auto-detect, default 1). */packbuilderParallelism(parallel: number): this
/** Set remote redirection settings. */followRedirects(opt: RemoteRedirect): this
/** Set extra headers for this push operation. */customHeaders(headers: Array<string>): this
/** Set "push options" to deliver to the remote. */remotePushOptions(options: Array<string>): this
}/** * Status of a single file in the working tree and/or index. * * The boolean flags mirror the `git2::Status` bits; `bits` carries the raw * value as a forward-compatible escape hatch for flags not surfaced here. */exportinterfaceFileStatus{/** Workdir-relative path. Undefined if the path is not valid UTF-8. */path?: string/** Raw `git2::Status` bits — forward-compat escape hatch. */bits: number/** Staged: a new file was added to the index. */isIndexNew: boolean/** Staged: a tracked file was modified in the index. */isIndexModified: boolean/** Staged: a tracked file was deleted from the index. */isIndexDeleted: boolean/** Staged: a tracked file was renamed in the index. */isIndexRenamed: boolean/** Staged: a tracked file changed type in the index. */isIndexTypechange: boolean/** Unstaged: an untracked file (new in the working directory). */isWtNew: boolean/** Unstaged: a tracked file was modified in the working directory. */isWtModified: boolean/** Unstaged: a tracked file was deleted from the working directory. */isWtDeleted: boolean/** Unstaged: a tracked file changed type in the working directory. */isWtTypechange: boolean/** Unstaged: a tracked file was renamed in the working directory. */isWtRenamed: boolean/** The file is ignored. */isIgnored: boolean/** The file has merge conflicts. */isConflicted: boolean}/** * Options controlling how a working-tree status scan is performed. * * Every field is optional; omitted fields fall back to the git CLI defaults * (`include_untracked` is `true`, everything else `false`). */exportinterfaceStatusOptions{/** Include untracked files in the status. Defaults to `true`. */includeUntracked?: boolean/** Include ignored files in the status. Defaults to `false`. */includeIgnored?: boolean/** Include unmodified files in the status. Defaults to `false`. */includeUnmodified?: boolean/** Skip submodules. Defaults to `false`. */excludeSubmodules?: boolean/** * Recurse into untracked directories instead of reporting the directory * itself. Defaults to `false`. */recurseUntrackedDirs?: boolean/** Detect renames between the HEAD tree and the index. Defaults to `false`. */renamesHeadToIndex?: boolean/** Detect renames between the index and the working directory. Defaults to `false`. */renamesIndexToWorkdir?: boolean/** Restrict the scan to the given pathspecs. */pathspec?: Array<string>}/** * A single blame hunk: a contiguous run of lines attributed to one commit. * * All identity fields are copied out of the borrowed `git2::BlameHunk` so the * value can safely outlive the underlying `git2::Blame`. */exportinterfaceBlameHunk{/** Number of lines covered by this hunk. */linesInHunk: number/** 40-char lowercase hex OID of the commit where these lines were last changed. */finalCommitId: string/** Line number where this hunk begins in the final file (1-based). */finalStartLine: number/** Author name of the final commit. Undefined if absent or not valid UTF-8. */finalAuthorName?: string/** Author email of the final commit. Undefined if absent or not valid UTF-8. */finalAuthorEmail?: string/** Author time of the final commit, as a `Date`. The Unix epoch if no signature. */finalTime: Date/** 40-char lowercase hex OID of the commit where this hunk was found. */origCommitId: string/** Line number where this hunk begins in the original file (1-based). */origStartLine: number/** Path to the file where this hunk originated. Undefined if not valid UTF-8. */origPath?: string/** Whether this hunk was tracked to a boundary commit (root or `oldest_commit`). */isBoundary: boolean}/** * Options controlling how a blame is computed. * * Every field is optional; omitted fields fall back to libgit2's defaults * (no copy tracking, the whole file, starting from the current HEAD). */exportinterfaceBlameOptions{/** * Track lines that have moved within a file. Defaults to `false`. * * Note: libgit2 1.9.4 does not implement blame copy/move tracking, so setting * this flag has no effect today (accepted for forward-compat; effectively a no-op). */trackCopiesSameFile?: boolean/** * Track lines that have moved across files in the same commit. Defaults to `false`. * * Note: libgit2 1.9.4 does not implement blame copy/move tracking, so setting * this flag has no effect today (accepted for forward-compat; effectively a no-op). */trackCopiesSameCommitMoves?: boolean/** 40-char hex OID of the newest commit to consider (the blame starts here). */newestCommit?: string/** 40-char hex OID of the oldest commit to consider (a boundary). */oldestCommit?: string/** Restrict the search to first-parent history only. Defaults to `false`. */firstParent?: boolean/** Map names/emails through the repository's mailmap. Defaults to `false`. */useMailmap?: boolean/** Ignore whitespace differences. Defaults to `false`. */ignoreWhitespace?: boolean/** The first line in the file to blame (1-based). */minLine?: number/** The last line in the file to blame (1-based). */maxLine?: number}/** * Options controlling how a checkout writes files into the working directory. * * The default is a **safe** checkout (matching `git checkout`): files with * local modifications are left untouched. Set `force` to overwrite them, which * can discard uncommitted changes — use it deliberately. */exportinterfaceCheckoutOptions{/** * Force the checkout, overwriting any local changes in the working tree. * Defaults to a safe checkout when omitted or `false`. */force?: boolean/** * Recreate files that are missing from the working tree even in a safe * checkout. */recreateMissing?: boolean/** Allow the checkout to write files that conflict with the working tree. */allowConflicts?: boolean/** * Restrict the checkout to these pathspecs. When omitted, all paths are * checked out. */paths?: Array<string>/** * Write the checked-out files into this directory instead of the * repository's working directory. */targetDir?: string}dispose() — and its alias free() — eagerly releases the native git2 handle
without waiting for garbage collection; both are idempotent. After disposal every
throwing method throws "Repository has been disposed" (the Option-returning
lookups return null instead), and every handle derived from the repo (Remote,
Tree, Commit, Reference, RevWalk, …) throws the same error on use —
whether it is the receiver or passed as an argument to another method
(machine-enforced, not merely documented). Disposal does NOT cancel
already-scheduled *Async operations: they reopen the repo on their own worker
thread and run to completion. To cancel a pending async op, pass an AbortSignal
to the *Async method rather than relying on dispose().
import{Repository}from'@napi-rs/simple-git'constrepo=newRepository('/path/to/repo')// Open an existed repoconstheadReference=repo.head()headReference.shorthand()// 'main'headReference.name()// 'refs/heads/main'headReference.target()// 7a1256e2f847f395219980bc06c6dadf0148f18d/** An enumeration of all possible kinds of references. */exportconstenumReferenceType{/** A reference which points at an object id. */Direct=0,/** A reference which points at another reference. */Symbolic=1,Unknown=2}exportclassReference{/** * Ensure the reference name is well-formed. * * Validation is performed as if [`ReferenceFormat::ALLOW_ONELEVEL`] * was given to [`Reference.normalize_name`]. No normalization is * performed, however. * * ```ts * import { Reference } from '@napi-rs/simple-git' * * console.assert(Reference.isValidName("HEAD")); * console.assert(Reference.isValidName("refs/heads/main")); * * // But: * console.assert(!Reference.isValidName("main")); * console.assert(!Reference.isValidName("refs/heads/*")); * console.assert(!Reference.isValidName("foo//bar")); * ``` */staticisValidName(name: string): boolean/** Check if a reference is a local branch. */isBranch(): boolean/** Check if a reference is a note. */isNote(): boolean/** Check if a reference is a remote tracking branch */isRemote(): boolean/** Check if a reference is a tag */isTag(): booleankind(): ReferenceType/** * Get the full name of a reference. * * Returns `None` if the name is not valid utf-8. */name(): string|null/** * Get the full shorthand of a reference. * * This will transform the reference name into a name "human-readable" * version. If no shortname is appropriate, it will return the full name. * * Returns `None` if the shorthand is not valid utf-8. */shorthand(): string|null/** * Get the OID pointed to by a direct reference. * * Only available if the reference is direct (i.e. an object id reference, * not a symbolic one). */target(): string|null/** * Return the peeled OID target of this reference. * * This peeled OID only applies to direct references that point to a hard * Tag object: it is the result of peeling such Tag. */targetPeel(): string|null/** * Get full name to the reference pointed to by a symbolic reference. * * May return `None` if the reference is either not symbolic or not a * valid utf-8 string. */symbolicTarget(): string|null}Every error thrown by this library — from a synchronous method or from a rejected
*Async promise — is a standard Error carrying a code string property drawn
from a fixed union of tokens. The tokens are exported as the GitErrorCode
const enum, and any caught value can be narrowed to a coded error with the
isGitError(e) type guard:
import{isGitError,GitErrorCode}from'@napi-rs/simple-git'try{// …some git operation…}catch(e){if(isGitError(e)&&e.code===GitErrorCode.NotFound){// handle the missing object/reference/config entry}}isGitError(e) returns true only when e is a genuine Error object whose
code is a real member of the GitErrorCode enum; it narrows e to
Error & { code: GitErrorCode } in TypeScript. The Error check is the native
Node-API napi_is_error test (not a JS-level instanceof), so it recognizes
cross-realm and subclassed errors while rejecting look-alike proxies and plain
objects. Membership is validated against the enum (the single source of truth),
so a non-git Error that merely exposes some other string .code (e.g. Node's
ENOENT) returns false. The guard is total — it never throws for any input,
even a hostile value with a throwing proxy trap or Symbol.hasInstance, or an
Error whose code is a throwing getter (all return false) — so it is always
safe to call inside a catch.
The 29 members of GitErrorCode are the 28 libgit2 error classes plus one
napi-level token:
| Token | Meaning |
|---|---|
GenericError | Unclassified catch-all. Any unmapped or future libgit2 code collapses here — treat it as "something went wrong" rather than a specific class. |
NotFound | A requested object/reference/config entry does not exist. |
Exists | The object already exists and cannot be overwritten. |
Ambiguous | A short OID or name matched more than one object. |
BufSize | An output buffer was too small. |
User | A user-supplied callback returned an error. |
BareRepo | The operation is not allowed on a bare repository. |
UnbornBranch | HEAD points at a branch with no commits yet. |
Unmerged | The index contains unmerged entries. |
NotFastForward | A reference update was rejected because it was not fast-forward. |
InvalidSpec | A refspec/name is not well-formed. |
Conflict | A checkout/merge conflict prevents the operation. |
Locked | The resource is locked by another operation. |
Modified | The reference/object was modified concurrently. |
Auth | Authentication failed. |
Certificate | The server TLS certificate was rejected. |
Applied | The patch/commit was already applied. |
Peel | An object could not be peeled to the requested type. |
Eof | Unexpected end of file/stream. |
Invalid | An invalid operation or input for the current state. |
Uncommitted | Uncommitted changes prevent the operation. |
Directory | The operation is not valid on a directory. |
MergeConflict | A merge produced conflicts. |
HashsumMismatch | A hash verification failed. |
IndexDirty | The index has unsaved changes. |
ApplyFail | A patch failed to apply. |
Owner | The repository is owned by an unexpected user. |
Timeout | The operation timed out. |
InvalidArg | napi-level argument validation / API misuse (e.g. an option object reused across two *Async calls, or RemoteCallbacks passed to fetchAsync/pushAsync). Not a libgit2 class. |
Synchronous methods and *Async promise rejections expose the same
GitErrorCode token set, so you can branch on it either way:
import{isGitError,GitErrorCode}from'@napi-rs/simple-git'// Synchronoustry{repo.findRemote('does-not-exist')}catch(e){if(isGitError(e)&&e.code===GitErrorCode.NotFound){// handle the missing remote}}// Asynchronousawaitrepo.getFileLatestModifiedDateAsync('build.rs').catch((e)=>{if(isGitError(e)&&e.code===GitErrorCode.GenericError){// unclassified failure}})Cancellation is different.
*Asyncmethods that accept anAbortSignalreject an aborted call with napi's ownAbortError, whose.code === "Cancelled". That is napi's runtime cancellation code — it is not aGitErrorCodemember (it comes from napi, not the git layer), because cancellation bypasses the task's own reject hook. Because"Cancelled"is not aGitErrorCodemember,isGitErrorreturnsfalsefor it. Detect an aborted call separately — via theAbortSignal'saborted/abortevent or by checkinge.code === "Cancelled"— rather than throughisGitError.
Compared with the exec function, which gets the file's latest modified date by spawning a child process. Getting the latest modified date from the file 1000 times:
Child process took 1.9s
@napi-rs/simple-git took 65ms