@@ -10,14 +10,15 @@ use git_internal::errors::GitError;
1010use git_internal:: hash:: SHA1 ;
1111use git_internal:: internal:: object:: commit:: Commit ;
1212use git_internal:: internal:: object:: tree:: { Tree , TreeItem , TreeItemMode } ;
13+ use git_internal:: internal:: pack:: entry:: Entry ;
1314
1415use common:: errors:: MegaError ;
1516use common:: model:: TagInfo ;
1617use jupiter:: storage:: Storage ;
1718
1819use crate :: api_service:: { ApiHandler , GitObjectCache } ;
1920use crate :: model:: blame:: { BlameQuery , BlameResult } ;
20- use crate :: model:: git:: CreateEntryInfo ;
21+ use crate :: model:: git:: { CreateEntryInfo , EditFilePayload , EditFileResult } ;
2122use crate :: protocol:: repo:: Repo ;
2223use callisto:: { git_tag, import_refs} ;
2324use jupiter:: utils:: converter:: FromGitModel ;
@@ -248,14 +249,15 @@ impl ApiHandler for ImportApiService {
248249 if result. iter ( ) . any ( |t| t. name == tag_name) {
249250 continue ;
250251 }
252+ let created_at = r. created_at . and_utc ( ) . to_rfc3339 ( ) ;
251253 lightweight_refs. push ( TagInfo {
252254 name : tag_name. clone ( ) ,
253255 tag_id : r. ref_git_id . clone ( ) ,
254256 object_id : r. ref_git_id . clone ( ) ,
255257 object_type : "commit" . to_string ( ) ,
256258 tagger : "" . to_string ( ) ,
257259 message : "" . to_string ( ) ,
258- created_at : chrono :: Utc :: now ( ) . to_rfc3339 ( ) ,
260+ created_at,
259261 } ) ;
260262 }
261263 }
@@ -313,14 +315,15 @@ impl ApiHandler for ImportApiService {
313315 if let Ok ( refs) = git_storage. get_ref ( self . repo . repo_id ) . await {
314316 for r in refs {
315317 if r. ref_name == full_ref {
318+ let created_at = r. created_at . and_utc ( ) . to_rfc3339 ( ) ;
316319 return Ok ( Some ( TagInfo {
317320 name : name. clone ( ) ,
318321 tag_id : r. ref_git_id . clone ( ) ,
319322 object_id : r. ref_git_id . clone ( ) ,
320323 object_type : "commit" . to_string ( ) ,
321324 tagger : "" . to_string ( ) ,
322325 message : "" . to_string ( ) ,
323- created_at : chrono :: Utc :: now ( ) . to_rfc3339 ( ) ,
326+ created_at,
324327 } ) ) ;
325328 }
326329 }
@@ -387,6 +390,84 @@ impl ApiHandler for ImportApiService {
387390 "Import directory does not support blame functionality" . to_string ( ) ,
388391 ) )
389392 }
393+
394+ /// Save file edit for import repo path
395+ async fn save_file_edit ( & self , payload : EditFilePayload ) -> Result < EditFileResult , GitError > {
396+ use git_internal:: internal:: object:: blob:: Blob ;
397+ use git_internal:: internal:: object:: tree:: TreeItemMode ;
398+
399+ let path = PathBuf :: from ( & payload. path ) ;
400+ let parent = path
401+ . parent ( )
402+ . ok_or_else ( || GitError :: CustomError ( "Invalid file path" . to_string ( ) ) ) ?;
403+ let update_chain = self
404+ . search_tree_for_update ( parent)
405+ . await
406+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?;
407+ let parent_tree = update_chain
408+ . last ( )
409+ . cloned ( )
410+ . ok_or_else ( || GitError :: CustomError ( "Parent tree not found" . to_string ( ) ) ) ?;
411+ let name = path
412+ . file_name ( )
413+ . and_then ( |n| n. to_str ( ) )
414+ . ok_or_else ( || GitError :: CustomError ( "Invalid file name" . to_string ( ) ) ) ?;
415+ let _current = parent_tree
416+ . tree_items
417+ . iter ( )
418+ . find ( |x| x. name == name && x. mode == TreeItemMode :: Blob )
419+ . ok_or_else ( || GitError :: CustomError ( "[code:404] File not found" . to_string ( ) ) ) ?;
420+
421+ // Create new blob and rebuild tree up to root
422+ let new_blob = Blob :: from_content ( & payload. content ) ;
423+ let ( updated_trees, new_root_id) =
424+ self . build_updated_trees ( path. clone ( ) , update_chain, new_blob. id ) ?;
425+
426+ // Save commit and objects under import repo tables
427+ let git_storage = self . storage . git_db_storage ( ) ;
428+ let new_commit_id = {
429+ // Update default branch ref commit with parent = current default commit
430+ let default_ref = git_storage
431+ . get_default_ref ( self . repo . repo_id )
432+ . await
433+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?
434+ . ok_or_else ( || GitError :: CustomError ( "Default ref not found" . to_string ( ) ) ) ?;
435+ let current_commit = git_storage
436+ . get_commit_by_hash ( self . repo . repo_id , & default_ref. ref_git_id )
437+ . await
438+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?
439+ . ok_or ( GitError :: InvalidCommitObject ) ?;
440+ let parent_id = SHA1 :: from_str ( & current_commit. commit_id ) . unwrap ( ) ;
441+
442+ let new_commit =
443+ Commit :: from_tree_id ( new_root_id, vec ! [ parent_id] , & payload. commit_message ) ;
444+ let new_commit_id = new_commit. id . to_string ( ) ;
445+
446+ let mut entries: Vec < Entry > = Vec :: new ( ) ;
447+ for t in updated_trees. iter ( ) . cloned ( ) {
448+ entries. push ( Entry :: from ( t) ) ;
449+ }
450+ entries. push ( Entry :: from ( new_blob. clone ( ) ) ) ;
451+ entries. push ( Entry :: from ( new_commit. clone ( ) ) ) ;
452+ git_storage
453+ . save_entry ( self . repo . repo_id , entries)
454+ . await
455+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?;
456+
457+ // Update ref to new commit id
458+ git_storage
459+ . update_ref ( self . repo . repo_id , & default_ref. ref_name , & new_commit_id)
460+ . await
461+ . map_err ( |e| GitError :: CustomError ( e. to_string ( ) ) ) ?;
462+ new_commit_id
463+ } ;
464+
465+ Ok ( EditFileResult {
466+ commit_id : new_commit_id,
467+ new_oid : new_blob. id . to_string ( ) ,
468+ path : payload. path ,
469+ } )
470+ }
390471}
391472
392473impl ImportApiService {
@@ -462,6 +543,8 @@ impl ImportApiService {
462543 created_at : chrono:: Utc :: now ( ) . naive_utc ( ) ,
463544 updated_at : chrono:: Utc :: now ( ) . naive_utc ( ) ,
464545 } ;
546+ // Use ref creation time as lightweight tag created_at (capture before move)
547+ let created_at = import_ref. created_at . and_utc ( ) . to_rfc3339 ( ) ;
465548 git_storage
466549 . save_ref ( self . repo . repo_id , import_ref)
467550 . await
@@ -476,7 +559,7 @@ impl ImportApiService {
476559 object_type : "commit" . to_string ( ) ,
477560 tagger : tagger_info. clone ( ) ,
478561 message : String :: new ( ) ,
479- created_at : chrono :: Utc :: now ( ) . to_rfc3339 ( ) ,
562+ created_at,
480563 } )
481564 }
482565 /// Traverses the commit history starting from a given commit, looking for the earliest commit
@@ -638,9 +721,7 @@ impl ImportApiService {
638721 . unwrap ( ) ,
639722 )
640723 }
641- }
642724
643- impl ImportApiService {
644725 async fn validate_target_commit ( & self , target : Option < & String > ) -> Result < ( ) , GitError > {
645726 if let Some ( ref t) = target {
646727 let git_storage = self . storage . git_db_storage ( ) ;
@@ -743,4 +824,43 @@ impl ImportApiService {
743824 }
744825 Ok ( ( ) )
745826 }
827+
828+ fn update_tree_hash (
829+ & self ,
830+ tree : Arc < Tree > ,
831+ name : & str ,
832+ target_hash : SHA1 ,
833+ ) -> Result < Tree , GitError > {
834+ let index = tree
835+ . tree_items
836+ . iter ( )
837+ . position ( |item| item. name == name)
838+ . ok_or_else ( || GitError :: CustomError ( format ! ( "Tree item '{}' not found" , name) ) ) ?;
839+ let mut items = tree. tree_items . clone ( ) ;
840+ items[ index] . id = target_hash;
841+ Tree :: from_tree_items ( items) . map_err ( |_| GitError :: CustomError ( "Invalid tree" . to_string ( ) ) )
842+ }
843+
844+ /// Build updated trees chain and return (updated_trees, new_root_tree_id)
845+ fn build_updated_trees (
846+ & self ,
847+ mut path : PathBuf ,
848+ mut update_chain : Vec < Arc < Tree > > ,
849+ mut updated_tree_hash : SHA1 ,
850+ ) -> Result < ( Vec < Tree > , SHA1 ) , GitError > {
851+ let mut updated_trees = Vec :: new ( ) ;
852+ while let Some ( tree) = update_chain. pop ( ) {
853+ let cloned_path = path. clone ( ) ;
854+ let name = cloned_path
855+ . file_name ( )
856+ . and_then ( |n| n. to_str ( ) )
857+ . ok_or_else ( || GitError :: CustomError ( "Invalid path" . into ( ) ) ) ?;
858+ path. pop ( ) ;
859+
860+ let new_tree = self . update_tree_hash ( tree, name, updated_tree_hash) ?;
861+ updated_tree_hash = new_tree. id ;
862+ updated_trees. push ( new_tree) ;
863+ }
864+ Ok ( ( updated_trees, updated_tree_hash) )
865+ }
746866}
0 commit comments