69 _matchall = scmutil.matchall
70 except ImportError:
71 pass
72
73 def repocompat(repo):
74 # Modern mercurial versions use len(repo) and repo[cset_id]; enable those
75 # operations with older versions.
76 t = type(repo)
77 if not getattr(t, '__len__', None):
78 def repolen(self):
79 return self.changelog.count()
80 setattr(t, '__len__', repolen)
81 if not getattr(t, '__getitem__', None):
82 def repoitem(self, arg):
83 return context.changectx(self, arg)
84 setattr(t, '__getitem__', repoitem)
85 # Similarly, use branchmap instead of branchtags; enable it if needed.
86 if not getattr(t, 'branchmap', None):
87 setattr(t, 'branchmap', t.branchtags)
88
89
90 # Configuration-file parsing
91
92 def load_conf(root):
93 cf = { }
94 fn = os.path.join(root, ".jcheck/conf")
95 f = open(fn)
96 try:
97 prop_re = re.compile("\s*(\S+)\s*=\s*(\S+)\s*$")
98 i = 0
99 for ln in f.readlines():
100 i = i + 1
101 ln = ln.strip()
102 if (ln.startswith("#")):
103 continue
104 m = prop_re.match(ln)
105 if not m:
106 raise util.Abort("%s:%d: Invalid configuration syntax: %s"
107 % (fn, i, ln))
108 cf[m.group(1)] = m.group(2)
109 finally:
110 f.close()
111 for pn in ["project"]:
112 if not cf.has_key(pn):
113 raise util.Abort("%s: Missing property: %s" % (fn, pn))
114 return cf
115
116
117 # Author validation
118
119 author_cache = { } ## Should really cache more permanently
120
121 def validate_author(an, pn):
122 if author_cache.has_key(an):
123 return True
124 u = ("http://db.openjdk.java.net/people/%s/projects/%s"
125 % (urllib.quote(an), pn))
126 f = None
127 try:
128 try:
129 f = urllib2.urlopen(u)
130 except urllib2.HTTPError, e:
131 if e.code == 404:
132 return False
133 raise e
134 finally:
135 if f:
136 f.close()
137 author_cache[an] = True
138 return True
139
140
141 # Whitespace and comment validation
142
143 badwhite_re = re.compile("(\t)|([ \t]$)|\r", re.MULTILINE)
144 normext_re = re.compile(".*\.(java|c|h|cpp|hpp)$")
145
146 tag_desc_re = re.compile("Added tag [^ ]+ for changeset [0-9a-f]{12}")
147 tag_re = re.compile("tip$|jdk[4-9](u\d{1,3})?-b\d{2,3}$|hs\d\d(\.\d{1,2})?-b\d\d$")
148
149 def badwhite_what(m):
150 if m.group(1):
151 return "Tab character"
152 if m.group(2):
153 return "Trailing whitespace"
154 return "Carriage return (^M)"
155
156 base_addr_pat = "[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}"
157 addr_pat = ("(" + base_addr_pat + ")"
158 + "|(([-_a-zA-Z0-9][-_ a-zA-Z0-9]+) +<" + base_addr_pat + ">)")
159
160 bug_ident = re.compile("(([A-Z][A-Z0-9]+-)?[0-9]+):")
161 bug_check = re.compile("([0-9]{7}): \S.*$")
162 sum_ident = re.compile("Summary:")
163 sum_check = re.compile("Summary: \S.*")
164 rev_ident = re.compile("Reviewed-by:")
165 rev_check = re.compile("Reviewed-by: (([a-z0-9]+)(, [a-z0-9]+)*$)")
166 con_ident = re.compile("Contributed-by:")
167 con_check = re.compile("Contributed-by: ((" + addr_pat + ")(, (" + addr_pat + "))*)$")
168
169 def bug_validate(ch, ctx, m, pn):
170 bs = m.group(1)
171 if not (bs[0] in ['1','2','4','5','6','7','8']):
172 ch.error(ctx, "Invalid bugid: %s" % bs)
173 b = int(bs)
174 if b in ch.cs_bugids:
232 opts = { 'rev' : ['0:tip'] }
233 try:
234 nop = lambda c, fns: None
235 iter = cmdutil.walkchangerevs(repo, _matchall(repo), opts, nop)
236 for ctx in iter:
237 addbugids(bugids, ctx)
238 except (AttributeError, TypeError):
239 # AttributeError: matchall does not exist in hg < 1.1
240 # TypeError: walkchangerevs args differ in hg <= 1.3.1
241 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
242 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, [], get, opts)
243 for st, rev, fns in changeiter:
244 if st == 'add':
245 node = repo.changelog.node(rev)
246 addbugids(bugids, context.changectx(repo, node))
247 if ui.debugflag:
248 ui.debug("Bugids: %s\n" % bugids)
249 return bugids
250
251
252
253 # Black/white lists
254 ## The black/white lists should really be in the database
255
256 # Bogus yet historically-accepted changesets,
257 # so that jcheck may evolve
258 #
259 changeset_whitelist = [
260 '31000d79ec713de1e601dc16d74d726edd661ed5',
261 'b7987d19f5122a9f169e568f935b7cdf1a2609f5',
262 'c70a245cad3ad74602aa26b9d8e3d0472f7317c3',
263 'e8e20316458c1cdb85d9733a2e357e438a76a859',
264 'f68325221ce1efe94ab367400a49a8039d9b3db3',
265 '4dfa5d67c44500155ce9ab1e00d0de21bdbb9ee6',
266 '73a4d5be86497baf74c1fc194c9a0dd4e86d3a31', # jdk6/jdk6/jaxp bad comment
267 'a25f15bfd04b46a302b6ca1a298c176344f432dd', # jdk6/jdk6/jdk bad comment
268 'bf87d5af43614d609a5251c43eea44c028500d02', # jdk6/jdk6/jdk bad comment
269 'd77434402021cebc4c25b452db18bbfd2d7ccda1', # jdk6/jdk6/jdk bad comment
270 '931e5f39e365a0d550d79148ff87a7f9e864d2e1', # hotspot dup bug id 7147064
271 'd8abc90163a4b58db407a60cba331ab21c9977e7', # hotspot dup bug id 7147064
272 '45849c62c298aa8426c9e67599e4e35793d8db13', # pubs executable files
346 '3ecd3336c805978a37a933fbeca26c65fbe81432',
347 # hsx/jdk7u/hotspot wrong bugid
348 'f5d8e6d72e23d972db522f7ad4cd3b9b01085466',
349 # jdk8/tl/jdk erroneous push 7152892
350 'da4b0962ad1161dbd84e7daa0fdc706281c456a2',
351 # jdk8/tl/jdk/test/closed erroneous push 7152892
352 '1e69a1ce212c7c4c884f155dd123c936787db273',
353 # jdk9/jdk9/closed bad tag
354 '61fdebb503d79392536b8f502ae215022d1a1f1c',
355 # jdk9/hs-rt/jdk/src/closed dup bugid 8034951
356 'a19596796430761dde87bee9f6616480f1c93678',
357 # jdk9/hs-rt/jdk/test/closed dup bugid 8034951
358 'd2308c9714c94e87a0e60cda314746a5c17dbcc2',
359 # jdk9/client/deploy erroneous push 8041798
360 'fff4ff4fd6f031ab335b44842d69fd125297b5ab',
361 ]
362
363 # Path to file containing additional blacklisted changesets
364 blacklist_file = '/oj/db/hg/blacklist'
365
366
367 # Checker class
368
369 class checker(object):
370
371 def __init__(self, ui, repo, strict, lax):
372 self.ui = ui
373 self.repo = repo
374 self.rv = Pass
375 self.checks = [c for c in checker.__dict__ if c.startswith("c_")]
376 self.checks.sort()
377 self.summarized = False
378 self.repo_bugids = [ ]
379 self.cs_bugids = [ ] # Bugids in current changeset
380 self.cs_author = None # Author of current changeset
381 self.cs_reviewers = [ ] # Reviewers of current changeset
382 self.cs_contributor = None # Contributor of current changeset
383 self.strict = strict
384 self.conf = load_conf(repo.root)
385 self.whitespace_lax = lax and not strict
386 if self.conf.get("whitespace") == "lax":
387 self.whitespace_lax = True
388 self.comments_lax = lax and not strict
389 if self.conf.get("comments") == "lax":
390 self.comments_lax = True
391 self.tags_lax = lax and not strict
392 if self.conf.get("tags") == "lax":
393 self.tags_lax = True
394 self.bugids_allow_dups = self.conf.get("bugids") == "dup"
395 self.bugids_lax = lax and not strict
396 if self.conf.get("bugids") == "lax":
397 self.bugids_lax = True
398 self.bugids_ignore = False
399 if self.conf.get("bugids") == "ignore":
400 self.bugids_ignore = True
401 if not self.bugids_ignore:
402 # only identify bug ids if we are going to use them
403 self.repo_bugids = repo_bugids(ui, repo)
404 self.blacklist = dict.fromkeys(changeset_blacklist)
405 self.read_blacklist(blacklist_file)
406 # hg < 1.0 does not have localrepo.tagtype()
407 self.tagtype = getattr(self.repo, 'tagtype', lambda k: 'global')
408
409 def read_blacklist(self, fname):
410 if not os.path.exists(fname):
411 return
429
430 def error(self, ctx, msg):
431 if self.rv != Fail:
432 self.ui.status("[jcheck %s %s]\n" % (_version, _date))
433 if not self.summarized:
434 if ctx:
435 self.summarize(ctx)
436 else:
437 self.ui.status("\n")
438 self.summarized = True
439 self.ui.status(msg + "\n")
440 self.rv = Fail
441
442 def c_00_author(self, ctx):
443 self.ui.debug("author: %s\n" % ctx.user())
444 if not validate_author(ctx.user(), self.conf["project"]):
445 self.error(ctx, "Invalid changeset author: %s" % ctx.user())
446 self.cs_author = ctx.user()
447
448 def c_01_comment(self, ctx):
449 m = badwhite_re.search(ctx.description())
450 if m:
451 ln = ctx.description().count("\n", 0, m.start()) + 1
452 self.error(ctx, "%s in comment (line %d)" % (badwhite_what(m), ln))
453
454 if is_merge(self.repo, ctx.rev()):
455 if ctx.description() != "Merge":
456 self.error(ctx, ("Invalid comment for merge changeset"
457 + " (must be \"Merge\")"))
458 return
459
460 if tag_desc_re.match(ctx.description()):
461 ## Should check tag itself
462 return
463
464 if ((ctx.rev() == 0 or (ctx.rev() == 1 and self.comments_lax))
465 and ctx.user() == "duke"
466 and ctx.description().startswith("Initial load")):
467 return
468
469 lns = ctx.description().splitlines()
501 self.error(ctx, "Too many %ss" % st.name)
502
503 if not self.cs_contributor and [self.cs_author] == self.cs_reviewers:
504 self.error(ctx, "Self-reviews not permitted")
505 if not self.comments_lax:
506 if (gi == 0 and n > 0):
507 self.error(ctx, "Incomplete comment: Missing bugid line")
508 elif gi == 1 or (gi == 2 and n == 0):
509 self.error(ctx, "Incomplete comment: Missing reviewer attribution")
510 if (i < len(lns)):
511 self.error(ctx, "Extraneous text in comment")
512
513 def c_02_files(self, ctx):
514 changes = self.repo.status(ctx.parents()[0].node(),
515 ctx.node(), None)[:5]
516 modified, added = changes[:2]
517 # ## Skip files that were renamed but not modified
518 files = modified + added
519 if self.ui.debugflag:
520 self.ui.debug("Checking files: %s\n" % ", ".join(files))
521 for f in files:
522 if ctx.rev() == 0:
523 ## This is loathsome
524 if f.startswith("test/java/rmi"): continue
525 if f.startswith("test/com/sun/javadoc/test"): continue
526 if f.startswith("docs/technotes/guides"): continue
527 fx = ctx.filectx(f)
528 if normext_re.match(f) and not self.whitespace_lax:
529 data = fx.data()
530 m = badwhite_re.search(data)
531 if m:
532 ln = data.count("\n", 0, m.start()) + 1
533 self.error(ctx, "%s:%d: %s" % (f, ln, badwhite_what(m)))
534 ## check_file_header(self, fx, data)
535 flags = fx.manifest().flags(f)
536 if 'x' in flags:
537 self.error(ctx, "%s: Executable files not permitted" % f)
538 if 'l' in flags:
539 self.error(ctx, "%s: Symbolic links not permitted" % f)
540
|
69 _matchall = scmutil.matchall
70 except ImportError:
71 pass
72
73 def repocompat(repo):
74 # Modern mercurial versions use len(repo) and repo[cset_id]; enable those
75 # operations with older versions.
76 t = type(repo)
77 if not getattr(t, '__len__', None):
78 def repolen(self):
79 return self.changelog.count()
80 setattr(t, '__len__', repolen)
81 if not getattr(t, '__getitem__', None):
82 def repoitem(self, arg):
83 return context.changectx(self, arg)
84 setattr(t, '__getitem__', repoitem)
85 # Similarly, use branchmap instead of branchtags; enable it if needed.
86 if not getattr(t, 'branchmap', None):
87 setattr(t, 'branchmap', t.branchtags)
88
89 # Configuration-file parsing
90
91 def load_conf(root):
92 cf = { }
93 fn = os.path.join(root, ".jcheck/conf")
94 f = open(fn)
95 try:
96 prop_re = re.compile("\s*(\S+)\s*=\s*(\S+)\s*$")
97 i = 0
98 for ln in f.readlines():
99 i = i + 1
100 ln = ln.strip()
101 if (ln.startswith("#")):
102 continue
103 m = prop_re.match(ln)
104 if not m:
105 raise util.Abort("%s:%d: Invalid configuration syntax: %s"
106 % (fn, i, ln))
107 cf[m.group(1)] = m.group(2)
108 finally:
109 f.close()
110 for pn in ["project"]:
111 if not cf.has_key(pn):
112 raise util.Abort("%s: Missing property: %s" % (fn, pn))
113 return cf
114
115 # Author validation
116
117 author_cache = { } ## Should really cache more permanently
118
119 def validate_author(an, pn):
120 if author_cache.has_key(an):
121 return True
122 u = ("http://db.openjdk.java.net/people/%s/projects/%s"
123 % (urllib.quote(an), pn))
124 f = None
125 try:
126 try:
127 f = urllib2.urlopen(u)
128 except urllib2.HTTPError, e:
129 if e.code == 404:
130 return False
131 raise e
132 finally:
133 if f:
134 f.close()
135 author_cache[an] = True
136 return True
137
138 # Whitespace and comment validation
139
140 badwhite_comment_re = re.compile("(\t)|([ \t]$)|(\r)", re.MULTILINE)
141 badwhite_with_eof_re = re.compile("(\t)|([ \t]$)|(\r)|([^\n]\Z)|(\n\n+\Z)", re.MULTILINE)
142 normext_re = re.compile(".*\.(java|c|h|cpp|hpp)$")
143
144 tag_desc_re = re.compile("Added tag [^ ]+ for changeset [0-9a-f]{12}")
145 tag_re = re.compile("tip$|jdk[4-9](u\d{1,3})?-b\d{2,3}$|hs\d\d(\.\d{1,2})?-b\d\d$")
146
147 def badwhite_what(m):
148 if m.group(1):
149 return "Tab character"
150 if m.group(2):
151 return "Trailing whitespace"
152 if m.group(3):
153 return "Carriage return (^M)"
154 if m.group(4):
155 return "No newline (\\n) at the end of file"
156 return "More than one newline (\\n) at the end of file"
157
158 base_addr_pat = "[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}"
159 addr_pat = ("(" + base_addr_pat + ")"
160 + "|(([-_a-zA-Z0-9][-_ a-zA-Z0-9]+) +<" + base_addr_pat + ">)")
161
162 bug_ident = re.compile("(([A-Z][A-Z0-9]+-)?[0-9]+):")
163 bug_check = re.compile("([0-9]{7}): \S.*$")
164 sum_ident = re.compile("Summary:")
165 sum_check = re.compile("Summary: \S.*")
166 rev_ident = re.compile("Reviewed-by:")
167 rev_check = re.compile("Reviewed-by: (([a-z0-9]+)(, [a-z0-9]+)*$)")
168 con_ident = re.compile("Contributed-by:")
169 con_check = re.compile("Contributed-by: ((" + addr_pat + ")(, (" + addr_pat + "))*)$")
170
171 def bug_validate(ch, ctx, m, pn):
172 bs = m.group(1)
173 if not (bs[0] in ['1','2','4','5','6','7','8']):
174 ch.error(ctx, "Invalid bugid: %s" % bs)
175 b = int(bs)
176 if b in ch.cs_bugids:
234 opts = { 'rev' : ['0:tip'] }
235 try:
236 nop = lambda c, fns: None
237 iter = cmdutil.walkchangerevs(repo, _matchall(repo), opts, nop)
238 for ctx in iter:
239 addbugids(bugids, ctx)
240 except (AttributeError, TypeError):
241 # AttributeError: matchall does not exist in hg < 1.1
242 # TypeError: walkchangerevs args differ in hg <= 1.3.1
243 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
244 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, [], get, opts)
245 for st, rev, fns in changeiter:
246 if st == 'add':
247 node = repo.changelog.node(rev)
248 addbugids(bugids, context.changectx(repo, node))
249 if ui.debugflag:
250 ui.debug("Bugids: %s\n" % bugids)
251 return bugids
252
253
254 # Black/white lists
255 ## The black/white lists should really be in the database
256
257 # Bogus yet historically-accepted changesets,
258 # so that jcheck may evolve
259 #
260 changeset_whitelist = [
261 '31000d79ec713de1e601dc16d74d726edd661ed5',
262 'b7987d19f5122a9f169e568f935b7cdf1a2609f5',
263 'c70a245cad3ad74602aa26b9d8e3d0472f7317c3',
264 'e8e20316458c1cdb85d9733a2e357e438a76a859',
265 'f68325221ce1efe94ab367400a49a8039d9b3db3',
266 '4dfa5d67c44500155ce9ab1e00d0de21bdbb9ee6',
267 '73a4d5be86497baf74c1fc194c9a0dd4e86d3a31', # jdk6/jdk6/jaxp bad comment
268 'a25f15bfd04b46a302b6ca1a298c176344f432dd', # jdk6/jdk6/jdk bad comment
269 'bf87d5af43614d609a5251c43eea44c028500d02', # jdk6/jdk6/jdk bad comment
270 'd77434402021cebc4c25b452db18bbfd2d7ccda1', # jdk6/jdk6/jdk bad comment
271 '931e5f39e365a0d550d79148ff87a7f9e864d2e1', # hotspot dup bug id 7147064
272 'd8abc90163a4b58db407a60cba331ab21c9977e7', # hotspot dup bug id 7147064
273 '45849c62c298aa8426c9e67599e4e35793d8db13', # pubs executable files
347 '3ecd3336c805978a37a933fbeca26c65fbe81432',
348 # hsx/jdk7u/hotspot wrong bugid
349 'f5d8e6d72e23d972db522f7ad4cd3b9b01085466',
350 # jdk8/tl/jdk erroneous push 7152892
351 'da4b0962ad1161dbd84e7daa0fdc706281c456a2',
352 # jdk8/tl/jdk/test/closed erroneous push 7152892
353 '1e69a1ce212c7c4c884f155dd123c936787db273',
354 # jdk9/jdk9/closed bad tag
355 '61fdebb503d79392536b8f502ae215022d1a1f1c',
356 # jdk9/hs-rt/jdk/src/closed dup bugid 8034951
357 'a19596796430761dde87bee9f6616480f1c93678',
358 # jdk9/hs-rt/jdk/test/closed dup bugid 8034951
359 'd2308c9714c94e87a0e60cda314746a5c17dbcc2',
360 # jdk9/client/deploy erroneous push 8041798
361 'fff4ff4fd6f031ab335b44842d69fd125297b5ab',
362 ]
363
364 # Path to file containing additional blacklisted changesets
365 blacklist_file = '/oj/db/hg/blacklist'
366
367 # Checker class
368
369 class checker(object):
370
371 def __init__(self, ui, repo, strict, lax):
372 self.ui = ui
373 self.repo = repo
374 self.rv = Pass
375 self.checks = [c for c in checker.__dict__ if c.startswith("c_")]
376 self.checks.sort()
377 self.summarized = False
378 self.repo_bugids = [ ]
379 self.cs_bugids = [ ] # Bugids in current changeset
380 self.cs_author = None # Author of current changeset
381 self.cs_reviewers = [ ] # Reviewers of current changeset
382 self.cs_contributor = None # Contributor of current changeset
383 self.strict = strict
384 self.conf = load_conf(repo.root)
385 self.whitespace_lax = lax and not strict
386 if self.conf.get("whitespace") == "lax":
387 self.whitespace_lax = True
388 self.comments_lax = lax and not strict
389 if self.conf.get("comments") == "lax":
390 self.comments_lax = True
391 self.tags_lax = lax and not strict
392 # Test if we should check for a correct EOF (i.e. files end with exatly one '\n')
393 # This behaviour is controlled by the 'check_eof' attribute in the conf file.
394 # -1 means to not check for EOF at all.
395 # 0 means to potentially check all the changes for a correct EOF.
396 # any other positive number is interpreted as the revision number or change-
397 # set ID from which on jcheck should start checking for a correct EOF.
398 # If the 'check_eof' attribute is missing, '-1' (i.e. no EOF check) will be assumed.
399 if self.conf.has_key("check_eof"):
400 check_eof_cs = self.conf.get("check_eof")
401 if not check_eof_cs.startswith("-"):
402 try:
403 self.check_eof = repo[check_eof_cs].rev()
404 except:
405 self.check_eof = -1
406 else:
407 self.check_eof = -1
408 else:
409 self.check_eof = -1
410 if self.conf.get("tags") == "lax":
411 self.tags_lax = True
412 self.bugids_allow_dups = self.conf.get("bugids") == "dup"
413 self.bugids_lax = lax and not strict
414 if self.conf.get("bugids") == "lax":
415 self.bugids_lax = True
416 self.bugids_ignore = False
417 if self.conf.get("bugids") == "ignore":
418 self.bugids_ignore = True
419 if not self.bugids_ignore:
420 # only identify bug ids if we are going to use them
421 self.repo_bugids = repo_bugids(ui, repo)
422 self.blacklist = dict.fromkeys(changeset_blacklist)
423 self.read_blacklist(blacklist_file)
424 # hg < 1.0 does not have localrepo.tagtype()
425 self.tagtype = getattr(self.repo, 'tagtype', lambda k: 'global')
426
427 def read_blacklist(self, fname):
428 if not os.path.exists(fname):
429 return
447
448 def error(self, ctx, msg):
449 if self.rv != Fail:
450 self.ui.status("[jcheck %s %s]\n" % (_version, _date))
451 if not self.summarized:
452 if ctx:
453 self.summarize(ctx)
454 else:
455 self.ui.status("\n")
456 self.summarized = True
457 self.ui.status(msg + "\n")
458 self.rv = Fail
459
460 def c_00_author(self, ctx):
461 self.ui.debug("author: %s\n" % ctx.user())
462 if not validate_author(ctx.user(), self.conf["project"]):
463 self.error(ctx, "Invalid changeset author: %s" % ctx.user())
464 self.cs_author = ctx.user()
465
466 def c_01_comment(self, ctx):
467 m = badwhite_comment_re.search(ctx.description())
468 if m:
469 ln = ctx.description().count("\n", 0, m.start()) + 1
470 self.error(ctx, "%s in comment (line %d)" % (badwhite_what(m), ln))
471
472 if is_merge(self.repo, ctx.rev()):
473 if ctx.description() != "Merge":
474 self.error(ctx, ("Invalid comment for merge changeset"
475 + " (must be \"Merge\")"))
476 return
477
478 if tag_desc_re.match(ctx.description()):
479 ## Should check tag itself
480 return
481
482 if ((ctx.rev() == 0 or (ctx.rev() == 1 and self.comments_lax))
483 and ctx.user() == "duke"
484 and ctx.description().startswith("Initial load")):
485 return
486
487 lns = ctx.description().splitlines()
519 self.error(ctx, "Too many %ss" % st.name)
520
521 if not self.cs_contributor and [self.cs_author] == self.cs_reviewers:
522 self.error(ctx, "Self-reviews not permitted")
523 if not self.comments_lax:
524 if (gi == 0 and n > 0):
525 self.error(ctx, "Incomplete comment: Missing bugid line")
526 elif gi == 1 or (gi == 2 and n == 0):
527 self.error(ctx, "Incomplete comment: Missing reviewer attribution")
528 if (i < len(lns)):
529 self.error(ctx, "Extraneous text in comment")
530
531 def c_02_files(self, ctx):
532 changes = self.repo.status(ctx.parents()[0].node(),
533 ctx.node(), None)[:5]
534 modified, added = changes[:2]
535 # ## Skip files that were renamed but not modified
536 files = modified + added
537 if self.ui.debugflag:
538 self.ui.debug("Checking files: %s\n" % ", ".join(files))
539 if self.check_eof != -1 and ctx.rev() >= self.check_eof:
540 badwhite_re = badwhite_with_eof_re
541 else:
542 badwhite_re = badwhite_comment_re
543 for f in files:
544 if ctx.rev() == 0:
545 ## This is loathsome
546 if f.startswith("test/java/rmi"): continue
547 if f.startswith("test/com/sun/javadoc/test"): continue
548 if f.startswith("docs/technotes/guides"): continue
549 fx = ctx.filectx(f)
550 if normext_re.match(f) and not self.whitespace_lax:
551 data = fx.data()
552 m = badwhite_re.search(data)
553 if m:
554 ln = data.count("\n", 0, m.start()) + 1
555 self.error(ctx, "%s:%d: %s" % (f, ln, badwhite_what(m)))
556 ## check_file_header(self, fx, data)
557 flags = fx.manifest().flags(f)
558 if 'x' in flags:
559 self.error(ctx, "%s: Executable files not permitted" % f)
560 if 'l' in flags:
561 self.error(ctx, "%s: Symbolic links not permitted" % f)
562
|