diff --git a/crates/code2prompt-core/src/git.rs b/crates/code2prompt-core/src/git.rs index 2f6971b..1b1671c 100644 --- a/crates/code2prompt-core/src/git.rs +++ b/crates/code2prompt-core/src/git.rs @@ -11,6 +11,28 @@ use log::info; use std::collections::HashSet; use std::path::{Path, PathBuf}; +/// Synthesize a diff-line sigil (`+`/`-`/` `) for added/removed/context lines. +/// +/// `git2`'s `DiffFormat::Patch` splits the sigil from the line content: +/// - `origin()` returns `'+'` / `'-'` / `' '` for added/removed/context lines +/// - `content()` returns ONLY the raw line text WITHOUT the sigil +/// - file headers (`'F'`), hunk headers (`'H'`), binary notices (`'B'`) and the +/// end-of-file markers (`'='/'>'/'<'`) already contain their full text in +/// `content()`, so they get no synthesized sigil. +/// +/// `content()` is appended unconditionally, so binary notices and the +/// `\ No newline at end of file` markers are preserved rather than dropped. +pub fn write_diff_line(buf: &mut Vec, origin: char, content: &[u8]) { + match origin { + '+' => buf.push(b'+'), + '-' => buf.push(b'-'), + ' ' => buf.push(b' '), + 'F' | 'H' | 'B' | '=' | '>' | '<' => {} // full text already in content() + _ => {} + } + buf.extend_from_slice(content); +} + /// Extract git diff for staged changes in repository. /// /// Generates diff between HEAD and index to show staged changes, with additional @@ -50,7 +72,7 @@ pub fn get_git_diff(repo_path: &Path) -> Result { let mut staged_diff_text = Vec::new(); staged_diff .print(git2::DiffFormat::Patch, |_delta, _hunk, line| { - staged_diff_text.extend_from_slice(line.content()); + write_diff_line(&mut staged_diff_text, line.origin(), line.content()); true }) .context("Failed to print staged diff")?; @@ -70,7 +92,7 @@ pub fn get_git_diff(repo_path: &Path) -> Result { let mut unstaged_diff_text = Vec::new(); unstaged_diff .print(git2::DiffFormat::Patch, |_delta, _hunk, line| { - unstaged_diff_text.extend_from_slice(line.content()); + write_diff_line(&mut unstaged_diff_text, line.origin(), line.content()); true }) .context("Failed to print unstaged diff")?; @@ -127,7 +149,7 @@ pub fn get_git_diff_between_branches( let mut diff_text = Vec::new(); diff.print(git2::DiffFormat::Patch, |_delta, _hunk, line| { - diff_text.extend_from_slice(line.content()); + write_diff_line(&mut diff_text, line.origin(), line.content()); true }) .context("Failed to print diff")?; diff --git a/crates/code2prompt-core/tests/git_test.rs b/crates/code2prompt-core/tests/git_test.rs index fa3b731..2bd4b9d 100644 --- a/crates/code2prompt-core/tests/git_test.rs +++ b/crates/code2prompt-core/tests/git_test.rs @@ -1,4 +1,6 @@ -use code2prompt_core::git::{get_git_diff, get_git_diff_between_branches, get_git_log}; +use code2prompt_core::git::{ + get_git_diff, get_git_diff_between_branches, get_git_log, write_diff_line, +}; #[cfg(test)] mod tests { @@ -385,4 +387,46 @@ mod tests { .to_string() .contains("Branch nonexistent_reference doesn't exist!")); } + + #[test] + fn write_diff_line_synthesizes_sigils_for_content_lines() { + let mut buf = Vec::new(); + write_diff_line(&mut buf, '+', b"hello\n"); + assert_eq!(buf, b"+hello\n"); + + buf.clear(); + write_diff_line(&mut buf, '-', b"world\n"); + assert_eq!(buf, b"-world\n"); + + buf.clear(); + write_diff_line(&mut buf, ' ', b"ctx\n"); + assert_eq!(buf, b" ctx\n"); + } + + #[test] + fn write_diff_line_does_not_synthesize_sigil_for_headers_and_eof_markers() { + let mut buf = Vec::new(); + write_diff_line(&mut buf, 'F', b"diff --git a/x b/x\n"); + assert_eq!(buf, b"diff --git a/x b/x\n"); + + buf.clear(); + write_diff_line(&mut buf, '=', b"\\ No newline at end of file\n"); + assert_eq!(buf, b"\\ No newline at end of file\n"); + + buf.clear(); + write_diff_line(&mut buf, 'H', b"@@ -1,3 +1,4 @@\n"); + assert_eq!(buf, b"@@ -1,3 +1,4 @@\n"); + + buf.clear(); + write_diff_line(&mut buf, '>', b"old filename\n"); + assert_eq!(buf, b"old filename\n"); + + buf.clear(); + write_diff_line(&mut buf, '<', b"new filename\n"); + assert_eq!(buf, b"new filename\n"); + + buf.clear(); + write_diff_line(&mut buf, 'B', b"Binary files a/x and b/x differ\n"); + assert_eq!(buf, b"Binary files a/x and b/x differ\n"); + } }