Package Exports
- @rehearse/git-core
Readme
@rehearse/git-core
Pure-TypeScript implementation of the git protocol. No native dependencies. Runs on Cloudflare Workers, Node, Bun, Deno, and the browser.
A complete-enough git for in-process work: parse and serialize objects, read and write packfiles, speak the smart-HTTP wire protocol, do diffs and three-way merges, manage refs. ~2.7k lines of source, ~2.9k lines of tests. 162 tests passing across 8 suites.
Standalone library — install it directly when you need git inside a
sandboxed JS runtime. Ships alongside @rehearse/runner
as the foundation for future in-process checkout (the runner currently
no-ops actions/checkout for local execution since cwd is already
the repo, and uses native git on the Pro VM).
Install
npm install @rehearse/git-coreSingle runtime dependency: pako for
zlib (the git wire format and packfile encoding rely on it). Pure JS, no
node-gyp.
What's inside
| Module | Purpose |
|---|---|
objects |
Blob / tree / commit / tag parse + serialize. SHA-1 via Web Crypto, zlib via pako. |
packfile |
Packfile parser, REF_DELTA + OFS_DELTA decoder, packfile writer with delta-base search. |
protocol |
Smart-HTTP wire — pkt-line / flush / delim framing, capability advertisement, sideband-64k. |
client |
Smart-HTTP primitives: ref discovery, upload-pack request building, packfile fetch + sideband demux. |
diff |
Myers O(ND) diff with V-array offsetting and trace backtracking. Unified-diff formatter. |
merge |
diff3-style three-way merge built on top of myersDiff, with conflict-marker emission. |
refs |
Symbolic-ref resolution with cycle detection, packed-refs file parser. |
Quickstart — build a commit in memory
import {
hashObject,
serializeObject,
serializeTreeContent,
generatePackfile,
} from '@rehearse/git-core';
// 1. Hash + serialize a blob.
const blobContent = new TextEncoder().encode('hello world\n');
const blobSha = await hashObject('blob', blobContent);
const blobObject = serializeObject({ type: 'blob', content: blobContent });
// 2. Build a tree containing the blob.
const treeContent = serializeTreeContent([
{ mode: '100644', name: 'README.md', sha: blobSha },
]);
const treeSha = await hashObject('tree', treeContent);
const treeObject = serializeObject({ type: 'tree', content: treeContent });
// 3. Build a commit pointing at the tree.
const commitContent = new TextEncoder().encode(
`tree ${treeSha}\n` +
`author Alice <a@example.com> 1714500000 +0000\n` +
`committer Alice <a@example.com> 1714500000 +0000\n` +
`\n` +
`init\n`,
);
const commitSha = await hashObject('commit', commitContent);
const commitObject = serializeObject({ type: 'commit', content: commitContent });
// 4. Pack the three objects together for transport.
const pack = await generatePackfile([
{ sha: blobSha, type: 'blob', content: blobContent },
{ sha: treeSha, type: 'tree', content: treeContent },
{ sha: commitSha, type: 'commit', content: commitContent },
]);
console.log(`packfile is ${pack.length} bytes`);Quickstart — fetch a remote ref + packfile in pure TS
import {
fetchRemoteRefs,
buildUploadPackRequest,
fetchPackfile,
extractPackfileFromSideband,
resolvePackfile,
} from '@rehearse/git-core';
const url = 'https://github.com/honojs/hono.git';
// 1. Discover refs (info/refs?service=git-upload-pack).
const refs = await fetchRemoteRefs(url);
const mainSha = refs.refs['refs/heads/main'];
// 2. Negotiate a fetch for the main ref + ancestors.
const body = buildUploadPackRequest({
wants: [mainSha],
haves: [],
depth: 1,
});
// 3. POST to git-upload-pack and get the sideband response.
const sideband = await fetchPackfile(url, body);
const { packfile } = extractPackfileFromSideband(sideband);
// 4. Resolve deltas inside the packfile to standalone objects.
const { objects } = await resolvePackfile(packfile);
console.log(`fetched ${objects.size} objects, head=${mainSha}`);(The client module exposes the lower-level primitives you compose into
clone / fetch operations; you bring your own object store — memory,
filesystem, R2, KV, Durable Object, whatever fits the runtime.)
Why pure TypeScript
- Workers-friendly. No native deps, no
child_process, no filesystem assumptions. Runs in any V8 isolate, including Cloudflare Workers and Durable Objects. - Auditable. ~2.7k lines of source, ~2.9k lines of tests. You can read the implementation in an afternoon.
- Embeddable. Use it inside a CI tool, a code review bot, a VCS plugin, an MCP server — anywhere the git daemon would be too heavy.
- Strongly typed. Every public surface has hand-written TypeScript
types, not generated
.d.tsretrofits.
Repo
Source, issues, roadmap: https://github.com/plsft/rehearse.
License
Apache 2.0.