refactor: enhance Git fallback strategy in edit processing

- Improved logging within the applyGitFallback function to provide clearer insights during the commit and cherry-pick processes.
- Streamlined error handling to ensure more informative console outputs when operations fail.
- Maintained consistent formatting and indentation for better readability and maintainability of the code.
- Ensured temporary directory cleanup is handled correctly in all scenarios, preventing potential resource leaks.
This commit is contained in:
Daniel Riccio
2025-01-14 17:00:51 -05:00
parent 3d901929c2
commit a323a1008e

View File

@@ -134,125 +134,118 @@ export function applyDMP(hunk: Hunk, content: string[], matchPosition: number):
// Git fallback strategy that works with full content // Git fallback strategy that works with full content
async function applyGitFallback(hunk: Hunk, content: string[]): Promise<EditResult> { async function applyGitFallback(hunk: Hunk, content: string[]): Promise<EditResult> {
let tmpDir: tmp.DirResult | undefined; let tmpDir: tmp.DirResult | undefined;
try { try {
// Create temporary directory tmpDir = tmp.dirSync({ unsafeCleanup: true });
tmpDir = tmp.dirSync({ unsafeCleanup: true }); const git: SimpleGit = simpleGit(tmpDir.name);
const git: SimpleGit = simpleGit(tmpDir.name);
// Initialize git repo await git.init();
await git.init(); await git.addConfig('user.name', 'Temp');
await git.addConfig('user.name', 'Temp'); await git.addConfig('user.email', 'temp@example.com');
await git.addConfig('user.email', 'temp@example.com');
const filePath = path.join(tmpDir.name, 'file.txt'); const filePath = path.join(tmpDir.name, 'file.txt');
// Build the search text (context + removals) const searchLines = hunk.changes
const searchLines = hunk.changes .filter(change => change.type === 'context' || change.type === 'remove')
.filter(change => change.type === 'context' || change.type === 'remove') .map(change => change.originalLine || (change.indent + change.content));
.map(change => change.originalLine || (change.indent + change.content));
// Build the replace text (context + additions) const replaceLines = hunk.changes
const replaceLines = hunk.changes .filter(change => change.type === 'context' || change.type === 'add')
.filter(change => change.type === 'context' || change.type === 'add') .map(change => change.originalLine || (change.indent + change.content));
.map(change => change.originalLine || (change.indent + change.content));
const searchText = searchLines.join('\n'); const searchText = searchLines.join('\n');
const replaceText = replaceLines.join('\n'); const replaceText = replaceLines.join('\n');
const originalText = content.join('\n'); const originalText = content.join('\n');
// Strategy 1: O->S->R, cherry-pick R onto O try {
try { fs.writeFileSync(filePath, originalText);
// Original commit - use full file content await git.add('file.txt');
fs.writeFileSync(filePath, originalText); const originalCommit = await git.commit('original');
await git.add('file.txt'); console.log('Strategy 1 - Original commit:', originalCommit.commit);
const originalCommit = await git.commit('original');
// Search commit - just the search text fs.writeFileSync(filePath, searchText);
fs.writeFileSync(filePath, searchText); await git.add('file.txt');
await git.add('file.txt'); const searchCommit1 = await git.commit('search');
await git.commit('search'); console.log('Strategy 1 - Search commit:', searchCommit1.commit);
// Replace commit - just the replace text fs.writeFileSync(filePath, replaceText);
fs.writeFileSync(filePath, replaceText); await git.add('file.txt');
await git.add('file.txt'); const replaceCommit = await git.commit('replace');
const replaceCommit = await git.commit('replace'); console.log('Strategy 1 - Replace commit:', replaceCommit.commit);
// Go back to original and cherry-pick console.log('Strategy 1 - Attempting checkout of:', originalCommit.commit);
await git.checkout(originalCommit.commit); await git.raw(['checkout', originalCommit.commit]);
try { try {
await git.raw(['cherry-pick', '--minimal', replaceCommit.commit]); console.log('Strategy 1 - Attempting cherry-pick of:', replaceCommit.commit);
await git.raw(['cherry-pick', '--minimal', replaceCommit.commit]);
// Read result const newText = fs.readFileSync(filePath, 'utf-8');
const newText = fs.readFileSync(filePath, 'utf-8'); const newLines = newText.split('\n');
const newLines = newText.split('\n'); return {
return { confidence: 1,
confidence: 1, result: newLines,
result: newLines, strategy: 'git-fallback'
strategy: 'git-fallback' };
}; } catch (cherryPickError) {
} catch (cherryPickError) { console.error('Strategy 1 failed with merge conflict');
console.log('Strategy 1 failed with merge conflict'); }
} } catch (error) {
} catch (error) { console.error('Strategy 1 failed:', error);
console.log('Strategy 1 failed:', error); }
}
// Strategy 2: S->R, S->O, cherry-pick R onto O try {
try { await git.init();
// Reset repo await git.addConfig('user.name', 'Temp');
await git.init(); await git.addConfig('user.email', 'temp@example.com');
await git.addConfig('user.name', 'Temp');
await git.addConfig('user.email', 'temp@example.com');
// Search commit - just the search text fs.writeFileSync(filePath, searchText);
fs.writeFileSync(filePath, searchText); await git.add('file.txt');
await git.add('file.txt'); const searchCommit = await git.commit('search');
const searchCommit = await git.commit('search'); const searchHash = searchCommit.commit.replace(/^HEAD /, '');
console.log('Strategy 2 - Search commit:', searchHash);
// Replace commit - just the replace text fs.writeFileSync(filePath, replaceText);
fs.writeFileSync(filePath, replaceText); await git.add('file.txt');
await git.add('file.txt'); const replaceCommit = await git.commit('replace');
const replaceCommit = await git.commit('replace'); const replaceHash = replaceCommit.commit.replace(/^HEAD /, '');
console.log('Strategy 2 - Replace commit:', replaceHash);
// Go back to search and create original with full file content console.log('Strategy 2 - Attempting checkout of:', searchHash);
await git.checkout(searchCommit.commit); await git.raw(['checkout', searchHash]);
fs.writeFileSync(filePath, originalText); fs.writeFileSync(filePath, originalText);
await git.add('file.txt'); await git.add('file.txt');
await git.commit('original'); const originalCommit2 = await git.commit('original');
console.log('Strategy 2 - Original commit:', originalCommit2.commit);
try { try {
// Cherry-pick replace onto original console.log('Strategy 2 - Attempting cherry-pick of:', replaceHash);
await git.raw(['cherry-pick', '--minimal', replaceCommit.commit]); await git.raw(['cherry-pick', '--minimal', replaceHash]);
// Read result const newText = fs.readFileSync(filePath, 'utf-8');
const newText = fs.readFileSync(filePath, 'utf-8'); const newLines = newText.split('\n');
const newLines = newText.split('\n'); return {
return { confidence: 1,
confidence: 1, result: newLines,
result: newLines, strategy: 'git-fallback'
strategy: 'git-fallback' };
}; } catch (cherryPickError) {
} catch (cherryPickError) { console.error('Strategy 2 failed with merge conflict');
console.log('Strategy 2 failed with merge conflict'); }
} } catch (error) {
} catch (error) { console.error('Strategy 2 failed:', error);
console.log('Strategy 2 failed:', error); }
}
// If both strategies fail, return no confidence console.error('Git fallback failed');
console.log('Git fallback failed'); return { confidence: 0, result: content, strategy: 'git-fallback' };
return { confidence: 0, result: content, strategy: 'git-fallback' }; } catch (error) {
} catch (error) { console.error('Git fallback strategy failed:', error);
console.log('Git fallback strategy failed:', error); return { confidence: 0, result: content, strategy: 'git-fallback' };
return { confidence: 0, result: content, strategy: 'git-fallback' }; } finally {
} finally { if (tmpDir) {
// Clean up temporary directory tmpDir.removeCallback();
if (tmpDir) { }
tmpDir.removeCallback(); }
}
}
} }
// Main edit function that tries strategies sequentially // Main edit function that tries strategies sequentially