@@ -2,24 +2,59 @@ use std::{path::PathBuf, sync::Arc};
22
33use git_internal:: {
44 errors:: GitError ,
5- hash:: SHA1 ,
6- internal:: object:: {
7- commit:: Commit ,
8- tree:: { TreeItem , TreeItemMode } ,
9- } ,
5+ internal:: object:: tree:: { TreeItem , TreeItemMode } ,
106} ;
117use tokio:: sync:: Mutex ;
128
139use crate :: api_service:: { ApiHandler , cache:: GitObjectCache , history, tree_ops} ;
1410use crate :: model:: git:: { CommitBindingInfo , LatestCommitInfo } ;
1511
12+ /// Get the latest commit that modified a file or directory.
13+ ///
14+ /// This unified function handles both tag-based and commit-based browsing through
15+ /// the `refs` parameter, ensuring consistent behavior across all code paths.
16+ ///
17+ /// # Arguments
18+ /// - `handler`: API handler for accessing Git data
19+ /// - `path`: File or directory path to check
20+ /// - `refs`: Optional reference (tag name or commit SHA). If None, uses default HEAD/root.
21+ ///
22+ /// # Returns
23+ /// The commit information for the last modification of the specified path.
1624pub async fn get_latest_commit < T : ApiHandler + ?Sized > (
1725 handler : & T ,
1826 path : PathBuf ,
27+ refs : Option < & str > ,
1928) -> Result < LatestCommitInfo , GitError > {
29+ // Resolve the starting commit from refs
30+ let start_commit = crate :: api_service:: resolve_start_commit ( handler, refs) . await ?;
31+
2032 // 1) Try as directory path first
21- if let Some ( tree) = tree_ops:: search_tree_by_path ( handler, & path, None ) . await ? {
22- let commit = get_tree_relate_commit ( handler, tree. id , path) . await ?;
33+ if let Some ( tree) = tree_ops:: search_tree_by_path ( handler, & path, refs) . await ? {
34+ // Treat directory as a TreeItem and find its last modification
35+ let dir_name = path
36+ . file_name ( )
37+ . and_then ( |n| n. to_str ( ) )
38+ . ok_or_else ( || GitError :: CustomError ( "Invalid directory path" . to_string ( ) ) ) ?
39+ . to_string ( ) ;
40+
41+ let parent = path
42+ . parent ( )
43+ . ok_or_else ( || GitError :: CustomError ( "Directory has no parent" . to_string ( ) ) ) ?;
44+
45+ let dir_item = TreeItem :: new ( TreeItemMode :: Tree , tree. id , dir_name) ;
46+
47+ let cache = Arc :: new ( Mutex :: new ( GitObjectCache :: default ( ) ) ) ;
48+
49+ let commit = history:: traverse_commit_history_for_last_modification (
50+ handler,
51+ parent,
52+ start_commit. clone ( ) ,
53+ & dir_item,
54+ cache,
55+ )
56+ . await ?;
57+
2358 let mut commit_info: LatestCommitInfo = commit. clone ( ) . into ( ) ;
2459
2560 // If commit has a username binding, prefer showing that username
@@ -38,25 +73,11 @@ pub async fn get_latest_commit<T: ApiHandler + ?Sized>(
3873 }
3974
4075 // 2) If not a directory, try as file path
41- // basic validation for file path
42- path. file_name ( )
43- . and_then ( |n| n. to_str ( ) )
44- . ok_or_else ( || GitError :: CustomError ( "Invalid file path" . to_string ( ) ) ) ?;
45- let parent = path
46- . parent ( )
47- . ok_or_else ( || GitError :: CustomError ( "Invalid file path" . to_string ( ) ) ) ?;
76+ // Use unified last-modification logic
77+ let cache = Arc :: new ( Mutex :: new ( GitObjectCache :: default ( ) ) ) ;
4878
49- // parent must be a directory tree that exists
50- if tree_ops:: search_tree_by_path ( handler, parent, None )
51- . await ?
52- . is_none ( )
53- {
54- return Err ( GitError :: CustomError (
55- "can't find target parent tree under latest commit" . to_string ( ) ,
56- ) ) ;
57- } ;
58- match history:: resolve_latest_commit_for_file_path ( handler, & path) . await ? {
59- Some ( commit) => {
79+ match history:: resolve_last_modification_by_path ( handler, & path, start_commit, cache) . await {
80+ Ok ( commit) => {
6081 let mut commit_info: LatestCommitInfo = commit. clone ( ) . into ( ) ;
6182 // If commit has a username binding, prefer showing that username
6283 if let Ok ( Some ( binding) ) = handler
@@ -71,35 +92,12 @@ pub async fn get_latest_commit<T: ApiHandler + ?Sized>(
7192 }
7293 Ok ( commit_info)
7394 }
74- None => Err ( GitError :: CustomError (
95+ Err ( _ ) => Err ( GitError :: CustomError (
7596 "[code:404] File not found" . to_string ( ) ,
7697 ) ) ,
7798 }
7899}
79100
80- pub async fn get_tree_relate_commit < T : ApiHandler + ?Sized > (
81- handler : & T ,
82- t_hash : SHA1 ,
83- path : PathBuf ,
84- ) -> Result < Commit , GitError > {
85- let file_name = match path. file_name ( ) {
86- Some ( name) => name. to_string_lossy ( ) . to_string ( ) ,
87- None => {
88- return Err ( GitError :: CustomError ( "Invalid Path Input" . to_string ( ) ) ) ;
89- }
90- } ;
91-
92- let search_item = TreeItem :: new ( TreeItemMode :: Tree , t_hash, file_name) ;
93- let cache = Arc :: new ( Mutex :: new ( GitObjectCache :: default ( ) ) ) ;
94- let root_commit = Arc :: new ( handler. get_root_commit ( ) . await ) ;
95-
96- let parent = match path. parent ( ) {
97- Some ( p) => p,
98- None => return Err ( GitError :: CustomError ( "Invalid Path Input" . to_string ( ) ) ) ,
99- } ;
100- history:: traverse_commit_history ( handler, parent, root_commit, & search_item, cache) . await
101- }
102-
103101/// Build commit binding information for a given commit SHA
104102pub async fn build_commit_binding_info < T : ApiHandler + ?Sized > (
105103 handler : & T ,
0 commit comments