@@ -2,6 +2,7 @@ use anyhow::Result;
22use async_trait:: async_trait;
33use serde:: { Deserialize , Serialize } ;
44use serde_json:: json;
5+ use std:: collections:: HashMap ;
56use std:: path:: PathBuf ;
67use std:: sync:: Arc ;
78
@@ -612,19 +613,34 @@ impl ReviewTool for GitBlameTool {
612613
613614 // git2::Repository is not Send, so we must open it inside spawn_blocking
614615 let result = tokio:: task:: spawn_blocking ( move || -> Result < String > {
615- let repo = git2:: Repository :: open ( & repo_path) ?;
616- let blame = match repo. blame_file ( std:: path:: Path :: new ( & file_path_owned) , None ) {
617- Ok ( b) => b,
618- Err ( e) => return Ok ( format ! ( "Error: could not blame file: {}" , e) ) ,
619- } ;
620-
621- // Read file to count lines
616+ // Read file first so we can validate and clamp the requested range.
622617 let content = std:: fs:: read_to_string ( repo_path. join ( & file_path_owned) ) ?;
623618 let line_count = content. lines ( ) . count ( ) ;
624619
620+ if line_count == 0 {
621+ return Ok ( "No blame data available for the specified range." . to_string ( ) ) ;
622+ }
623+
625624 let start = start_line. unwrap_or ( 1 ) . max ( 1 ) ;
626625 let end = end_line. unwrap_or ( line_count) . min ( line_count) ;
626+ if start > end {
627+ return Ok ( "No blame data available for the specified range." . to_string ( ) ) ;
628+ }
629+
630+ let repo = git2:: Repository :: open ( & repo_path) ?;
631+ let mut blame_options = git2:: BlameOptions :: new ( ) ;
632+ blame_options. min_line ( start) ;
633+ blame_options. max_line ( end) ;
634+
635+ let blame = match repo. blame_file (
636+ std:: path:: Path :: new ( & file_path_owned) ,
637+ Some ( & mut blame_options) ,
638+ ) {
639+ Ok ( b) => b,
640+ Err ( e) => return Ok ( format ! ( "Error: could not blame file: {}" , e) ) ,
641+ } ;
627642
643+ let mut commit_message_cache: HashMap < git2:: Oid , String > = HashMap :: new ( ) ;
628644 let mut output = String :: new ( ) ;
629645 for line_num in start..=end {
630646 if let Some ( hunk) = blame. get_line ( line_num) {
@@ -639,14 +655,18 @@ impl ReviewTool for GitBlameTool {
639655 . unwrap_or_else ( || "unknown" . to_string ( ) ) ;
640656
641657 // Get commit message (first line only)
642- let message = repo
643- . find_commit ( oid)
644- . ok ( )
645- . and_then ( |c| {
646- c. message ( )
647- . map ( |m| m. lines ( ) . next ( ) . unwrap_or ( "" ) . to_string ( ) )
658+ let message = commit_message_cache
659+ . entry ( oid)
660+ . or_insert_with ( || {
661+ repo. find_commit ( oid)
662+ . ok ( )
663+ . and_then ( |c| {
664+ c. message ( )
665+ . map ( |m| m. lines ( ) . next ( ) . unwrap_or ( "" ) . to_string ( ) )
666+ } )
667+ . unwrap_or_default ( )
648668 } )
649- . unwrap_or_default ( ) ;
669+ . clone ( ) ;
650670
651671 output. push_str ( & format ! (
652672 "L{}: {} ({}) [{}] {}\n " ,
0 commit comments