Task attempt 19940407-2ae9-4850-b151-834c900e23e3 - Final changes
This commit is contained in:
@@ -1,11 +1,10 @@
|
|||||||
use anyhow::anyhow;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use git2::build::CheckoutBuilder;
|
use git2::build::CheckoutBuilder;
|
||||||
use git2::{Error as GitError, MergeOptions, Oid, RebaseOptions, Repository};
|
use git2::{Error as GitError, MergeOptions, Oid, RebaseOptions, Repository};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, SqlitePool, Type};
|
use sqlx::{FromRow, SqlitePool, Type};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tracing::{debug, error, info};
|
use tracing::error;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -690,33 +689,16 @@ impl TaskAttempt {
|
|||||||
String::new() // File was deleted
|
String::new() // File was deleted
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate diff chunks using dissimilar
|
// Generate line-based diff chunks
|
||||||
if old_content != new_content {
|
if old_content != new_content {
|
||||||
let chunks = dissimilar::diff(&old_content, &new_content);
|
let diff_chunks = Self::generate_line_based_diff(&old_content, &new_content);
|
||||||
let mut diff_chunks = Vec::new();
|
|
||||||
|
|
||||||
for chunk in chunks {
|
if !diff_chunks.is_empty() {
|
||||||
let diff_chunk = match chunk {
|
files.push(FileDiff {
|
||||||
dissimilar::Chunk::Equal(text) => DiffChunk {
|
path: path_str.to_string(),
|
||||||
chunk_type: DiffChunkType::Equal,
|
chunks: diff_chunks,
|
||||||
content: text.to_string(),
|
});
|
||||||
},
|
|
||||||
dissimilar::Chunk::Delete(text) => DiffChunk {
|
|
||||||
chunk_type: DiffChunkType::Delete,
|
|
||||||
content: text.to_string(),
|
|
||||||
},
|
|
||||||
dissimilar::Chunk::Insert(text) => DiffChunk {
|
|
||||||
chunk_type: DiffChunkType::Insert,
|
|
||||||
content: text.to_string(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
diff_chunks.push(diff_chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push(FileDiff {
|
|
||||||
path: path_str.to_string(),
|
|
||||||
chunks: diff_chunks,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true // Continue processing
|
true // Continue processing
|
||||||
@@ -729,6 +711,97 @@ impl TaskAttempt {
|
|||||||
Ok(WorktreeDiff { files })
|
Ok(WorktreeDiff { files })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate line-based diff chunks for better display
|
||||||
|
pub fn generate_line_based_diff(old_content: &str, new_content: &str) -> Vec<DiffChunk> {
|
||||||
|
let old_lines: Vec<&str> = old_content.lines().collect();
|
||||||
|
let new_lines: Vec<&str> = new_content.lines().collect();
|
||||||
|
let mut chunks = Vec::new();
|
||||||
|
|
||||||
|
// Use a simple line-by-line comparison algorithm
|
||||||
|
let mut old_idx = 0;
|
||||||
|
let mut new_idx = 0;
|
||||||
|
|
||||||
|
while old_idx < old_lines.len() || new_idx < new_lines.len() {
|
||||||
|
if old_idx < old_lines.len() && new_idx < new_lines.len() {
|
||||||
|
let old_line = old_lines[old_idx];
|
||||||
|
let new_line = new_lines[new_idx];
|
||||||
|
|
||||||
|
if old_line == new_line {
|
||||||
|
// Lines are identical
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Equal,
|
||||||
|
content: format!("{}\n", old_line),
|
||||||
|
});
|
||||||
|
old_idx += 1;
|
||||||
|
new_idx += 1;
|
||||||
|
} else {
|
||||||
|
// Lines are different - look ahead to see if this is a modification, insertion, or deletion
|
||||||
|
let mut found_match = false;
|
||||||
|
|
||||||
|
// Check if the new line appears later in old lines (deletion)
|
||||||
|
for look_ahead in (old_idx + 1)..std::cmp::min(old_idx + 5, old_lines.len()) {
|
||||||
|
if old_lines[look_ahead] == new_line {
|
||||||
|
// Found the new line later in old, so old_line was deleted
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Delete,
|
||||||
|
content: format!("{}\n", old_line),
|
||||||
|
});
|
||||||
|
old_idx += 1;
|
||||||
|
found_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_match {
|
||||||
|
// Check if the old line appears later in new lines (insertion)
|
||||||
|
for look_ahead in (new_idx + 1)..std::cmp::min(new_idx + 5, new_lines.len()) {
|
||||||
|
if new_lines[look_ahead] == old_line {
|
||||||
|
// Found the old line later in new, so new_line was inserted
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Insert,
|
||||||
|
content: format!("{}\n", new_line),
|
||||||
|
});
|
||||||
|
new_idx += 1;
|
||||||
|
found_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_match {
|
||||||
|
// Lines are different - treat as modification (delete old, insert new)
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Delete,
|
||||||
|
content: format!("{}\n", old_line),
|
||||||
|
});
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Insert,
|
||||||
|
content: format!("{}\n", new_line),
|
||||||
|
});
|
||||||
|
old_idx += 1;
|
||||||
|
new_idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if old_idx < old_lines.len() {
|
||||||
|
// Remaining old lines (deletions)
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Delete,
|
||||||
|
content: format!("{}\n", old_lines[old_idx]),
|
||||||
|
});
|
||||||
|
old_idx += 1;
|
||||||
|
} else {
|
||||||
|
// Remaining new lines (insertions)
|
||||||
|
chunks.push(DiffChunk {
|
||||||
|
chunk_type: DiffChunkType::Insert,
|
||||||
|
content: format!("{}\n", new_lines[new_idx]),
|
||||||
|
});
|
||||||
|
new_idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the branch status for this task attempt (ahead/behind main)
|
/// Get the branch status for this task attempt (ahead/behind main)
|
||||||
pub async fn get_branch_status(
|
pub async fn get_branch_status(
|
||||||
pool: &SqlitePool,
|
pool: &SqlitePool,
|
||||||
@@ -836,7 +909,7 @@ impl TaskAttempt {
|
|||||||
let mut last_oid: Option<Oid> = None;
|
let mut last_oid: Option<Oid> = None;
|
||||||
while let Some(res) = reb.next() {
|
while let Some(res) = reb.next() {
|
||||||
match res {
|
match res {
|
||||||
Ok(op) => {
|
Ok(_op) => {
|
||||||
let new_oid = reb.commit(None, &sig, None)?;
|
let new_oid = reb.commit(None, &sig, None)?;
|
||||||
last_oid = Some(new_oid);
|
last_oid = Some(new_oid);
|
||||||
}
|
}
|
||||||
@@ -863,7 +936,7 @@ impl TaskAttempt {
|
|||||||
repo.checkout_head(Some(CheckoutBuilder::new().force()))?;
|
repo.checkout_head(Some(CheckoutBuilder::new().force()))?;
|
||||||
|
|
||||||
// 🔟 final check
|
// 🔟 final check
|
||||||
let final_oid = repo.head()?.peel_to_commit()?.id();
|
let _final_oid = repo.head()?.peel_to_commit()?.id();
|
||||||
|
|
||||||
Ok(main_oid.to_string())
|
Ok(main_oid.to_string())
|
||||||
}
|
}
|
||||||
|
|||||||
51
backend/src/models/test_diff.rs
Normal file
51
backend/src/models/test_diff.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::models::task_attempt::{TaskAttempt, DiffChunkType};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_based_diff() {
|
||||||
|
let old_content = "line 1\nline 2\nline 3\n";
|
||||||
|
let new_content = "line 1\nmodified line 2\nline 3\n";
|
||||||
|
|
||||||
|
let chunks = TaskAttempt::generate_line_based_diff(old_content, new_content);
|
||||||
|
|
||||||
|
// Should have: equal, delete, insert, equal
|
||||||
|
assert_eq!(chunks.len(), 4);
|
||||||
|
|
||||||
|
// First chunk should be equal
|
||||||
|
assert_eq!(chunks[0].chunk_type, DiffChunkType::Equal);
|
||||||
|
assert_eq!(chunks[0].content, "line 1\n");
|
||||||
|
|
||||||
|
// Second chunk should be delete
|
||||||
|
assert_eq!(chunks[1].chunk_type, DiffChunkType::Delete);
|
||||||
|
assert_eq!(chunks[1].content, "line 2\n");
|
||||||
|
|
||||||
|
// Third chunk should be insert
|
||||||
|
assert_eq!(chunks[2].chunk_type, DiffChunkType::Insert);
|
||||||
|
assert_eq!(chunks[2].content, "modified line 2\n");
|
||||||
|
|
||||||
|
// Fourth chunk should be equal
|
||||||
|
assert_eq!(chunks[3].chunk_type, DiffChunkType::Equal);
|
||||||
|
assert_eq!(chunks[3].content, "line 3\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_insertion() {
|
||||||
|
let old_content = "line 1\nline 3\n";
|
||||||
|
let new_content = "line 1\nline 2\nline 3\n";
|
||||||
|
|
||||||
|
let chunks = TaskAttempt::generate_line_based_diff(old_content, new_content);
|
||||||
|
|
||||||
|
// Should have: equal, insert, equal
|
||||||
|
assert_eq!(chunks.len(), 3);
|
||||||
|
|
||||||
|
assert_eq!(chunks[0].chunk_type, DiffChunkType::Equal);
|
||||||
|
assert_eq!(chunks[0].content, "line 1\n");
|
||||||
|
|
||||||
|
assert_eq!(chunks[1].chunk_type, DiffChunkType::Insert);
|
||||||
|
assert_eq!(chunks[1].content, "line 2\n");
|
||||||
|
|
||||||
|
assert_eq!(chunks[2].chunk_type, DiffChunkType::Equal);
|
||||||
|
assert_eq!(chunks[2].content, "line 3\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
49
test_diff_logic.py
Normal file
49
test_diff_logic.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
def test_diff_logic():
|
||||||
|
"""Test the logic of our line-based diff algorithm"""
|
||||||
|
|
||||||
|
# Test case 1: Line modification
|
||||||
|
old_content = "line 1\nline 2\nline 3\n"
|
||||||
|
new_content = "line 1\nmodified line 2\nline 3\n"
|
||||||
|
|
||||||
|
old_lines = old_content.split('\n')[:-1] # Remove empty last element
|
||||||
|
new_lines = new_content.split('\n')[:-1]
|
||||||
|
|
||||||
|
print("Test 1 - Line modification:")
|
||||||
|
print(f"Old lines: {old_lines}")
|
||||||
|
print(f"New lines: {new_lines}")
|
||||||
|
|
||||||
|
# Expected chunks: Equal, Delete, Insert, Equal
|
||||||
|
expected_chunks = [
|
||||||
|
("Equal", "line 1\n"),
|
||||||
|
("Delete", "line 2\n"),
|
||||||
|
("Insert", "modified line 2\n"),
|
||||||
|
("Equal", "line 3\n")
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"Expected chunks: {expected_chunks}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Test case 2: Line insertion
|
||||||
|
old_content = "line 1\nline 3\n"
|
||||||
|
new_content = "line 1\nline 2\nline 3\n"
|
||||||
|
|
||||||
|
old_lines = old_content.split('\n')[:-1]
|
||||||
|
new_lines = new_content.split('\n')[:-1]
|
||||||
|
|
||||||
|
print("Test 2 - Line insertion:")
|
||||||
|
print(f"Old lines: {old_lines}")
|
||||||
|
print(f"New lines: {new_lines}")
|
||||||
|
|
||||||
|
# Expected chunks: Equal, Insert, Equal
|
||||||
|
expected_chunks = [
|
||||||
|
("Equal", "line 1\n"),
|
||||||
|
("Insert", "line 2\n"),
|
||||||
|
("Equal", "line 3\n")
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"Expected chunks: {expected_chunks}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_diff_logic()
|
||||||
Reference in New Issue
Block a user