Diff fixes (#101)
* Generate diffs for uncommitted changes and blank files * Fixes
This commit is contained in:
committed by
GitHub
parent
2829686a71
commit
5d256c243a
@@ -1228,7 +1228,9 @@ impl TaskAttempt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generate Git-native diff chunks
|
// Generate Git-native diff chunks
|
||||||
if old_content != new_content {
|
// Always generate diff for file changes, even if both contents are empty
|
||||||
|
// This handles cases where empty files are added or files are deleted
|
||||||
|
if old_content != new_content || delta.status() != git2::Delta::Modified {
|
||||||
match Self::generate_git_diff_chunks(
|
match Self::generate_git_diff_chunks(
|
||||||
&main_repo, &old_file, &new_file, path_str,
|
&main_repo, &old_file, &new_file, path_str,
|
||||||
) {
|
) {
|
||||||
@@ -1238,6 +1240,30 @@ impl TaskAttempt {
|
|||||||
chunks: diff_chunks,
|
chunks: diff_chunks,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// For added or deleted files without content, still show the file
|
||||||
|
Ok(_)
|
||||||
|
if delta.status() == git2::Delta::Added
|
||||||
|
|| delta.status() == git2::Delta::Deleted =>
|
||||||
|
{
|
||||||
|
files.push(FileDiff {
|
||||||
|
path: path_str.to_string(),
|
||||||
|
chunks: vec![DiffChunk {
|
||||||
|
chunk_type: if delta.status() == git2::Delta::Added {
|
||||||
|
DiffChunkType::Insert
|
||||||
|
} else {
|
||||||
|
DiffChunkType::Delete
|
||||||
|
},
|
||||||
|
content: format!(
|
||||||
|
"{} file",
|
||||||
|
if delta.status() == git2::Delta::Added {
|
||||||
|
"Added"
|
||||||
|
} else {
|
||||||
|
"Deleted"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error generating diff for {}: {:?}", path_str, e);
|
eprintln!("Error generating diff for {}: {:?}", path_str, e);
|
||||||
}
|
}
|
||||||
@@ -1310,7 +1336,9 @@ impl TaskAttempt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generate Git-native diff chunks
|
// Generate Git-native diff chunks
|
||||||
if old_content != new_content {
|
// Always generate diff for file changes, even if both contents are empty
|
||||||
|
// This handles cases where empty files are added or files are deleted
|
||||||
|
if old_content != new_content || delta.status() != git2::Delta::Modified {
|
||||||
match Self::generate_git_diff_chunks(
|
match Self::generate_git_diff_chunks(
|
||||||
&worktree_repo,
|
&worktree_repo,
|
||||||
&old_file,
|
&old_file,
|
||||||
@@ -1323,6 +1351,30 @@ impl TaskAttempt {
|
|||||||
chunks: diff_chunks,
|
chunks: diff_chunks,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// For added or deleted files without content, still show the file
|
||||||
|
Ok(_)
|
||||||
|
if delta.status() == git2::Delta::Added
|
||||||
|
|| delta.status() == git2::Delta::Deleted =>
|
||||||
|
{
|
||||||
|
files.push(FileDiff {
|
||||||
|
path: path_str.to_string(),
|
||||||
|
chunks: vec![DiffChunk {
|
||||||
|
chunk_type: if delta.status() == git2::Delta::Added {
|
||||||
|
DiffChunkType::Insert
|
||||||
|
} else {
|
||||||
|
DiffChunkType::Delete
|
||||||
|
},
|
||||||
|
content: format!(
|
||||||
|
"{} file",
|
||||||
|
if delta.status() == git2::Delta::Added {
|
||||||
|
"Added"
|
||||||
|
} else {
|
||||||
|
"Deleted"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error generating diff for {}: {:?}", path_str, e);
|
eprintln!("Error generating diff for {}: {:?}", path_str, e);
|
||||||
}
|
}
|
||||||
@@ -1469,17 +1521,136 @@ impl TaskAttempt {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// File only has unstaged changes (new file or uncommitted changes only)
|
// File only has unstaged changes (new file or uncommitted changes only)
|
||||||
match Self::generate_git_diff_chunks(worktree_repo, &old_file, &new_file, path_str) {
|
// First check if this is a new file or changed file by comparing with base
|
||||||
Ok(diff_chunks) if !diff_chunks.is_empty() => {
|
let base_content = if let Ok(base_commit) = worktree_repo.find_commit(base_oid) {
|
||||||
files.push(FileDiff {
|
if let Ok(base_tree) = base_commit.tree() {
|
||||||
path: path_str.to_string(),
|
match base_tree.get_path(std::path::Path::new(path_str)) {
|
||||||
chunks: diff_chunks,
|
Ok(entry) => {
|
||||||
});
|
if let Ok(blob) = worktree_repo.find_blob(entry.id()) {
|
||||||
|
String::from_utf8_lossy(blob.content()).to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => String::new(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
} else {
|
||||||
eprintln!("Error generating unstaged diff for {}: {:?}", path_str, e);
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the working directory content
|
||||||
|
let working_content = if delta.status() != git2::Delta::Deleted {
|
||||||
|
let file_path = std::path::Path::new(worktree_path).join(path_str);
|
||||||
|
std::fs::read_to_string(&file_path).unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create diff from base to working directory (including unstaged changes)
|
||||||
|
if base_content != working_content || delta.status() != git2::Delta::Modified {
|
||||||
|
if let Ok(patch) = git2::Patch::from_buffers(
|
||||||
|
base_content.as_bytes(),
|
||||||
|
Some(std::path::Path::new(path_str)),
|
||||||
|
working_content.as_bytes(),
|
||||||
|
Some(std::path::Path::new(path_str)),
|
||||||
|
Some(&mut git2::DiffOptions::new()),
|
||||||
|
) {
|
||||||
|
let mut chunks = Vec::new();
|
||||||
|
|
||||||
|
// Process the patch hunks
|
||||||
|
for hunk_idx in 0..patch.num_hunks() {
|
||||||
|
if let Ok((_hunk, hunk_lines)) = patch.hunk(hunk_idx) {
|
||||||
|
for line_idx in 0..hunk_lines {
|
||||||
|
if let Ok(line) = patch.line_in_hunk(hunk_idx, line_idx) {
|
||||||
|
let content =
|
||||||
|
String::from_utf8_lossy(line.content()).to_string();
|
||||||
|
let chunk_type = match line.origin() {
|
||||||
|
' ' => DiffChunkType::Equal,
|
||||||
|
'+' => DiffChunkType::Insert,
|
||||||
|
'-' => DiffChunkType::Delete,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no hunks but file status changed, add a placeholder
|
||||||
|
if chunks.is_empty() && delta.status() != git2::Delta::Modified {
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: if delta.status() == git2::Delta::Added {
|
||||||
|
DiffChunkType::Insert
|
||||||
|
} else {
|
||||||
|
DiffChunkType::Delete
|
||||||
|
},
|
||||||
|
content: format!(
|
||||||
|
"{} file",
|
||||||
|
if delta.status() == git2::Delta::Added {
|
||||||
|
"Added"
|
||||||
|
} else {
|
||||||
|
"Deleted"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chunks.is_empty() {
|
||||||
|
files.push(FileDiff {
|
||||||
|
path: path_str.to_string(),
|
||||||
|
chunks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to the original method if patch creation fails
|
||||||
|
match Self::generate_git_diff_chunks(
|
||||||
|
worktree_repo,
|
||||||
|
&old_file,
|
||||||
|
&new_file,
|
||||||
|
path_str,
|
||||||
|
) {
|
||||||
|
Ok(diff_chunks) if !diff_chunks.is_empty() => {
|
||||||
|
files.push(FileDiff {
|
||||||
|
path: path_str.to_string(),
|
||||||
|
chunks: diff_chunks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// For added or deleted files without content, still show the file
|
||||||
|
Ok(_)
|
||||||
|
if delta.status() == git2::Delta::Added
|
||||||
|
|| delta.status() == git2::Delta::Deleted =>
|
||||||
|
{
|
||||||
|
files.push(FileDiff {
|
||||||
|
path: path_str.to_string(),
|
||||||
|
chunks: vec![DiffChunk {
|
||||||
|
chunk_type: if delta.status() == git2::Delta::Added {
|
||||||
|
DiffChunkType::Insert
|
||||||
|
} else {
|
||||||
|
DiffChunkType::Delete
|
||||||
|
},
|
||||||
|
content: format!(
|
||||||
|
"{} file",
|
||||||
|
if delta.status() == git2::Delta::Added {
|
||||||
|
"Added"
|
||||||
|
} else {
|
||||||
|
"Deleted"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error generating unstaged diff for {}: {:?}", path_str, e);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -839,7 +839,7 @@ export function TaskDetailsPanel({
|
|||||||
{/* Top area - Code Changes (responsive height) */}
|
{/* Top area - Code Changes (responsive height) */}
|
||||||
{showDiffs && (
|
{showDiffs && (
|
||||||
<div
|
<div
|
||||||
className={`${areAllFilesCollapsed() ? 'h-auto' : 'max-h-[66vh]'} min-h-0 ${areAllFilesCollapsed() ? 'p-2' : 'p-4'} overflow-y-auto`}
|
className={`${areAllFilesCollapsed() ? 'h-auto' : 'max-h-[66vh]'} min-h-0 p-4 overflow-y-auto`}
|
||||||
>
|
>
|
||||||
{diffLoading ? (
|
{diffLoading ? (
|
||||||
<div className="flex items-center justify-center h-32">
|
<div className="flex items-center justify-center h-32">
|
||||||
@@ -1091,7 +1091,7 @@ export function TaskDetailsPanel({
|
|||||||
|
|
||||||
{/* Bottom area - Agent Logs (responsive height) */}
|
{/* Bottom area - Agent Logs (responsive height) */}
|
||||||
<div
|
<div
|
||||||
className={`${!showDiffs || areAllFilesCollapsed() ? 'flex-1' : 'flex-1'} min-h-0 ${showDiffs ? 'border-t' : ''} bg-muted/30`}
|
className={`${!showDiffs || areAllFilesCollapsed() ? 'flex-1' : 'flex-1'} min-h-60 ${showDiffs ? 'border-t' : ''} bg-muted/30`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={scrollContainerRef}
|
ref={scrollContainerRef}
|
||||||
|
|||||||
Reference in New Issue
Block a user