# @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

```bash
npm install @automaker/git-utils
```

## Exports

### Repository Detection

Check if a path is a git repository.

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
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

```typescript
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

```typescript
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
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**

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

**2. Not a Git Repository**

```typescript
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**

```typescript
// 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**

```typescript
// 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**:

   ```typescript
   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
