blob: b42675e02903e26d27963bcc1ed65b01c3227548 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16from command import PagedCommand
17
Terence Haddock4655e812011-03-31 12:33:34 +020018try:
19 import threading as _threading
20except ImportError:
21 import dummy_threading as _threading
22
Will Richey63d356f2012-06-21 09:49:59 -040023import glob
David Pursehouse59bbb582013-05-17 10:49:33 +090024
25from pyversion import is_python3
26if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053027 import io
David Pursehouse59bbb582013-05-17 10:49:33 +090028else:
29 import StringIO as io
30
Terence Haddock4655e812011-03-31 12:33:34 +020031import itertools
Will Richey63d356f2012-06-21 09:49:59 -040032import os
Terence Haddock4655e812011-03-31 12:33:34 +020033import sys
Terence Haddock4655e812011-03-31 12:33:34 +020034
Will Richey63d356f2012-06-21 09:49:59 -040035from color import Coloring
36
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070037class Status(PagedCommand):
38 common = True
39 helpSummary = "Show the working tree status"
40 helpUsage = """
41%prog [<project>...]
42"""
Shawn O. Pearce4c5c7aa2009-04-13 14:06:10 -070043 helpDescription = """
44'%prog' compares the working tree to the staging area (aka index),
45and the most recent commit on this branch (HEAD), in each project
46specified. A summary is displayed, one line per file where there
47is a difference between these three states.
48
Terence Haddock4655e812011-03-31 12:33:34 +020049The -j/--jobs option can be used to run multiple status queries
50in parallel.
51
Will Richey63d356f2012-06-21 09:49:59 -040052The -o/--orphans option can be used to show objects that are in
53the working directory, but not associated with a repo project.
54This includes unmanaged top-level files and directories, but also
55includes deeper items. For example, if dir/subdir/proj1 and
56dir/subdir/proj2 are repo projects, dir/subdir/proj3 will be shown
57if it is not known to repo.
58
Shawn O. Pearce4c5c7aa2009-04-13 14:06:10 -070059Status Display
60--------------
61
62The status display is organized into three columns of information,
63for example if the file 'subcmds/status.py' is modified in the
64project 'repo' on branch 'devwork':
65
66 project repo/ branch devwork
67 -m subcmds/status.py
68
69The first column explains how the staging area (index) differs from
70the last commit (HEAD). Its values are always displayed in upper
71case and have the following meanings:
72
73 -: no difference
74 A: added (not in HEAD, in index )
75 M: modified ( in HEAD, in index, different content )
76 D: deleted ( in HEAD, not in index )
77 R: renamed (not in HEAD, in index, path changed )
78 C: copied (not in HEAD, in index, copied from another)
79 T: mode changed ( in HEAD, in index, same content )
80 U: unmerged; conflict resolution required
81
82The second column explains how the working directory differs from
83the index. Its values are always displayed in lower case and have
84the following meanings:
85
86 -: new / unknown (not in index, in work tree )
87 m: modified ( in index, in work tree, modified )
88 d: deleted ( in index, not in work tree )
89
90"""
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070091
Terence Haddock4655e812011-03-31 12:33:34 +020092 def _Options(self, p):
93 p.add_option('-j', '--jobs',
94 dest='jobs', action='store', type='int', default=2,
95 help="number of projects to check simultaneously")
Will Richey63d356f2012-06-21 09:49:59 -040096 p.add_option('-o', '--orphans',
97 dest='orphans', action='store_true',
98 help="include objects in working directory outside of repo projects")
Terence Haddock4655e812011-03-31 12:33:34 +020099
100 def _StatusHelper(self, project, clean_counter, sem, output):
101 """Obtains the status for a specific project.
102
103 Obtains the status for a project, redirecting the output to
104 the specified object. It will release the semaphore
105 when done.
106
107 Args:
108 project: Project to get status of.
109 clean_counter: Counter for clean projects.
110 sem: Semaphore, will call release() when complete.
111 output: Where to output the status.
112 """
113 try:
114 state = project.PrintWorkTreeStatus(output)
115 if state == 'CLEAN':
Anthony King2cd1f042014-05-05 21:24:05 +0100116 next(clean_counter)
Terence Haddock4655e812011-03-31 12:33:34 +0200117 finally:
118 sem.release()
119
Will Richey63d356f2012-06-21 09:49:59 -0400120 def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring):
121 """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'"""
122 status_header = ' --\t'
123 for item in dirs:
124 if not os.path.isdir(item):
125 outstring.write(''.join([status_header, item]))
126 continue
127 if item in proj_dirs:
128 continue
129 if item in proj_dirs_parents:
130 self._FindOrphans(glob.glob('%s/.*' % item) + \
131 glob.glob('%s/*' % item), \
132 proj_dirs, proj_dirs_parents, outstring)
133 continue
134 outstring.write(''.join([status_header, item, '/']))
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136 def Execute(self, opt, args):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900137 all_projects = self.GetProjects(args)
Terence Haddock4655e812011-03-31 12:33:34 +0200138 counter = itertools.count()
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700139
Terence Haddock4655e812011-03-31 12:33:34 +0200140 if opt.jobs == 1:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900141 for project in all_projects:
Terence Haddock4655e812011-03-31 12:33:34 +0200142 state = project.PrintWorkTreeStatus()
143 if state == 'CLEAN':
Anthony King2cd1f042014-05-05 21:24:05 +0100144 next(counter)
Terence Haddock4655e812011-03-31 12:33:34 +0200145 else:
146 sem = _threading.Semaphore(opt.jobs)
147 threads_and_output = []
David Pursehouse8a68ff92012-09-24 12:15:13 +0900148 for project in all_projects:
Terence Haddock4655e812011-03-31 12:33:34 +0200149 sem.acquire()
Cezary Baginskiccf86432012-04-23 23:55:35 +0200150
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530151 class BufList(io.StringIO):
Cezary Baginskiccf86432012-04-23 23:55:35 +0200152 def dump(self, ostream):
153 for entry in self.buflist:
154 ostream.write(entry)
155
156 output = BufList()
157
Terence Haddock4655e812011-03-31 12:33:34 +0200158 t = _threading.Thread(target=self._StatusHelper,
159 args=(project, counter, sem, output))
160 threads_and_output.append((t, output))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200161 t.daemon = True
Terence Haddock4655e812011-03-31 12:33:34 +0200162 t.start()
163 for (t, output) in threads_and_output:
164 t.join()
Cezary Baginskiccf86432012-04-23 23:55:35 +0200165 output.dump(sys.stdout)
Terence Haddock4655e812011-03-31 12:33:34 +0200166 output.close()
Anthony King2cd1f042014-05-05 21:24:05 +0100167 if len(all_projects) == next(counter):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700168 print('nothing to commit (working directory clean)')
Will Richey63d356f2012-06-21 09:49:59 -0400169
170 if opt.orphans:
171 proj_dirs = set()
172 proj_dirs_parents = set()
173 for project in self.GetProjects(None, missing_ok=True):
174 proj_dirs.add(project.relpath)
175 (head, _tail) = os.path.split(project.relpath)
176 while head != "":
177 proj_dirs_parents.add(head)
178 (head, _tail) = os.path.split(head)
179 proj_dirs.add('.repo')
180
181 class StatusColoring(Coloring):
182 def __init__(self, config):
183 Coloring.__init__(self, config, 'status')
184 self.project = self.printer('header', attr = 'bold')
185 self.untracked = self.printer('untracked', fg = 'red')
186
187 orig_path = os.getcwd()
188 try:
189 os.chdir(self.manifest.topdir)
190
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530191 outstring = io.StringIO()
Will Richey63d356f2012-06-21 09:49:59 -0400192 self._FindOrphans(glob.glob('.*') + \
193 glob.glob('*'), \
194 proj_dirs, proj_dirs_parents, outstring)
195
196 if outstring.buflist:
197 output = StatusColoring(self.manifest.globalConfig)
198 output.project('Objects not within a project (orphans)')
199 output.nl()
200 for entry in outstring.buflist:
201 output.untracked(entry)
202 output.nl()
203 else:
204 print('No orphan files or directories')
205
206 outstring.close()
207
208 finally:
209 # Restore CWD.
210 os.chdir(orig_path)