Git Utilities
Async utilities for git operations including branch management, merging with conflict detection, diffing, and repository inspection.
Branch Management
createBranch
Create and checkout a new git branch.
async function createBranch(branchName: string, cwd?: string): Promise<void>
Name of the branch to create
Working directory (defaults to process.cwd())
Throws: Error if branch creation fails.
Example:
import { createBranch } from "@longshot/core";
await createBranch("feature-new-api");
checkoutBranch
Checkout an existing branch.
async function checkoutBranch(branchName: string, cwd?: string): Promise<void>
Name of the branch to checkout
Working directory (defaults to process.cwd())
Throws: Error if checkout fails.
Example:
import { checkoutBranch } from "@longshot/core";
await checkoutBranch("main");
getCurrentBranch
Get the name of the current branch.
async function getCurrentBranch(cwd?: string): Promise<string>
Working directory (defaults to process.cwd())
Returns: Name of the current branch.
Example:
import { getCurrentBranch } from "@longshot/core";
const branch = await getCurrentBranch();
console.log(`Current branch: ${branch}`);
Merging and Rebasing
mergeBranch
Merge a source branch into a target branch with configurable strategy and conflict detection.
async function mergeBranch(
source: string,
target?: string,
strategy?: "fast-forward" | "rebase" | "merge-commit",
cwd?: string
): Promise<MergeResult>
Source branch to merge from
Target branch to merge into (defaults to current branch)
Merge strategy: fast-forward, rebase, or merge-commit (default)
Working directory (defaults to process.cwd())
Returns: MergeResult object with success status, conflict information, and details.
Example:
import { mergeBranch } from "@longshot/core";
const result = await mergeBranch("feature-branch", "main", "fast-forward");
if (result.success) {
console.log(result.message);
} else if (result.conflicted) {
console.error(`Conflict in files: ${result.conflictingFiles?.join(", ")}`);
} else {
console.error(result.message);
}
MergeResult Type
interface MergeResult {
success: boolean;
conflicted?: boolean;
message: string;
conflictingFiles?: string[];
}
Whether the merge succeeded
Whether the merge failed due to conflicts
Human-readable result message
List of files with conflicts (populated before merge is aborted)
rebaseBranch
Rebase a branch onto another branch.
async function rebaseBranch(
branchName: string,
onto: string,
cwd?: string
): Promise<RebaseResult>
Working directory (defaults to process.cwd())
Returns: RebaseResult object with success and conflict status.
Example:
import { rebaseBranch } from "@longshot/core";
const result = await rebaseBranch("feature-branch", "main");
if (!result.success) {
if (result.conflicted) {
console.error("Rebase conflict occurred");
} else {
console.error(result.message);
}
}
If a rebase fails, the operation is automatically aborted to leave the repository in a clean state.
RebaseResult Type
interface RebaseResult {
success: boolean;
conflicted: boolean;
message: string;
}
Whether the rebase succeeded
Whether the rebase failed due to conflicts
Human-readable result message
Diff and Stats
getDiffStat
Get statistics about uncommitted changes in the working directory.
async function getDiffStat(cwd?: string): Promise<DiffStat>
Working directory (defaults to process.cwd())
Returns: DiffStat object with change statistics.
Example:
import { getDiffStat } from "@longshot/core";
const stats = await getDiffStat();
console.log(`${stats.filesChanged} files changed`);
console.log(`+${stats.linesAdded} -${stats.linesRemoved}`);
DiffStat Type
interface DiffStat {
filesChanged: number;
linesAdded: number;
linesRemoved: number;
}
Number of files with changes
hasUncommittedChanges
Check if there are any uncommitted changes in the working directory.
async function hasUncommittedChanges(cwd?: string): Promise<boolean>
Working directory (defaults to process.cwd())
Returns: true if there are uncommitted changes, false otherwise.
Example:
import { hasUncommittedChanges } from "@longshot/core";
if (await hasUncommittedChanges()) {
console.log("You have uncommitted changes");
}
Repository Inspection
getRecentCommits
Get recent commit history.
async function getRecentCommits(count: number, cwd?: string): Promise<CommitInfo[]>
Number of commits to retrieve
Working directory (defaults to process.cwd())
Returns: Array of CommitInfo objects.
Example:
import { getRecentCommits } from "@longshot/core";
const commits = await getRecentCommits(10);
for (const commit of commits) {
console.log(`${commit.hash.slice(0, 7)} - ${commit.message}`);
console.log(` by ${commit.author} on ${new Date(commit.date)}`);
}
CommitInfo Type
interface CommitInfo {
hash: string;
message: string;
author: string;
date: number;
}
Commit message (first line)
Commit timestamp in Unix milliseconds
getFileTree
Get a list of all tracked files in the repository.
async function getFileTree(cwd?: string, maxDepth?: number): Promise<string[]>
Working directory (defaults to process.cwd())
Optional maximum directory depth to include
Returns: Array of file paths relative to repository root.
Example:
import { getFileTree } from "@longshot/core";
// Get all files
const allFiles = await getFileTree();
// Get only top-level and one-level-deep files
const shallowFiles = await getFileTree(undefined, 2);
console.log(`Total files: ${allFiles.length}`);
console.log(`Shallow files: ${shallowFiles.length}`);
Complete Example
import {
createBranch,
checkoutBranch,
getCurrentBranch,
mergeBranch,
getDiffStat,
hasUncommittedChanges,
getRecentCommits
} from "@longshot/core";
async function processTask() {
// Check current state
const currentBranch = await getCurrentBranch();
console.log(`Starting on branch: ${currentBranch}`);
if (await hasUncommittedChanges()) {
console.error("Please commit or stash changes first");
return;
}
// Create task branch
await createBranch("task-branch-123");
console.log("Created task branch");
// Do work...
// (make changes to files)
// Check what changed
const stats = await getDiffStat();
console.log(`Changed ${stats.filesChanged} files (+${stats.linesAdded} -${stats.linesRemoved})`);
// Commit work (using shell command, not shown)
// Merge back to main
await checkoutBranch("main");
const mergeResult = await mergeBranch("task-branch-123", "main", "fast-forward");
if (mergeResult.success) {
console.log("Successfully merged task branch");
// Show recent commits
const commits = await getRecentCommits(5);
console.log("\nRecent commits:");
for (const commit of commits) {
console.log(` ${commit.hash.slice(0, 7)} - ${commit.message}`);
}
} else if (mergeResult.conflicted) {
console.error("Merge conflicts detected:");
console.error(mergeResult.conflictingFiles?.join("\n"));
} else {
console.error(`Merge failed: ${mergeResult.message}`);
}
}
processTask().catch(console.error);
Merge Strategies
Fast-Forward
Performs a fast-forward merge with --ff-only. Fails if fast-forward is not possible.
await mergeBranch("feature", "main", "fast-forward");
Best for linear history. Fails if the target branch has diverged from the source.
Rebase
Rebases the source branch onto the target, then performs a fast-forward merge.
await mergeBranch("feature", "main", "rebase");
Creates a linear history by replaying commits. Uses a temporary branch internally to avoid polluting the branch namespace.
Merge Commit
Creates a merge commit with --no-ff.
await mergeBranch("feature", "main", "merge-commit");
// or simply:
await mergeBranch("feature", "main");
Default strategy. Preserves the full branch history with an explicit merge commit.
Conflict Handling
All merge and rebase operations automatically detect conflicts and abort the operation to leave the repository in a clean state.
const result = await mergeBranch("feature", "main");
if (result.conflicted) {
console.log("Conflicts detected in:");
for (const file of result.conflictingFiles || []) {
console.log(` - ${file}`);
}
// Repository is automatically returned to pre-merge state
}
When conflicts occur:
- The merge/rebase is automatically aborted
- Conflicting files are captured before abort
- Repository is returned to a clean state
- The
conflictingFiles array contains all files with conflicts