Files
automaker/libs/git-utils
Test User 59bbbd43c5 feat: add Node.js version management and improve error handling
- Introduced a .nvmrc file to specify the Node.js version (22) for the project, ensuring consistent development environments.
- Enhanced error handling in the startServer function to provide clearer messages when the Node.js executable cannot be found, improving debugging experience.
- Updated package.json files across various modules to enforce Node.js version compatibility and ensure consistent dependency versions.

These changes aim to streamline development processes and enhance the application's reliability by enforcing version control and improving error reporting.
2025-12-31 18:42:33 -05:00
..

@automaker/git-utils

Git operations and utilities for AutoMaker.

Overview

This package provides git-related utilities including repository detection, status parsing, and diff generation for both tracked and untracked files.

Installation

npm install @automaker/git-utils

Exports

Repository Detection

Check if a path is a git repository.

import { isGitRepo } from '@automaker/git-utils';

const isRepo = await isGitRepo('/project/path');
if (isRepo) {
  console.log('This is a git repository');
}

Status Parsing

Parse git status output into structured data.

import { parseGitStatus } from '@automaker/git-utils';
import type { FileStatus } from '@automaker/git-utils';

const statusOutput = await execAsync('git status --porcelain');
const files: FileStatus[] = parseGitStatus(statusOutput.stdout);

files.forEach((file) => {
  console.log(`${file.statusText}: ${file.path}`);
  // Example: "Modified: src/index.ts"
  // Example: "Untracked: new-file.ts"
});

Diff Generation

Generate diffs including untracked files.

import {
  generateSyntheticDiffForNewFile,
  appendUntrackedFileDiffs,
  getGitRepositoryDiffs,
} from '@automaker/git-utils';

// Generate diff for single untracked file
const diff = await generateSyntheticDiffForNewFile('/project/path', 'src/new-file.ts');

// Get complete repository diffs (tracked + untracked)
const result = await getGitRepositoryDiffs('/project/path');
console.log(result.diff); // Combined diff string
console.log(result.files); // Array of FileStatus
console.log(result.hasChanges); // Boolean

Non-Git Directory Support

Handle non-git directories by treating all files as new.

import { listAllFilesInDirectory, generateDiffsForNonGitDirectory } from '@automaker/git-utils';

// List all files (excluding build artifacts)
const files = await listAllFilesInDirectory('/project/path');

// Generate diffs for non-git directory
const result = await generateDiffsForNonGitDirectory('/project/path');
console.log(result.diff); // Synthetic diffs for all files
console.log(result.files); // All files as "New" status

Types

FileStatus

interface FileStatus {
  status: string; // Git status code (M/A/D/R/C/U/?/!)
  path: string; // File path relative to repo root
  statusText: string; // Human-readable status
}

Status Codes

  • M - Modified
  • A - Added
  • D - Deleted
  • R - Renamed
  • C - Copied
  • U - Updated
  • ? - Untracked
  • ! - Ignored
  • - Unmodified

Status Text Examples

  • "Modified" - File has changes
  • "Added" - New file in staging
  • "Deleted" - File removed
  • "Renamed" - File renamed
  • "Untracked" - New file not in git
  • "Modified (staged), Modified (unstaged)" - Changes in both areas

Usage Example

import { isGitRepo, getGitRepositoryDiffs, parseGitStatus } from '@automaker/git-utils';

async function getProjectChanges(projectPath: string) {
  const isRepo = await isGitRepo(projectPath);

  if (!isRepo) {
    console.log('Not a git repository, analyzing all files...');
  }

  const result = await getGitRepositoryDiffs(projectPath);

  if (!result.hasChanges) {
    console.log('No changes detected');
    return;
  }

  console.log(`Found ${result.files.length} changed files:\n`);

  // Group by status
  const byStatus = result.files.reduce(
    (acc, file) => {
      acc[file.statusText] = acc[file.statusText] || [];
      acc[file.statusText].push(file.path);
      return acc;
    },
    {} as Record<string, string[]>
  );

  Object.entries(byStatus).forEach(([status, paths]) => {
    console.log(`${status}:`);
    paths.forEach((path) => console.log(`  - ${path}`));
  });

  return result.diff;
}

Features

Binary File Detection

Automatically detects binary files by extension and generates appropriate diff markers.

Supported binary extensions:

  • Images: .png, .jpg, .jpeg, .gif, .svg, etc.
  • Documents: .pdf, .doc, .docx, etc.
  • Archives: .zip, .tar, .gz, etc.
  • Media: .mp3, .mp4, .wav, etc.
  • Fonts: .ttf, .otf, .woff, etc.

Large File Handling

Files larger than 1MB show size information instead of full content.

Synthetic Diff Format

Generates unified diff format for untracked files:

diff --git a/new-file.ts b/new-file.ts
new file mode 100644
index 0000000..0000000
--- /dev/null
+++ b/new-file.ts
@@ -0,0 +1,10 @@
+export function hello() {
+  console.log('Hello');
+}

Directory Filtering

When scanning non-git directories, automatically excludes:

  • node_modules, .git, .automaker
  • Build outputs: dist, build, out, tmp, .tmp
  • Framework caches: .next, .nuxt, .cache, coverage
  • Language-specific: __pycache__ (Python), target (Rust), vendor (Go/PHP), .gradle (Gradle), .venv/venv (Python)

Error Handling

Git operations can fail for various reasons. This package provides graceful error handling patterns:

Common Error Scenarios

1. Repository Not Found

const isRepo = await isGitRepo('/path/does/not/exist');
// Returns: false (no exception thrown)

2. Not a Git Repository

const result = await getGitRepositoryDiffs('/not/a/git/repo');
// Fallback behavior: treats all files as "new"
// Returns synthetic diffs for all files in directory

3. Git Command Failures

// Permission errors, corrupted repos, or git not installed
try {
  const result = await getGitRepositoryDiffs('/project');
} catch (error) {
  // Handle errors from git commands
  // Errors are logged via @automaker/utils logger
  console.error('Git operation failed:', error);
}

4. File Read Errors

// When generating synthetic diffs for inaccessible files
const diff = await generateSyntheticDiffForNewFile('/path', 'locked-file.txt');
// Returns placeholder: "[Unable to read file content]"
// Error is logged but doesn't throw

Best Practices

  1. Check repository status first:

    const isRepo = await isGitRepo(path);
    if (!isRepo) {
      // Handle non-git case appropriately
    }
    
  2. Expect non-git directories:

    • getGitRepositoryDiffs() automatically handles both cases
    • Always returns a valid result structure
  3. Monitor logs:

    • Errors are logged with the [GitUtils] prefix
    • Check logs for permission issues or git configuration problems
  4. Handle edge cases:

    • Empty repositories (no commits yet)
    • Detached HEAD states
    • Corrupted git repositories
    • Missing git binary

Dependencies

  • @automaker/types - FileStatus type definition
  • @automaker/utils - Logger utilities

Used By

  • @automaker/server - Git routes, worktree operations, feature context