@@ -259,9 +259,37 @@ impl DiffParser {
259259 }
260260
261261 fn extract_file_path ( line : & str ) -> Result < String > {
262+ let re = regex:: Regex :: new ( r#"^diff --git (?:"a/(.*?)"|a/(\S+)) (?:"b/(.*?)"|b/(\S+))"# ) ?;
263+ if let Some ( caps) = re. captures ( line) {
264+ let a_path = caps
265+ . get ( 1 )
266+ . or_else ( || caps. get ( 2 ) )
267+ . map ( |m| m. as_str ( ) )
268+ . unwrap_or ( "" ) ;
269+ let b_path = caps
270+ . get ( 3 )
271+ . or_else ( || caps. get ( 4 ) )
272+ . map ( |m| m. as_str ( ) )
273+ . unwrap_or ( "" ) ;
274+
275+ let chosen = if !b_path. is_empty ( ) && b_path != "/dev/null" {
276+ b_path
277+ } else {
278+ a_path
279+ } ;
280+ return Ok ( chosen. to_string ( ) ) ;
281+ }
282+
262283 let parts: Vec < & str > = line. split_whitespace ( ) . collect ( ) ;
263284 if parts. len ( ) >= 4 {
264- Ok ( parts[ 2 ] . trim_start_matches ( "a/" ) . to_string ( ) )
285+ let a_path = parts[ 2 ] . trim_start_matches ( "a/" ) ;
286+ let b_path = parts[ 3 ] . trim_start_matches ( "b/" ) ;
287+ let chosen = if b_path != "/dev/null" {
288+ b_path
289+ } else {
290+ a_path
291+ } ;
292+ Ok ( chosen. to_string ( ) )
265293 } else {
266294 anyhow:: bail!( "Invalid diff header: {}" , line)
267295 }
@@ -272,7 +300,15 @@ impl DiffParser {
272300 . strip_prefix ( prefix)
273301 . ok_or_else ( || anyhow:: anyhow!( "Invalid file header: {}" , line) ) ?
274302 . trim ( ) ;
275- let path = raw. split_whitespace ( ) . next ( ) . unwrap_or ( raw) ;
303+ let path = if let Some ( stripped) = raw. strip_prefix ( '"' ) {
304+ if let Some ( end) = stripped. find ( '"' ) {
305+ & stripped[ ..end]
306+ } else {
307+ stripped
308+ }
309+ } else {
310+ raw. split_whitespace ( ) . next ( ) . unwrap_or ( raw)
311+ } ;
276312 Ok ( path
277313 . trim_start_matches ( "a/" )
278314 . trim_start_matches ( "b/" )
@@ -295,6 +331,10 @@ impl DiffParser {
295331 && !lines[ * i] . starts_with ( "+++ " )
296332 {
297333 let line = lines[ * i] ;
334+ if line. starts_with ( "\\ No newline at end of file" ) {
335+ * i += 1 ;
336+ continue ;
337+ }
298338 if line. is_empty ( ) {
299339 * i += 1 ;
300340 continue ;
@@ -400,4 +440,37 @@ mod tests {
400440 assert_eq ! ( diffs[ 0 ] . file_path, PathBuf :: from( "foo.txt" ) ) ;
401441 assert_eq ! ( diffs[ 0 ] . hunks. len( ) , 1 ) ;
402442 }
443+
444+ #[ test]
445+ fn test_parse_diff_header_with_spaces ( ) {
446+ let diff_text = "\
447+ diff --git \" a/foo bar.txt\" \" b/foo bar.txt\" \n \
448+ index 83db48f..f735c20 100644\n \
449+ --- \" a/foo bar.txt\" \n \
450+ +++ \" b/foo bar.txt\" \n \
451+ @@ -1,1 +1,1 @@\n \
452+ -hello\n \
453+ +world\n ";
454+
455+ let diffs = DiffParser :: parse_unified_diff ( diff_text) . unwrap ( ) ;
456+ assert_eq ! ( diffs. len( ) , 1 ) ;
457+ assert_eq ! ( diffs[ 0 ] . file_path, PathBuf :: from( "foo bar.txt" ) ) ;
458+ }
459+
460+ #[ test]
461+ fn test_parse_no_newline_marker ( ) {
462+ let diff_text = "\
463+ diff --git a/foo.txt b/foo.txt\n \
464+ index 83db48f..f735c20 100644\n \
465+ --- a/foo.txt\n \
466+ +++ b/foo.txt\n \
467+ @@ -1,1 +1,1 @@\n \
468+ -hello\n \
469+ \\ No newline at end of file\n \
470+ +world\n ";
471+
472+ let diffs = DiffParser :: parse_unified_diff ( diff_text) . unwrap ( ) ;
473+ assert_eq ! ( diffs. len( ) , 1 ) ;
474+ assert_eq ! ( diffs[ 0 ] . hunks. len( ) , 1 ) ;
475+ }
403476}
0 commit comments