diff --git a/subcmds/status.py b/subcmds/status.py index 40562274..cce00c81 100644 --- a/subcmds/status.py +++ b/subcmds/status.py @@ -20,10 +20,14 @@ try: except ImportError: import dummy_threading as _threading +import glob import itertools +import os import sys import StringIO +from color import Coloring + class Status(PagedCommand): common = True helpSummary = "Show the working tree status" @@ -39,6 +43,13 @@ is a difference between these three states. The -j/--jobs option can be used to run multiple status queries in parallel. +The -o/--orphans option can be used to show objects that are in +the working directory, but not associated with a repo project. +This includes unmanaged top-level files and directories, but also +includes deeper items. For example, if dir/subdir/proj1 and +dir/subdir/proj2 are repo projects, dir/subdir/proj3 will be shown +if it is not known to repo. + Status Display -------------- @@ -76,6 +87,9 @@ the following meanings: p.add_option('-j', '--jobs', dest='jobs', action='store', type='int', default=2, help="number of projects to check simultaneously") + p.add_option('-o', '--orphans', + dest='orphans', action='store_true', + help="include objects in working directory outside of repo projects") def _StatusHelper(self, project, clean_counter, sem, output): """Obtains the status for a specific project. @@ -97,6 +111,22 @@ the following meanings: finally: sem.release() + def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring): + """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'""" + status_header = ' --\t' + for item in dirs: + if not os.path.isdir(item): + outstring.write(''.join([status_header, item])) + continue + if item in proj_dirs: + continue + if item in proj_dirs_parents: + self._FindOrphans(glob.glob('%s/.*' % item) + \ + glob.glob('%s/*' % item), \ + proj_dirs, proj_dirs_parents, outstring) + continue + outstring.write(''.join([status_header, item, '/'])) + def Execute(self, opt, args): all_projects = self.GetProjects(args) counter = itertools.count() @@ -130,3 +160,45 @@ the following meanings: output.close() if len(all_projects) == counter.next(): print('nothing to commit (working directory clean)') + + if opt.orphans: + proj_dirs = set() + proj_dirs_parents = set() + for project in self.GetProjects(None, missing_ok=True): + proj_dirs.add(project.relpath) + (head, _tail) = os.path.split(project.relpath) + while head != "": + proj_dirs_parents.add(head) + (head, _tail) = os.path.split(head) + proj_dirs.add('.repo') + + class StatusColoring(Coloring): + def __init__(self, config): + Coloring.__init__(self, config, 'status') + self.project = self.printer('header', attr = 'bold') + self.untracked = self.printer('untracked', fg = 'red') + + orig_path = os.getcwd() + try: + os.chdir(self.manifest.topdir) + + outstring = StringIO.StringIO() + self._FindOrphans(glob.glob('.*') + \ + glob.glob('*'), \ + proj_dirs, proj_dirs_parents, outstring) + + if outstring.buflist: + output = StatusColoring(self.manifest.globalConfig) + output.project('Objects not within a project (orphans)') + output.nl() + for entry in outstring.buflist: + output.untracked(entry) + output.nl() + else: + print('No orphan files or directories') + + outstring.close() + + finally: + # Restore CWD. + os.chdir(orig_path)