@@ -24,7 +24,16 @@ use git_internal::{
2424use jupiter:: { storage:: Storage , utils:: converter:: FromGitModel } ;
2525
2626use crate :: {
27- api_service:: { ApiHandler , cache:: GitObjectCache , history} ,
27+ api_service:: {
28+ ApiHandler ,
29+ cache:: GitObjectCache ,
30+ history,
31+ mono:: MonoServiceLogic ,
32+ tag_ops:: {
33+ self , build_git_internal_tag, db_error, format_tagger_info, is_annotated_tag,
34+ lightweight_commit_tag, merge_paginated_tags, tag_already_exists, tags_full_ref,
35+ } ,
36+ } ,
2837 model:: {
2938 git:: { CreateEntryInfo , CreateEntryResult , EditFilePayload , EditFileResult } ,
3039 tag:: TagInfo ,
@@ -167,51 +176,35 @@ impl ApiHandler for ImportApiService {
167176 message : Option < String > ,
168177 ) -> Result < TagInfo , GitError > {
169178 let git_storage = self . storage . git_db_storage ( ) ;
170- let is_annotated = message. as_ref ( ) . map ( |s| !s. is_empty ( ) ) . unwrap_or ( false ) ;
171- let tagger_info = match ( tagger_name, tagger_email) {
172- ( Some ( n) , Some ( e) ) => format ! ( "{} <{}>" , n, e) ,
173- ( Some ( n) , None ) => n,
174- ( None , Some ( e) ) => e,
175- ( None , None ) => "unknown" . to_string ( ) ,
176- } ;
179+ let tagger_info = format_tagger_info ( tagger_name, tagger_email) ;
177180
178- // validate target commit if provided
179181 self . validate_target_commit ( target. as_ref ( ) ) . await ?;
180182
181- let full_ref = format ! ( "refs/tags/{}" , name. clone( ) ) ;
182- // Prevent duplicate tag/ref creation: check annotated table and refs first.
183+ let full_ref = tags_full_ref ( & name) ;
183184 match git_storage
184185 . get_tag_by_repo_and_name ( self . repo . repo_id , & name)
185186 . await
186187 {
187- Ok ( Some ( _) ) => {
188- return Err ( GitError :: CustomError ( format ! (
189- "[code:400] Tag '{}' already exists" ,
190- name
191- ) ) ) ;
192- }
188+ Ok ( Some ( _) ) => return Err ( tag_already_exists ( & name) ) ,
193189 Ok ( None ) => { }
194190 Err ( e) => {
195191 tracing:: error!( "DB error while checking git_tag existence: {}" , e) ;
196- return Err ( GitError :: CustomError ( "[code:500] DB error" . to_string ( ) ) ) ;
192+ return Err ( db_error ( ) ) ;
197193 }
198194 }
199195
200196 if let Ok ( refs) = git_storage. get_ref ( self . repo . repo_id ) . await
201197 && refs. iter ( ) . any ( |r| r. ref_name == full_ref)
202198 {
203- return Err ( GitError :: CustomError ( format ! (
204- "[code:400] Tag '{}' already exists" ,
205- name
206- ) ) ) ;
199+ return Err ( tag_already_exists ( & name) ) ;
207200 }
208- if is_annotated {
201+
202+ if is_annotated_tag ( & message) {
209203 return self
210204 . create_annotated_tag ( & git_storage, full_ref, name, target, tagger_info, message)
211205 . await ;
212206 }
213207
214- // lightweight
215208 self . create_lightweight_tag ( & git_storage, full_ref, name, target, tagger_info)
216209 . await
217210 }
@@ -234,8 +227,7 @@ impl ApiHandler for ImportApiService {
234227 }
235228 } ;
236229
237- // map annotated page into TagInfo
238- let mut result: Vec < TagInfo > = annotated_tags_page
230+ let result: Vec < TagInfo > = annotated_tags_page
239231 . into_iter ( )
240232 . map ( |t| TagInfo {
241233 name : t. tag_name ,
@@ -248,48 +240,30 @@ impl ApiHandler for ImportApiService {
248240 } )
249241 . collect ( ) ;
250242
251- // lightweight refs
252243 let mut lightweight_refs: Vec < TagInfo > = vec ! [ ] ;
253244 if let Ok ( refs) = git_storage. get_ref ( self . repo . repo_id ) . await {
254245 for r in refs {
255246 if r. ref_name . starts_with ( "refs/tags/" ) {
256247 let tag_name = r. ref_name . trim_start_matches ( "refs/tags/" ) . to_string ( ) ;
257- // skip if annotated exists (anywhere)
258- // Note: we only have the annotated page in memory; to avoid duplicate names we check by tag_name against annotated page and will accept duplicates only if not present.
259248 if result. iter ( ) . any ( |t| t. name == tag_name) {
260249 continue ;
261250 }
262- let created_at = r. created_at . and_utc ( ) . to_rfc3339 ( ) ;
263- lightweight_refs. push ( TagInfo {
264- name : tag_name. clone ( ) ,
265- tag_id : r. ref_git_id . clone ( ) ,
266- object_id : r. ref_git_id . clone ( ) ,
267- object_type : "commit" . to_string ( ) ,
268- tagger : "" . to_string ( ) ,
269- message : "" . to_string ( ) ,
270- created_at,
271- } ) ;
251+ lightweight_refs. push ( lightweight_commit_tag (
252+ tag_name,
253+ r. ref_git_id . clone ( ) ,
254+ "" ,
255+ r. created_at . and_utc ( ) . to_rfc3339 ( ) ,
256+ ) ) ;
272257 }
273258 }
274259 }
275260
276- // total is annotated_total + lightweight_refs.len()
277- let total = annotated_total + lightweight_refs. len ( ) as u64 ;
278-
279- // fill page: annotated page items come first, then lightweight refs to make up page size
280- let per_page = if pagination. per_page == 0 {
281- 20
282- } else {
283- pagination. per_page
284- } as usize ;
285- if result. len ( ) < per_page {
286- let need = per_page - result. len ( ) ;
287- for r in lightweight_refs. into_iter ( ) . take ( need) {
288- result. push ( r) ;
289- }
290- }
291-
292- Ok ( ( result, total) )
261+ Ok ( merge_paginated_tags (
262+ result,
263+ lightweight_refs,
264+ annotated_total,
265+ pagination. per_page ,
266+ ) )
293267 }
294268
295269 async fn get_tag (
@@ -320,21 +294,16 @@ impl ApiHandler for ImportApiService {
320294 return Err ( GitError :: CustomError ( "[code:500] DB error" . to_string ( ) ) ) ;
321295 }
322296 }
323- // check import_refs for lightweight
324- let full_ref = format ! ( "refs/tags/{}" , name. clone( ) ) ;
297+ let full_ref = tags_full_ref ( & name) ;
325298 if let Ok ( refs) = git_storage. get_ref ( self . repo . repo_id ) . await {
326299 for r in refs {
327300 if r. ref_name == full_ref {
328- let created_at = r. created_at . and_utc ( ) . to_rfc3339 ( ) ;
329- return Ok ( Some ( TagInfo {
330- name : name. clone ( ) ,
331- tag_id : r. ref_git_id . clone ( ) ,
332- object_id : r. ref_git_id . clone ( ) ,
333- object_type : "commit" . to_string ( ) ,
334- tagger : "" . to_string ( ) ,
335- message : "" . to_string ( ) ,
336- created_at,
337- } ) ) ;
301+ return Ok ( Some ( lightweight_commit_tag (
302+ name,
303+ r. ref_git_id . clone ( ) ,
304+ "" ,
305+ r. created_at . and_utc ( ) . to_rfc3339 ( ) ,
306+ ) ) ) ;
338307 }
339308 }
340309 }
@@ -349,8 +318,7 @@ impl ApiHandler for ImportApiService {
349318 . await
350319 {
351320 Ok ( Some ( _tag) ) => {
352- // remove import ref if exists
353- let full_ref = format ! ( "refs/tags/{}" , name. clone( ) ) ;
321+ let full_ref = tags_full_ref ( & name) ;
354322 git_storage
355323 . remove_ref ( self . repo . repo_id , & full_ref)
356324 . await
@@ -371,8 +339,7 @@ impl ApiHandler for ImportApiService {
371339 Ok ( ( ) )
372340 }
373341 Ok ( None ) => {
374- // remove lightweight ref if exists
375- let full_ref = format ! ( "refs/tags/{}" , name. clone( ) ) ;
342+ let full_ref = tags_full_ref ( & name) ;
376343 git_storage
377344 . remove_ref ( self . repo . repo_id , & full_ref)
378345 . await
@@ -418,7 +385,7 @@ impl ApiHandler for ImportApiService {
418385 // Create new blob and rebuild tree up to root
419386 let new_blob = Blob :: from_content ( & payload. content ) ;
420387 let ( updated_trees, new_root_id) =
421- self . build_updated_trees ( path. clone ( ) , update_chain, new_blob. id ) ?;
388+ MonoServiceLogic :: propagate_tree_chain ( path. clone ( ) , update_chain, new_blob. id ) ?;
422389
423390 // Save commit and objects under import repo tables
424391 let git_storage = self . storage . git_db_storage ( ) ;
@@ -488,12 +455,8 @@ impl ImportApiService {
488455 message : Option < String > ,
489456 ) -> Result < TagInfo , GitError > {
490457 // build git_internal tag and models
491- let ( tag_id_hex, object_id) = self . build_git_internal_tag (
492- name. clone ( ) ,
493- target. clone ( ) ,
494- tagger_info. clone ( ) ,
495- message. clone ( ) ,
496- ) ?;
458+ let ( tag_id_hex, object_id) =
459+ build_git_internal_tag ( name. clone ( ) , target, tagger_info. clone ( ) , message. clone ( ) ) ?;
497460
498461 let new_model = self . build_git_tag_model (
499462 tag_id_hex. clone ( ) ,
@@ -559,66 +522,32 @@ impl ImportApiService {
559522 tracing:: error!( "Failed to write import ref for lightweight tag: {}" , e) ;
560523 GitError :: CustomError ( "[code:500] Failed to write import ref" . to_string ( ) )
561524 } ) ?;
562- Ok ( TagInfo {
563- name : name. clone ( ) ,
564- tag_id : object_id. clone ( ) ,
565- object_id : object_id. clone ( ) ,
566- object_type : "commit" . to_string ( ) ,
567- tagger : tagger_info. clone ( ) ,
568- message : String :: new ( ) ,
525+ Ok ( lightweight_commit_tag (
526+ name,
527+ object_id,
528+ tagger_info,
569529 created_at,
570- } )
530+ ) )
571531 }
572532
573533 async fn validate_target_commit ( & self , target : Option < & String > ) -> Result < ( ) , GitError > {
574- if let Some ( ref t) = target {
534+ if let Some ( t) = target {
575535 let git_storage = self . storage . git_db_storage ( ) ;
576536 match git_storage. get_commit_by_hash ( self . repo . repo_id , t) . await {
577537 Ok ( c) => {
578538 if c. is_none ( ) {
579- return Err ( GitError :: CustomError ( format ! (
580- "[code:404] Target commit '{}' not found" ,
581- t
582- ) ) ) ;
539+ return Err ( tag_ops:: commit_not_found ( t) ) ;
583540 }
584541 }
585542 Err ( e) => {
586543 tracing:: error!( "DB error while fetching commit by hash: {}" , e) ;
587- return Err ( GitError :: CustomError ( "[code:500] DB error" . to_string ( ) ) ) ;
544+ return Err ( db_error ( ) ) ;
588545 }
589546 }
590547 }
591548 Ok ( ( ) )
592549 }
593550
594- fn build_git_internal_tag (
595- & self ,
596- name : String ,
597- target : Option < String > ,
598- tagger_info : String ,
599- message : Option < String > ,
600- ) -> Result < ( String , String ) , GitError > {
601- let tag_target = target
602- . as_ref ( )
603- . ok_or ( GitError :: InvalidCommitObject )
604- . and_then ( |t| ObjectHash :: from_str ( t) . map_err ( |_| GitError :: InvalidCommitObject ) ) ?;
605- let git_internal_tag = git_internal:: internal:: object:: tag:: Tag :: new (
606- tag_target,
607- git_internal:: internal:: object:: types:: ObjectType :: Commit ,
608- name. clone ( ) ,
609- git_internal:: internal:: object:: signature:: Signature :: new (
610- git_internal:: internal:: object:: signature:: SignatureType :: Tagger ,
611- tagger_info. clone ( ) ,
612- String :: new ( ) ,
613- ) ,
614- message. clone ( ) . unwrap_or_default ( ) ,
615- ) ;
616- Ok ( (
617- git_internal_tag. id . to_string ( ) ,
618- target. unwrap_or_else ( || "HEAD" . to_string ( ) ) ,
619- ) )
620- }
621-
622551 fn build_git_tag_model (
623552 & self ,
624553 tag_id_hex : String ,
@@ -675,43 +604,4 @@ impl ImportApiService {
675604 }
676605 Ok ( ( ) )
677606 }
678-
679- fn update_tree_hash (
680- & self ,
681- tree : Arc < Tree > ,
682- name : & str ,
683- target_hash : ObjectHash ,
684- ) -> Result < Tree , GitError > {
685- let index = tree
686- . tree_items
687- . iter ( )
688- . position ( |item| item. name == name)
689- . ok_or_else ( || GitError :: CustomError ( format ! ( "Tree item '{}' not found" , name) ) ) ?;
690- let mut items = tree. tree_items . clone ( ) ;
691- items[ index] . id = target_hash;
692- Tree :: from_tree_items ( items) . map_err ( |_| GitError :: CustomError ( "Invalid tree" . to_string ( ) ) )
693- }
694-
695- /// Build updated trees chain and return (updated_trees, new_root_tree_id)
696- fn build_updated_trees (
697- & self ,
698- mut path : PathBuf ,
699- mut update_chain : Vec < Arc < Tree > > ,
700- mut updated_tree_hash : ObjectHash ,
701- ) -> Result < ( Vec < Tree > , ObjectHash ) , GitError > {
702- let mut updated_trees = Vec :: new ( ) ;
703- while let Some ( tree) = update_chain. pop ( ) {
704- let cloned_path = path. clone ( ) ;
705- let name = cloned_path
706- . file_name ( )
707- . and_then ( |n| n. to_str ( ) )
708- . ok_or_else ( || GitError :: CustomError ( "Invalid path" . into ( ) ) ) ?;
709- path. pop ( ) ;
710-
711- let new_tree = self . update_tree_hash ( tree, name, updated_tree_hash) ?;
712- updated_tree_hash = new_tree. id ;
713- updated_trees. push ( new_tree) ;
714- }
715- Ok ( ( updated_trees, updated_tree_hash) )
716- }
717607}
0 commit comments