@@ -170,8 +170,12 @@ pub trait ApiHandler: Send + Sync {
170170 }
171171 }
172172
173- async fn get_tree_info ( & self , path : & Path ) -> Result < Vec < TreeBriefItem > , GitError > {
174- match self . search_tree_by_path ( path) . await ? {
173+ async fn get_tree_info (
174+ & self ,
175+ path : & Path ,
176+ refs : Option < & str > ,
177+ ) -> Result < Vec < TreeBriefItem > , GitError > {
178+ match self . search_tree_by_path_with_refs ( path, refs) . await ? {
175179 Some ( tree) => {
176180 let items = tree
177181 . tree_items
@@ -189,14 +193,61 @@ pub trait ApiHandler: Send + Sync {
189193 }
190194 }
191195
192- async fn get_tree_commit_info ( & self , path : PathBuf ) -> Result < Vec < TreeCommitItem > , GitError > {
193- let item_to_commit_map = self . item_to_commit_map ( path) . await ?;
196+ async fn get_tree_commit_info (
197+ & self ,
198+ path : PathBuf ,
199+ refs : Option < & str > ,
200+ ) -> Result < Vec < TreeCommitItem > , GitError > {
201+ // If refs provided, resolve to commit and list items from that commit's tree at path,
202+ // attaching the same commit info to each item for consistent, minimal behavior.
203+ let maybe = refs. unwrap_or ( "" ) . trim ( ) ;
204+ if !maybe. is_empty ( ) {
205+ // Resolve commit from refs (SHA or tag)
206+ let is_hex_sha1 = |s : & str | s. len ( ) == 40 && s. chars ( ) . all ( |c| c. is_ascii_hexdigit ( ) ) ;
207+ let mut commit_hash = String :: new ( ) ;
208+ if is_hex_sha1 ( maybe) {
209+ commit_hash = maybe. to_string ( ) ;
210+ } else if let Ok ( Some ( tag) ) = self . get_tag ( None , maybe. to_string ( ) ) . await {
211+ commit_hash = tag. object_id ;
212+ }
194213
214+ if !commit_hash. is_empty ( ) {
215+ if let Some ( commit) = self . get_commit_by_hash ( & commit_hash) . await {
216+ // Use refs-aware tree search
217+ if let Some ( tree) = self . search_tree_by_path_with_refs ( & path, refs) . await ? {
218+ let mut items: Vec < TreeCommitItem > = tree
219+ . tree_items
220+ . into_iter ( )
221+ . map ( |item| TreeCommitItem :: from ( ( item, Some ( commit. clone ( ) ) ) ) )
222+ . collect ( ) ;
223+ items. sort_by ( |a, b| {
224+ a. content_type
225+ . cmp ( & b. content_type )
226+ . then ( a. name . cmp ( & b. name ) )
227+ } ) ;
228+ return Ok ( items) ;
229+ } else {
230+ // path not found under this refs
231+ return Ok ( vec ! [ ] ) ;
232+ }
233+ } else {
234+ return Err ( GitError :: CustomError (
235+ "Invalid refs: commit not found" . to_string ( ) ,
236+ ) ) ;
237+ }
238+ } else {
239+ return Err ( GitError :: CustomError (
240+ "Invalid refs: tag or commit not found" . to_string ( ) ,
241+ ) ) ;
242+ }
243+ }
244+
245+ // No refs provided: fallback to existing behavior
246+ let item_to_commit_map = self . item_to_commit_map ( path) . await ?;
195247 let mut items: Vec < TreeCommitItem > = item_to_commit_map
196248 . into_iter ( )
197249 . map ( TreeCommitItem :: from)
198250 . collect ( ) ;
199- // sort with type and name
200251 items. sort_by ( |a, b| {
201252 a. content_type
202253 . cmp ( & b. content_type )
@@ -210,6 +261,15 @@ pub trait ApiHandler: Send + Sync {
210261 path : PathBuf ,
211262 ) -> Result < HashMap < TreeItem , Option < Commit > > , GitError > ;
212263
264+ /// Refs-aware version; default fallback to refs-unaware implementation
265+ async fn item_to_commit_map_with_refs (
266+ & self ,
267+ path : PathBuf ,
268+ _refs : Option < & str > ,
269+ ) -> Result < HashMap < TreeItem , Option < Commit > > , GitError > {
270+ self . item_to_commit_map ( path) . await
271+ }
272+
213273 // Tag related operations shared across mono/import implementations.
214274 /// Create a tag in the repository context represented by `repo_path`.
215275 /// Returns TagInfo on success.
@@ -251,8 +311,12 @@ pub trait ApiHandler: Send + Sync {
251311 /// the dir's hash as same as old,file's hash is the content hash
252312 /// may think about change dir'hash as the content
253313 /// for now,only change the file's hash
254- async fn get_tree_content_hash ( & self , path : PathBuf ) -> Result < Vec < TreeHashItem > , GitError > {
255- match self . search_tree_by_path ( & path) . await ? {
314+ async fn get_tree_content_hash (
315+ & self ,
316+ path : PathBuf ,
317+ refs : Option < & str > ,
318+ ) -> Result < Vec < TreeHashItem > , GitError > {
319+ match self . search_tree_by_path_with_refs ( & path, refs) . await ? {
256320 Some ( tree) => {
257321 let mut items: Vec < TreeHashItem > = tree
258322 . tree_items
@@ -277,8 +341,9 @@ pub trait ApiHandler: Send + Sync {
277341 & self ,
278342 path : PathBuf ,
279343 dir_name : & str ,
344+ refs : Option < & str > ,
280345 ) -> Result < Vec < TreeHashItem > , GitError > {
281- match self . search_tree_by_path ( & path) . await ? {
346+ match self . search_tree_by_path_with_refs ( & path, refs ) . await ? {
282347 Some ( tree) => {
283348 let items: Vec < TreeHashItem > = tree
284349 . tree_items
@@ -384,6 +449,66 @@ pub trait ApiHandler: Send + Sync {
384449 Ok ( Some ( search_tree) )
385450 }
386451
452+ /// Get root tree for a given refs (commit SHA or tag name). If refs is None/empty, use default root.
453+ async fn get_root_tree_for_refs ( & self , refs : Option < & str > ) -> Result < Tree , GitError > {
454+ let maybe = refs. unwrap_or ( "" ) . trim ( ) ;
455+ if maybe. is_empty ( ) {
456+ return Ok ( self . get_root_tree ( ) . await ) ;
457+ }
458+ let is_hex_sha1 = maybe. len ( ) == 40 && maybe. chars ( ) . all ( |c| c. is_ascii_hexdigit ( ) ) ;
459+ let mut commit_hash = String :: new ( ) ;
460+ if is_hex_sha1 {
461+ commit_hash = maybe. to_string ( ) ;
462+ } else if let Ok ( Some ( tag) ) = self . get_tag ( None , maybe. to_string ( ) ) . await {
463+ commit_hash = tag. object_id ;
464+ }
465+
466+ if commit_hash. is_empty ( ) {
467+ return Err ( GitError :: CustomError (
468+ "Invalid refs: tag or commit not found" . to_string ( ) ,
469+ ) ) ;
470+ }
471+ if let Some ( commit) = self . get_commit_by_hash ( & commit_hash) . await {
472+ Ok ( self . get_tree_by_hash ( & commit. tree_id . to_string ( ) ) . await )
473+ } else {
474+ Err ( GitError :: CustomError (
475+ "Invalid refs: commit not found" . to_string ( ) ,
476+ ) )
477+ }
478+ }
479+
480+ /// Refs-aware tree search using a resolved root from refs
481+ async fn search_tree_by_path_with_refs (
482+ & self ,
483+ path : & Path ,
484+ refs : Option < & str > ,
485+ ) -> Result < Option < Tree > , GitError > {
486+ let relative_path = self
487+ . strip_relative ( path)
488+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?;
489+ let root_tree = self . get_root_tree_for_refs ( refs) . await ?;
490+ let mut search_tree = root_tree. clone ( ) ;
491+ for component in relative_path. components ( ) {
492+ if component != Component :: RootDir {
493+ let target_name = component. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
494+ let search_res = search_tree
495+ . tree_items
496+ . iter ( )
497+ . find ( |x| x. name == target_name) ;
498+ if let Some ( search_res) = search_res {
499+ if !search_res. is_tree ( ) {
500+ return Ok ( None ) ;
501+ }
502+ let res = self . get_tree_by_hash ( & search_res. id . to_string ( ) ) . await ;
503+ search_tree = res. clone ( ) ;
504+ } else {
505+ return Ok ( None ) ;
506+ }
507+ }
508+ }
509+ Ok ( Some ( search_tree) )
510+ }
511+
387512 /// Searches for a tree in the Git repository by its path, creating intermediate trees if necessary,
388513 /// and returns the trees involved in the update process.
389514 ///
0 commit comments