@@ -48,6 +48,7 @@ use crate::{
4848
4949/// Enumeration of errors that can occur in repo operations. 
5050pub  mod  error { 
51+     use  crate :: Oid ; 
5152    use  std:: path:: PathBuf ; 
5253    use  thiserror:: Error ; 
5354
@@ -56,6 +57,8 @@ pub mod error {
5657    pub  enum  Repo  { 
5758        #[ error( "path not found for: {0}" ) ]  
5859        PathNotFound ( PathBuf ) , 
60+         #[ error( "blob not found for: {0}" ) ]  
61+         BlobNotFound ( Oid ) , 
5962    } 
6063} 
6164
@@ -301,6 +304,20 @@ impl Repository {
301304        Ok ( Blob :: < BlobRef < ' a > > :: new ( file. id ( ) ,  git2_blob,  last_commit) ) 
302305    } 
303306
307+     /// Retrieves the blob with `oid` in `commit`. 
308+ pub  fn  blob_at < ' a ,  C :  ToCommit > ( 
309+         & ' a  self , 
310+         commit :  C , 
311+         oid :  Oid , 
312+     )  -> Result < Blob < BlobRef < ' a > > ,  Error >  { 
313+         let  commit = commit
314+             . to_commit ( self ) 
315+             . map_err ( |e| Error :: ToCommit ( e. into ( ) ) ) ?; 
316+         let  git2_blob = self . find_blob ( oid) ?; 
317+         let  last_commit = self . find_commit_of_blob ( oid,  & commit) ?; 
318+         Ok ( Blob :: < BlobRef < ' a > > :: new ( oid,  git2_blob,  last_commit) ) 
319+     } 
320+ 
304321    /// Returns the last commit, if exists, for a `path` in the history of 
305322/// `rev`. 
306323pub  fn  last_commit < P ,  C > ( & self ,  path :  & P ,  rev :  C )  -> Result < Option < Commit > ,  Error > 
@@ -524,6 +541,53 @@ impl Repository {
524541        Ok ( diff) 
525542    } 
526543
544+     /// Returns true if the diff between `from` and `to` creates `oid`. 
545+ fn  diff_commits_has_oid ( 
546+         & self , 
547+         from :  Option < & git2:: Commit > , 
548+         to :  & git2:: Commit , 
549+         oid :  & git2:: Oid , 
550+     )  -> Result < bool ,  Error >  { 
551+         let  diff = self . diff_commits ( None ,  from,  to) ?; 
552+         for  delta in  diff. deltas ( )  { 
553+             if  & delta. new_file ( ) . id ( )  == oid { 
554+                 return  Ok ( true ) ; 
555+             } 
556+         } 
557+         Ok ( false ) 
558+     } 
559+ 
560+     /// Returns whether `oid` was created in `commit` or not. 
561+ fn  is_oid_in_commit ( & self ,  oid :  Oid ,  commit :  & git2:: Commit )  -> Result < bool ,  Error >  { 
562+         if  commit. parent_count ( )  == 0  { 
563+             return  self . diff_commits_has_oid ( None ,  commit,  oid. as_ref ( ) ) ; 
564+         } 
565+ 
566+         for  p in  commit. parents ( )  { 
567+             if  self . diff_commits_has_oid ( Some ( & p) ,  commit,  oid. as_ref ( ) ) ? { 
568+                 return  Ok ( true ) ; 
569+             } 
570+         } 
571+ 
572+         Ok ( false ) 
573+     } 
574+ 
575+     /// Returns the commit that created the blob with `oid`. 
576+ /// 
577+ /// It is assumed that `oid` exists in `head`. 
578+ fn  find_commit_of_blob ( & self ,  oid :  Oid ,  head :  & Commit )  -> Result < Commit ,  Error >  { 
579+         let  mut  revwalk = self . revwalk ( ) ?; 
580+         revwalk. push ( head. id . into ( ) ) ?; 
581+         for  commit_id in  revwalk { 
582+             let  commit_id = commit_id?; 
583+             let  git2_commit = self . inner . find_commit ( commit_id) ?; 
584+             if  self . is_oid_in_commit ( oid,  & git2_commit) ? { 
585+                 return  Ok ( Commit :: try_from ( git2_commit) ?) ; 
586+             } 
587+         } 
588+         Err ( Error :: Repo ( error:: Repo :: BlobNotFound ( oid) ) ) 
589+     } 
590+ 
527591    /// Returns a full reference name with namespace(s) included. 
528592pub ( crate )  fn  namespaced_refname < ' a > ( 
529593        & ' a  self , 
0 commit comments