@@ -584,6 +584,11 @@ def __init__(self):
584584 If this file uses imports, it will not contain all the
585585 manifest data.
586586
587+ - --untracked print all files and folders inside the workspace that
588+ are not tracked or managed by west. This effectively means any
589+ file or folder that lays outside all of the projects' folders on
590+ disk. This is similar to `git status` for untracked files.
591+
587592 If the manifest file does not use imports, and all project
588593 revisions are SHAs, the --freeze and --resolve output will
589594 be identical after a "west update".
@@ -604,6 +609,9 @@ def do_add_parser(self, parser_adder):
604609 exiting with an error if there are issues''' )
605610 group .add_argument ('--path' , action = 'store_true' ,
606611 help = "print the top level manifest file's path" )
612+ group .add_argument ('--untracked' , action = 'store_true' ,
613+ help = '''print any files and folders not managed or
614+ tracked by west''' )
607615
608616 group = parser .add_argument_group ('options for --resolve and --freeze' )
609617 group .add_argument ('-o' , '--out' ,
@@ -624,6 +632,8 @@ def do_run(self, args, user_args):
624632 elif args .freeze :
625633 self ._die_if_manifest_project_filter ('freeze' )
626634 self ._dump (args , manifest .as_frozen_yaml (** dump_kwargs ))
635+ elif args .untracked :
636+ self ._untracked ()
627637 elif args .path :
628638 self .inf (manifest .path )
629639 else :
@@ -640,6 +650,59 @@ def _die_if_manifest_project_filter(self, action):
640650 'the manifest while projects are made inactive by the '
641651 'project filter.' )
642652
653+ def _untracked (self ):
654+ ppaths = []
655+ untracked = []
656+ for project in self ._projects (None ):
657+ # We do not check for self.manifest.is_active(project) because
658+ # inactive projects are still considered "tracked folders".
659+ ppaths .append (Path (project .abspath ).resolve (strict = False ))
660+
661+ def _find_untracked (folder ):
662+ self .dbg (f'_find_untracked in: { folder } ' )
663+ for e in [e .resolve () for e in folder .iterdir ()]:
664+ if e .is_dir ():
665+ self .dbg (f'processing folder: { e } ' )
666+ for ppath in ppaths :
667+ # We cannot use samefile() because it requires the file
668+ # to exist (not always the case with inactive or even
669+ # uncloned projects).
670+ if ppath == e :
671+ # We hit a project root folder, skip it.
672+ break
673+ elif e in ppath .parents :
674+ self .dbg (f'recursing into: { e } ' )
675+ _find_untracked (e )
676+ break
677+ else :
678+ # No match, untracked folder.
679+ untracked .append (e )
680+ continue
681+ else :
682+ # Untracked file.
683+ untracked .append (e )
684+
685+ # Since west supports nested projects (i.e. a project inside the folder
686+ # of another project) we must sort the project paths to ensure that we
687+ # hit the "enclosing" project first when iterating.
688+ ppaths .sort ()
689+
690+ # Avoid using Path.walk() since that returns all files and folders under
691+ # a particular folder, which is overkill in our case. Instead, recurse
692+ # only when required.
693+ _find_untracked (Path (self .topdir ))
694+
695+ # Remove the .west folder, which is maintained by west
696+ try :
697+ untracked .remove ((Path (topdir ) / Path (WEST_DIR )).resolve ())
698+ except ValueError :
699+ self .die (f'Folder { WEST_DIR } not found in workspace' )
700+
701+ # Sort the results for displaying to the user.
702+ untracked .sort ()
703+ for u in untracked :
704+ self .inf (u .relative_to (Path .cwd (), walk_up = True ))
705+
643706 def _dump (self , args , to_dump ):
644707 if args .out :
645708 with open (args .out , 'w' ) as f :
@@ -881,7 +944,10 @@ def __init__(self):
881944 'status' ,
882945 '"git status" for one or more projects' ,
883946 '''Runs "git status" for each of the specified projects.
884- Unknown arguments are passed to "git status".''' ,
947+ Unknown arguments are passed to "git status".
948+
949+ Note: If you are looking to find untracked files and folders
950+ in the workspace use "west manifest --untracked".''' ,
885951 accepts_unknown_args = True ,
886952 )
887953
0 commit comments