Spaces:
Paused
Paused
| # @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 | |