Print this page
NEX-5237 git-pbchk fails to detect parent branch
Reviewed by: Dan Fields <dan.fields@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/tools/scripts/git-pbchk.py
+++ new/usr/src/tools/scripts/git-pbchk.py
1 1 #!@PYTHON@
2 2 #
3 3 # This program is free software; you can redistribute it and/or modify
4 4 # it under the terms of the GNU General Public License version 2
5 5 # as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
|
↓ open down ↓ |
11 lines elided |
↑ open up ↑ |
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program; if not, write to the Free Software
14 14 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 15 #
16 16
17 17 #
18 18 # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
19 19 # Copyright 2008, 2012 Richard Lowe
20 20 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
21 21 # Copyright (c) 2014, Joyent, Inc.
22 +# Copyright 2016 Nexenta Systems, Inc.
22 23 # Copyright (c) 2015, 2016 by Delphix. All rights reserved.
23 24 # Copyright 2016 Nexenta Systems, Inc.
24 25 #
25 26
26 27 import getopt
27 28 import os
28 29 import re
29 30 import subprocess
30 31 import sys
31 32 import tempfile
32 33
33 34 from cStringIO import StringIO
34 35
35 36 #
36 37 # Adjust the load path based on our location and the version of python into
37 38 # which it is being loaded. This assumes the normal onbld directory
38 39 # structure, where we are in bin/ and the modules are in
39 40 # lib/python(version)?/onbld/Scm/. If that changes so too must this.
40 41 #
41 42 sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib",
42 43 "python%d.%d" % sys.version_info[:2]))
43 44
44 45 #
45 46 # Add the relative path to usr/src/tools to the load path, such that when run
46 47 # from the source tree we use the modules also within the source tree.
47 48 #
48 49 sys.path.insert(2, os.path.join(os.path.dirname(__file__), ".."))
49 50
50 51 from onbld.Scm import Ignore
51 52 from onbld.Checks import Comments, Copyright, CStyle, HdrChk
52 53 from onbld.Checks import JStyle, Keywords, ManLint, Mapfile, SpellCheck
53 54
54 55
55 56 class GitError(Exception):
56 57 pass
57 58
58 59 def git(command):
59 60 """Run a command and return a stream containing its stdout (and write its
60 61 stderr to its stdout)"""
61 62
62 63 if type(command) != list:
63 64 command = command.split()
64 65
65 66 command = ["git"] + command
66 67
67 68 try:
68 69 tmpfile = tempfile.TemporaryFile(prefix="git-nits")
69 70 except EnvironmentError, e:
70 71 raise GitError("Could not create temporary file: %s\n" % e)
71 72
72 73 try:
73 74 p = subprocess.Popen(command,
74 75 stdout=tmpfile,
75 76 stderr=subprocess.PIPE)
76 77 except OSError, e:
77 78 raise GitError("could not execute %s: %s\n" % (command, e))
78 79
79 80 err = p.wait()
80 81 if err != 0:
81 82 raise GitError(p.stderr.read())
82 83
83 84 tmpfile.seek(0)
84 85 return tmpfile
85 86
86 87
87 88 def git_root():
88 89 """Return the root of the current git workspace"""
89 90
90 91 p = git('rev-parse --git-dir')
91 92
92 93 if not p:
93 94 sys.stderr.write("Failed finding git workspace\n")
94 95 sys.exit(err)
95 96
96 97 return os.path.abspath(os.path.join(p.readlines()[0],
97 98 os.path.pardir))
98 99
99 100
100 101 def git_branch():
101 102 """Return the current git branch"""
102 103
103 104 p = git('branch')
104 105
105 106 if not p:
106 107 sys.stderr.write("Failed finding git branch\n")
107 108 sys.exit(err)
108 109
109 110 for elt in p:
110 111 if elt[0] == '*':
111 112 if elt.endswith('(no branch)'):
112 113 return None
113 114 return elt.split()[1]
114 115
115 116
116 117 def git_parent_branch(branch):
117 118 """Return the parent of the current git branch.
118 119
119 120 If this branch tracks a remote branch, return the remote branch which is
120 121 tracked. If not, default to origin/master."""
121 122
122 123 if not branch:
123 124 return None
124 125
125 126 p = git(["for-each-ref", "--format=%(refname:short) %(upstream:short)",
126 127 "refs/heads/"])
127 128
128 129 if not p:
129 130 sys.stderr.write("Failed finding git parent branch\n")
130 131 sys.exit(err)
131 132
132 133 for line in p:
133 134 # Git 1.7 will leave a ' ' trailing any non-tracking branch
134 135 if ' ' in line and not line.endswith(' \n'):
135 136 local, remote = line.split()
136 137 if local == branch:
137 138 return remote
138 139 return 'origin/master'
139 140
140 141
141 142 def git_comments(parent):
142 143 """Return a list of any checkin comments on this git branch"""
143 144
144 145 p = git('log --pretty=tformat:%%B:SEP: %s..' % parent)
145 146
146 147 if not p:
147 148 sys.stderr.write("Failed getting git comments\n")
148 149 sys.exit(err)
149 150
150 151 return [x.strip() for x in p.readlines() if x != ':SEP:\n']
151 152
152 153
153 154 def git_file_list(parent, paths=None):
154 155 """Return the set of files which have ever changed on this branch.
155 156
156 157 NB: This includes files which no longer exist, or no longer actually
157 158 differ."""
158 159
159 160 p = git("log --name-only --pretty=format: %s.. %s" %
160 161 (parent, ' '.join(paths)))
161 162
162 163 if not p:
163 164 sys.stderr.write("Failed building file-list from git\n")
164 165 sys.exit(err)
165 166
166 167 ret = set()
167 168 for fname in p:
168 169 if fname and not fname.isspace() and fname not in ret:
169 170 ret.add(fname.strip())
170 171
171 172 return ret
172 173
173 174
174 175 def not_check(root, cmd):
175 176 """Return a function which returns True if a file given as an argument
176 177 should be excluded from the check named by 'cmd'"""
177 178
178 179 ignorefiles = filter(os.path.exists,
179 180 [os.path.join(root, ".git", "%s.NOT" % cmd),
180 181 os.path.join(root, "exception_lists", cmd)])
181 182 return Ignore.ignore(root, ignorefiles)
182 183
183 184
184 185 def gen_files(root, parent, paths, exclude):
185 186 """Return a function producing file names, relative to the current
186 187 directory, of any file changed on this branch (limited to 'paths' if
187 188 requested), and excluding files for which exclude returns a true value """
188 189
189 190 # Taken entirely from Python 2.6's os.path.relpath which we would use if we
190 191 # could.
191 192 def relpath(path, here):
192 193 c = os.path.abspath(os.path.join(root, path)).split(os.path.sep)
193 194 s = os.path.abspath(here).split(os.path.sep)
194 195 l = len(os.path.commonprefix((s, c)))
195 196 return os.path.join(*[os.path.pardir] * (len(s)-l) + c[l:])
196 197
197 198 def ret(select=None):
198 199 if not select:
199 200 select = lambda x: True
200 201
201 202 for f in git_file_list(parent, paths):
202 203 f = relpath(f, '.')
203 204 try:
204 205 res = git("diff %s HEAD %s" % (parent, f))
205 206 except GitError, e:
206 207 # This ignores all the errors that can be thrown. Usually, this means
207 208 # that git returned non-zero because the file doesn't exist, but it
208 209 # could also fail if git can't create a new file or it can't be
209 210 # executed. Such errors are 1) unlikely, and 2) will be caught by other
210 211 # invocations of git().
211 212 continue
212 213 empty = not res.readline()
213 214 if (os.path.isfile(f) and not empty and select(f) and not exclude(f)):
214 215 yield f
215 216 return ret
216 217
217 218
218 219 def comchk(root, parent, flist, output):
219 220 output.write("Comments:\n")
220 221
221 222 return Comments.comchk(git_comments(parent), check_db=True,
222 223 output=output)
223 224
224 225
225 226 def mapfilechk(root, parent, flist, output):
226 227 ret = 0
227 228
228 229 # We are interested in examining any file that has the following
229 230 # in its final path segment:
230 231 # - Contains the word 'mapfile'
231 232 # - Begins with 'map.'
232 233 # - Ends with '.map'
233 234 # We don't want to match unless these things occur in final path segment
234 235 # because directory names with these strings don't indicate a mapfile.
235 236 # We also ignore files with suffixes that tell us that the files
236 237 # are not mapfiles.
237 238 MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$',
238 239 re.IGNORECASE)
239 240 NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE)
240 241
241 242 output.write("Mapfile comments:\n")
242 243
243 244 for f in flist(lambda x: MapfileRE.match(x) and not
244 245 NotMapSuffixRE.match(x)):
245 246 fh = open(f, 'r')
246 247 ret |= Mapfile.mapfilechk(fh, output=output)
247 248 fh.close()
248 249 return ret
249 250
250 251
251 252 def copyright(root, parent, flist, output):
252 253 ret = 0
253 254 output.write("Copyrights:\n")
254 255 for f in flist():
255 256 fh = open(f, 'r')
256 257 ret |= Copyright.copyright(fh, output=output)
257 258 fh.close()
258 259 return ret
259 260
260 261
261 262 def hdrchk(root, parent, flist, output):
262 263 ret = 0
263 264 output.write("Header format:\n")
264 265 for f in flist(lambda x: x.endswith('.h')):
265 266 fh = open(f, 'r')
266 267 ret |= HdrChk.hdrchk(fh, lenient=True, output=output)
267 268 fh.close()
268 269 return ret
269 270
270 271
271 272 def cstyle(root, parent, flist, output):
272 273 ret = 0
273 274 output.write("C style:\n")
274 275 for f in flist(lambda x: x.endswith('.c') or x.endswith('.h')):
275 276 fh = open(f, 'r')
276 277 ret |= CStyle.cstyle(fh, output=output, picky=True,
277 278 check_posix_types=True,
278 279 check_continuation=True)
279 280 fh.close()
280 281 return ret
281 282
282 283
283 284 def jstyle(root, parent, flist, output):
284 285 ret = 0
285 286 output.write("Java style:\n")
286 287 for f in flist(lambda x: x.endswith('.java')):
287 288 fh = open(f, 'r')
288 289 ret |= JStyle.jstyle(fh, output=output, picky=True)
289 290 fh.close()
290 291 return ret
291 292
292 293
293 294 def manlint(root, parent, flist, output):
294 295 ret = 0
295 296 output.write("Man page format/spelling:\n")
296 297 ManfileRE = re.compile(r'.*\.[0-9][a-z]*$', re.IGNORECASE)
297 298 for f in flist(lambda x: ManfileRE.match(x)):
298 299 fh = open(f, 'r')
299 300 ret |= ManLint.manlint(fh, output=output, picky=True)
300 301 ret |= SpellCheck.spellcheck(fh, output=output)
301 302 fh.close()
302 303 return ret
303 304
304 305 def keywords(root, parent, flist, output):
305 306 ret = 0
306 307 output.write("SCCS Keywords:\n")
307 308 for f in flist():
308 309 fh = open(f, 'r')
309 310 ret |= Keywords.keywords(fh, output=output)
310 311 fh.close()
311 312 return ret
312 313
313 314
314 315 def run_checks(root, parent, cmds, paths='', opts={}):
315 316 """Run the checks given in 'cmds', expected to have well-known signatures,
316 317 and report results for any which fail.
317 318
318 319 Return failure if any of them did.
319 320
320 321 NB: the function name of the commands passed in is used to name the NOT
321 322 file which excepts files from them."""
322 323
323 324 ret = 0
324 325
325 326 for cmd in cmds:
326 327 s = StringIO()
327 328
328 329 exclude = not_check(root, cmd.func_name)
329 330 result = cmd(root, parent, gen_files(root, parent, paths, exclude),
330 331 output=s)
331 332 ret |= result
332 333
333 334 if result != 0:
334 335 print s.getvalue()
335 336
336 337 return ret
337 338
338 339
339 340 def nits(root, parent, paths):
340 341 cmds = [copyright,
341 342 cstyle,
342 343 hdrchk,
343 344 jstyle,
344 345 keywords,
345 346 manlint,
346 347 mapfilechk]
347 348 run_checks(root, parent, cmds, paths)
348 349
349 350
350 351 def pbchk(root, parent, paths):
351 352 cmds = [comchk,
352 353 copyright,
353 354 cstyle,
354 355 hdrchk,
355 356 jstyle,
356 357 keywords,
357 358 manlint,
358 359 mapfilechk]
359 360 run_checks(root, parent, cmds)
360 361
361 362
362 363 def main(cmd, args):
363 364 parent_branch = None
364 365
365 366 try:
366 367 opts, args = getopt.getopt(args, 'b:')
367 368 except getopt.GetoptError, e:
368 369 sys.stderr.write(str(e) + '\n')
369 370 sys.stderr.write("Usage: %s [-b branch] [path...]\n" % cmd)
370 371 sys.exit(1)
371 372
372 373 for opt, arg in opts:
373 374 if opt == '-b':
374 375 parent_branch = arg
375 376
376 377 if not parent_branch:
377 378 parent_branch = git_parent_branch(git_branch())
378 379
379 380 func = nits
380 381 if cmd == 'git-pbchk':
381 382 func = pbchk
382 383 if args:
383 384 sys.stderr.write("only complete workspaces may be pbchk'd\n");
384 385 sys.exit(1)
385 386
386 387 func(git_root(), parent_branch, args)
387 388
388 389 if __name__ == '__main__':
389 390 try:
390 391 main(os.path.basename(sys.argv[0]), sys.argv[1:])
391 392 except GitError, e:
392 393 sys.stderr.write("failed to run git:\n %s\n" % str(e))
393 394 sys.exit(1)
|
↓ open down ↓ |
362 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX