'repo status --orphans' shows non-repo files
'repo status --orphans' searches for non-repo objects
(not within a project), which is particularly helpful
before removing a working tree.
Change-Id: I2239c12e6bc0447b0ad71129551cb50fa671961c
diff --git a/subcmds/status.py b/subcmds/status.py
index 4056227..cce00c8 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -20,10 +20,14 @@
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 @@
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 @@
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 @@
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 @@
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)