22
33from __future__ import annotations
44
5+ import copy
56import logging
67import pathlib
78import subprocess
1819 merge_duplicate_workspace_roots ,
1920 normalize_workspace_roots ,
2021 save_config_yaml ,
22+ save_config_yaml_with_items ,
2123 workspace_root_label ,
2224)
2325from vcspull .util import contract_user_home
@@ -335,14 +337,15 @@ def add_repo(
335337 # Load existing config
336338 raw_config : dict [str , t .Any ]
337339 duplicate_root_occurrences : dict [str , list [t .Any ]]
340+ top_level_items : list [tuple [str , t .Any ]]
338341 display_config_path = contract_user_home (config_file_path )
339342
340343 if config_file_path .exists () and config_file_path .is_file ():
341344 try :
342345 (
343346 raw_config ,
344347 duplicate_root_occurrences ,
345- _top_level_items ,
348+ top_level_items ,
346349 ) = DuplicateAwareConfigReader .load_with_duplicates (config_file_path )
347350 except TypeError :
348351 log .exception (
@@ -358,11 +361,32 @@ def add_repo(
358361 else :
359362 raw_config = {}
360363 duplicate_root_occurrences = {}
364+ top_level_items = []
361365 log .info (
362366 "Config file %s not found. A new one will be created." ,
363367 display_config_path ,
364368 )
365369
370+ config_items : list [tuple [str , t .Any ]] = (
371+ [(label , copy .deepcopy (section )) for label , section in top_level_items ]
372+ if top_level_items
373+ else [(label , copy .deepcopy (section )) for label , section in raw_config .items ()]
374+ )
375+
376+ def _aggregate_items (items : list [tuple [str , t .Any ]]) -> dict [str , t .Any ]:
377+ aggregated : dict [str , t .Any ] = {}
378+ for label , section in items :
379+ if isinstance (section , dict ):
380+ workspace_section = aggregated .setdefault (label , {})
381+ for repo_name , repo_config in section .items ():
382+ workspace_section [repo_name ] = copy .deepcopy (repo_config )
383+ else :
384+ aggregated [label ] = copy .deepcopy (section )
385+ return aggregated
386+
387+ if not merge_duplicates :
388+ raw_config = _aggregate_items (config_items )
389+
366390 duplicate_merge_conflicts : list [str ] = []
367391 duplicate_merge_changes = 0
368392 duplicate_merge_details : list [tuple [str , int ]] = []
@@ -417,14 +441,18 @@ def add_repo(
417441 cwd = pathlib .Path .cwd ()
418442 home = pathlib .Path .home ()
419443
444+ aggregated_config = (
445+ raw_config if merge_duplicates else _aggregate_items (config_items )
446+ )
447+
420448 if merge_duplicates :
421449 (
422450 raw_config ,
423451 workspace_map ,
424452 merge_conflicts ,
425453 merge_changes ,
426454 ) = normalize_workspace_roots (
427- raw_config ,
455+ aggregated_config ,
428456 cwd = cwd ,
429457 home = home ,
430458 )
@@ -436,7 +464,7 @@ def add_repo(
436464 merge_conflicts ,
437465 _merge_changes ,
438466 ) = normalize_workspace_roots (
439- raw_config ,
467+ aggregated_config ,
440468 cwd = cwd ,
441469 home = home ,
442470 )
@@ -459,15 +487,24 @@ def add_repo(
459487 )
460488 workspace_map [workspace_path ] = workspace_label
461489 raw_config .setdefault (workspace_label , {})
490+ if not merge_duplicates :
491+ config_items .append ((workspace_label , {}))
462492
463493 if workspace_label not in raw_config :
464494 raw_config [workspace_label ] = {}
495+ if not merge_duplicates :
496+ config_items .append ((workspace_label , {}))
465497 elif not isinstance (raw_config [workspace_label ], dict ):
466498 log .error (
467499 "Workspace root '%s' in configuration is not a dictionary. Aborting." ,
468500 workspace_label ,
469501 )
470502 return
503+ workspace_sections : list [tuple [int , dict [str , t .Any ]]] = [
504+ (idx , section )
505+ for idx , (label , section ) in enumerate (config_items )
506+ if label == workspace_label and isinstance (section , dict )
507+ ]
471508
472509 # Check if repo already exists
473510 if name in raw_config [workspace_label ]:
@@ -518,7 +555,18 @@ def add_repo(
518555 return
519556
520557 # Add the repository in verbose format
521- raw_config [workspace_label ][name ] = {"repo" : url }
558+ new_repo_entry = {"repo" : url }
559+ if merge_duplicates :
560+ raw_config [workspace_label ][name ] = new_repo_entry
561+ else :
562+ target_section : dict [str , t .Any ]
563+ if workspace_sections :
564+ _ , target_section = workspace_sections [- 1 ]
565+ else :
566+ target_section = {}
567+ config_items .append ((workspace_label , target_section ))
568+ target_section [name ] = copy .deepcopy (new_repo_entry )
569+ raw_config [workspace_label ][name ] = copy .deepcopy (new_repo_entry )
522570
523571 # Save or preview config
524572 if dry_run :
@@ -541,7 +589,10 @@ def add_repo(
541589 )
542590 else :
543591 try :
544- save_config_yaml (config_file_path , raw_config )
592+ if merge_duplicates :
593+ save_config_yaml (config_file_path , raw_config )
594+ else :
595+ save_config_yaml_with_items (config_file_path , config_items )
545596 log .info (
546597 "%s✓%s Successfully added %s'%s'%s (%s%s%s) to %s%s%s under '%s%s%s'." ,
547598 Fore .GREEN ,
0 commit comments