Skip to main content
wit can be used as a library in your Node.js applications, giving you programmatic access to all version control operations.

Installation

npm install wit

Quick Start

// Import from the wit package
import { Repository } from 'wit';

// Initialize a new repository
const repo = Repository.init('/path/to/project');

// Or open an existing one
const repo = Repository.open('/path/to/existing');

// Add files
repo.add('src/index.ts');
repo.add('.');  // Add all

// Commit
const hash = repo.commit('Initial commit');

// Check status
const status = repo.status();
console.log(status.staged);
console.log(status.modified);
console.log(status.untracked);

// View log
const commits = repo.log({ limit: 10 });

Core Classes

Module Structure

import {
  // Core
  Repository,
  ObjectStore,
  Refs,
  
  // Search
  SearchEngine,
  
  // Types
  Commit,
  Tree,
  Blob,
  Status,
  DiffResult,
} from 'wit';

Common Patterns

Error Handling

import { Repository, WitError, NotFoundError } from 'wit';

try {
  const repo = Repository.open('/path/to/repo');
  const commit = repo.getCommit('abc123');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error('Commit not found');
  } else if (error instanceof WitError) {
    console.error('wit error:', error.message);
  } else {
    throw error;
  }
}

Async Operations

Most operations are synchronous, but some (like large file operations) are async:
// Sync
const hash = repo.commit('Message');

// Async
const results = await repo.search('query');

Working with Commits

// Get a commit
const commit = repo.getCommit('a1b2c3d4');
console.log(commit.message);
console.log(commit.author);
console.log(commit.timestamp);

// Get parent
const parent = repo.getCommit(commit.parentHash);

// Get tree
const tree = repo.getTree(commit.treeHash);
for (const entry of tree.entries) {
  console.log(entry.name, entry.mode, entry.hash);
}

Working with Diffs

// Diff working tree vs index
const unstaged = repo.diff();

// Diff index vs HEAD
const staged = repo.diff({ staged: true });

// Diff between commits
const changes = repo.diff({ 
  from: 'abc123', 
  to: 'def456' 
});

// Process diff
for (const file of changes.files) {
  console.log(file.path, file.status);
  for (const hunk of file.hunks) {
    console.log(hunk.header);
  }
}

Branching

// List branches
const branches = repo.branches();

// Create branch
repo.createBranch('feature');

// Switch branch
repo.switch('feature');

// Get current branch
const current = repo.currentBranch();

Type Definitions

Status

interface Status {
  branch: string;
  staged: FileStatus[];
  modified: FileStatus[];
  untracked: string[];
  ahead: number;
  behind: number;
}

interface FileStatus {
  path: string;
  status: 'added' | 'modified' | 'deleted' | 'renamed';
  oldPath?: string;  // For renames
}

Commit

interface Commit {
  hash: string;
  message: string;
  author: string;
  email: string;
  timestamp: Date;
  parentHash: string | null;
  treeHash: string;
}

DiffResult

interface DiffResult {
  files: FileDiff[];
}

interface FileDiff {
  path: string;
  status: 'added' | 'modified' | 'deleted';
  hunks: DiffHunk[];
}

interface DiffHunk {
  header: string;
  lines: DiffLine[];
}

interface DiffLine {
  type: 'context' | 'add' | 'remove';
  content: string;
  oldLine?: number;
  newLine?: number;
}

Example: Build Tool Integration

import { Repository } from 'wit';

async function buildIfChanged(paths: string[]) {
  const repo = Repository.open('.');
  const status = repo.status();
  
  // Check if any of our paths changed
  const changedFiles = [
    ...status.staged.map(f => f.path),
    ...status.modified.map(f => f.path),
  ];
  
  const needsBuild = paths.some(p => 
    changedFiles.some(f => f.startsWith(p))
  );
  
  if (needsBuild) {
    console.log('Changes detected, building...');
    await runBuild();
  } else {
    console.log('No changes in watched paths');
  }
}

buildIfChanged(['src/', 'lib/']);

Example: Commit Hook

import { Repository } from 'wit';

function preCommitHook() {
  const repo = Repository.open('.');
  const status = repo.status();
  
  // Check for forbidden patterns
  for (const file of status.staged) {
    const content = repo.getFileContent(file.path);
    if (content.includes('console.log')) {
      console.error(`Found console.log in ${file.path}`);
      process.exit(1);
    }
  }
  
  console.log('Pre-commit checks passed');
  process.exit(0);
}

preCommitHook();

tRPC API

wit provides a tRPC API for web and programmatic access. See the individual API reference pages for details:

Next Steps