blob: c22b8518c385881f7b36ca8fc923e6930a2d7093 [file] [log] [blame]
// 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) {
throw new Error(`Failed to fetch working copy content: ${response.statusText}`);
}
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;
}
}
}