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,13 +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 ,
348+ top_level_items ,
345349 ) = DuplicateAwareConfigReader .load_with_duplicates (config_file_path )
346350 except TypeError :
347351 log .exception (
@@ -357,11 +361,32 @@ def add_repo(
357361 else :
358362 raw_config = {}
359363 duplicate_root_occurrences = {}
364+ top_level_items = []
360365 log .info (
361366 "Config file %s not found. A new one will be created." ,
362367 display_config_path ,
363368 )
364369
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+
365390 duplicate_merge_conflicts : list [str ] = []
366391 duplicate_merge_changes = 0
367392 duplicate_merge_details : list [tuple [str , int ]] = []
@@ -416,14 +441,18 @@ def add_repo(
416441 cwd = pathlib .Path .cwd ()
417442 home = pathlib .Path .home ()
418443
444+ aggregated_config = (
445+ raw_config if merge_duplicates else _aggregate_items (config_items )
446+ )
447+
419448 if merge_duplicates :
420449 (
421450 raw_config ,
422451 workspace_map ,
423452 merge_conflicts ,
424453 merge_changes ,
425454 ) = normalize_workspace_roots (
426- raw_config ,
455+ aggregated_config ,
427456 cwd = cwd ,
428457 home = home ,
429458 )
@@ -435,7 +464,7 @@ def add_repo(
435464 merge_conflicts ,
436465 _merge_changes ,
437466 ) = normalize_workspace_roots (
438- raw_config ,
467+ aggregated_config ,
439468 cwd = cwd ,
440469 home = home ,
441470 )
@@ -458,15 +487,24 @@ def add_repo(
458487 )
459488 workspace_map [workspace_path ] = workspace_label
460489 raw_config .setdefault (workspace_label , {})
490+ if not merge_duplicates :
491+ config_items .append ((workspace_label , {}))
461492
462493 if workspace_label not in raw_config :
463494 raw_config [workspace_label ] = {}
495+ if not merge_duplicates :
496+ config_items .append ((workspace_label , {}))
464497 elif not isinstance (raw_config [workspace_label ], dict ):
465498 log .error (
466499 "Workspace root '%s' in configuration is not a dictionary. Aborting." ,
467500 workspace_label ,
468501 )
469502 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+ ]
470508
471509 # Check if repo already exists
472510 if name in raw_config [workspace_label ]:
@@ -517,7 +555,18 @@ def add_repo(
517555 return
518556
519557 # Add the repository in verbose format
520- 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 )
521570
522571 # Save or preview config
523572 if dry_run :
@@ -540,7 +589,10 @@ def add_repo(
540589 )
541590 else :
542591 try :
543- 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 )
544596 log .info (
545597 "%s✓%s Successfully added %s'%s'%s (%s%s%s) to %s%s%s under '%s%s%s'." ,
546598 Fore .GREEN ,
0 commit comments