| // git-data-service.ts |
| // Interface and implementation for fetching Git data |
| |
| import { DiffFile, GitLogEntry } from "../types"; |
| |
| // Re-export DiffFile as GitDiffFile |
| export type GitDiffFile = DiffFile; |
| |
| /** |
| * Interface for Git data services |
| */ |
| export interface GitDataService { |
| /** |
| * Fetches recent commit history |
| * @param initialCommit The initial commit hash to start from |
| * @returns List of commits |
| */ |
| getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]>; |
| |
| /** |
| * Fetches diff between two commits |
| * @param from Starting commit hash |
| * @param to Ending commit hash (can be empty string for unstaged changes) |
| * @returns List of changed files |
| */ |
| getDiff(from: string, to: string): Promise<GitDiffFile[]>; |
| |
| /** |
| * Fetches diff for a single commit |
| * @param commit Commit hash |
| * @returns List of changed files |
| */ |
| getCommitDiff(commit: string): Promise<GitDiffFile[]>; |
| |
| /** |
| * Fetches file content from git using a file hash |
| * @param fileHash Git blob hash of the file to fetch |
| * @returns File content as string |
| */ |
| getFileContent(fileHash: string): Promise<string>; |
| |
| /** |
| * Gets file content from the current working directory |
| * @param filePath Path to the file within the repository |
| * @returns File content as string |
| */ |
| getWorkingCopyContent(filePath: string): Promise<string>; |
| |
| /** |
| * Saves file content to the working directory |
| * @param filePath Path to the file within the repository |
| * @param content New content to save to the file |
| */ |
| saveFileContent(filePath: string, content: string): Promise<void>; |
| |
| /** |
| * Gets the base commit reference (often "sketch-base") |
| * @returns Base commit reference |
| */ |
| getBaseCommitRef(): Promise<string>; |
| |
| /** |
| * Fetches unstaged changes (diff between a commit and working directory) |
| * @param from Starting commit hash (defaults to HEAD if not specified) |
| * @returns List of changed files |
| */ |
| getUnstagedChanges(from?: string): Promise<GitDiffFile[]>; |
| } |
| |
| /** |
| * Default implementation of GitDataService for the real application |
| */ |
| export class DefaultGitDataService implements GitDataService { |
| private baseCommitRef: string | null = null; |
| |
| async getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]> { |
| try { |
| const url = initialCommit |
| ? `git/recentlog?initialCommit=${encodeURIComponent(initialCommit)}` |
| : "git/recentlog"; |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| throw new Error( |
| `Failed to fetch commit history: ${response.statusText}`, |
| ); |
| } |
| |
| return await response.json(); |
| } catch (error) { |
| console.error("Error fetching commit history:", error); |
| throw error; |
| } |
| } |
| |
| async getDiff(from: string, to: string): Promise<GitDiffFile[]> { |
| try { |
| const url = `git/rawdiff?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`; |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| throw new Error(`Failed to fetch diff: ${response.statusText}`); |
| } |
| |
| return await response.json(); |
| } catch (error) { |
| console.error("Error fetching diff:", error); |
| throw error; |
| } |
| } |
| |
| async getCommitDiff(commit: string): Promise<GitDiffFile[]> { |
| try { |
| const url = `git/rawdiff?commit=${encodeURIComponent(commit)}`; |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| throw new Error(`Failed to fetch commit diff: ${response.statusText}`); |
| } |
| |
| return await response.json(); |
| } catch (error) { |
| console.error("Error fetching commit diff:", error); |
| throw error; |
| } |
| } |
| |
| async getFileContent(fileHash: string): Promise<string> { |
| try { |
| // If the hash is marked as a working copy (special value '000000' or empty) |
| if ( |
| fileHash === "0000000000000000000000000000000000000000" || |
| !fileHash |
| ) { |
| // This shouldn't happen, but if it does, return empty string |
| // Working copy content should be fetched through getWorkingCopyContent |
| console.warn( |
| "Invalid file hash for getFileContent, returning empty string", |
| ); |
| return ""; |
| } |
| |
| const url = `git/show?hash=${encodeURIComponent(fileHash)}`; |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| throw new Error(`Failed to fetch file content: ${response.statusText}`); |
| } |
| |
| const data = await response.json(); |
| return data.output || ""; |
| } catch (error) { |
| console.error("Error fetching file content:", error); |
| throw error; |
| } |
| } |
| |
| async getWorkingCopyContent(filePath: string): Promise<string> { |
| try { |
| const url = `git/cat?path=${encodeURIComponent(filePath)}`; |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| const errorText = await response.text(); |
| throw new Error( |
| `Failed to fetch working copy content (${response.status}): ${errorText}`, |
| ); |
| } |
| |
| const data = await response.json(); |
| return data.output || ""; |
| } catch (error) { |
| console.error("Error fetching working copy content:", error); |
| throw error; |
| } |
| } |
| |
| async saveFileContent(filePath: string, content: string): Promise<void> { |
| try { |
| const url = `git/save`; |
| const response = await fetch(url, { |
| method: "POST", |
| headers: { |
| "Content-Type": "application/json", |
| }, |
| body: JSON.stringify({ |
| path: filePath, |
| content: content, |
| }), |
| }); |
| |
| if (!response.ok) { |
| const errorText = await response.text(); |
| throw new Error( |
| `Failed to save file content: ${response.statusText} - ${errorText}`, |
| ); |
| } |
| |
| // Don't need to return the response, just ensure it was successful |
| } catch (error) { |
| console.error("Error saving file content:", error); |
| throw error; |
| } |
| } |
| |
| async getUnstagedChanges(from: string = "HEAD"): Promise<GitDiffFile[]> { |
| try { |
| // To get unstaged changes, we diff the specified commit (or HEAD) with an empty 'to' |
| return await this.getDiff(from, ""); |
| } catch (error) { |
| console.error("Error fetching unstaged changes:", error); |
| throw error; |
| } |
| } |
| |
| async getBaseCommitRef(): Promise<string> { |
| // Cache the base commit reference to avoid multiple requests |
| if (this.baseCommitRef) { |
| return this.baseCommitRef; |
| } |
| |
| try { |
| // This could be replaced with a specific endpoint call if available |
| // For now, we'll use a fixed value or try to get it from the server |
| this.baseCommitRef = "sketch-base"; |
| return this.baseCommitRef; |
| } catch (error) { |
| console.error("Error fetching base commit reference:", error); |
| throw error; |
| } |
| } |
| } |