#!/usr/bin/python # PRE-COMMIT HOOK import sys import re import os def ex(cmd): return os.popen(cmd, 'r') def ex_pipe(text, cmd): w, r = os.popen2(cmd) w.write(text) w.close() return r.read() def no_quotes(v): if "'" in v: raise ValueError("Single quotes are not allowed") class ListDict(dict): def __getitem__(self, key): if not self.has_key(key): self[key] = [] return dict.__getitem__(self, key) class StringDict(dict): def __getitem__(self, key): if not self.has_key(key): return '' return dict.__getitem__(self, key) class Path(object): def __init__(self, transaction, path): self._transaction = transaction self._path = path self._file_st = None self._prop_st = None self._props = {} def set_changes(self, file_st, prop_st): self._file_st = file_st self._prop_st = prop_st def get_props(self): self._props = self._transaction.proplist(self._path) return self._props def get_path(self): return self._path def get_is_dir(self): return self.path.endswith('/') def get_contents(self): return self._transaction.get_contents_of_file(self._path) path = property(get_path) is_dir = property(get_is_dir) props = property(get_props) cat = property(get_contents) class Transaction(object): def __init__(self, transaction, repo_path): no_quotes(transaction) no_quotes(repo_path) self._transaction = transaction self._repo_path = repo_path self._got_changes = False self._paths = {} self._changed_paths = {} self._changes() def _changes(self): # FIXME> this could break filenames that end with whitespace... # FIXME> but do I care? changes = [x.rstrip() for x in ex('svnlook changed -t "%s" "%s"' % (self._transaction, self._repo_path))] changes1 = [] for x in changes: file_st, prop_st, path = x[0], x[1], x[4:] p = Path(self, path) p.set_changes(file_st, prop_st) self._paths[path] = self._changed_paths[path] = p def proplist(self, path): no_quotes(path) # FIXME> Use ad-hoc multithreading to get all the properties faster... propnames = [x.strip() for x in ex("svnlook proplist -t '%s' '%s' '%s'" % ( self._transaction, self._repo_path, path)).readlines()] properties = StringDict() for n in propnames: no_quotes(n) value = ex("svnlook propget -t '%s' '%s' '%s' '%s'"% ( self._transaction, self._repo_path, n, path)).read() properties[n] = value return properties def get_changed_paths(self): return self._changed_paths.values() def get_contents_of_file(self, path): no_quotes(path) return ex("svnlook cat -t '%s' %s %s" % (self._transaction, self._repo_path, path)).read() changed_paths = property(get_changed_paths) def main(): REPOS = sys.argv[1] # the path to this repository TXN = sys.argv[2] # the name of the txn about to be committed tab_re = re.compile('\t') space_tab_re = re.compile(' \t') gnu_no_tab_indent_cmd = \ r'''indent -nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 \ -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -psl -nsc -nsob \ -nut''' def gnu_no_tab_indent(in_text): return ex_pipe(in_text, gnu_no_tab_indent_cmd) # reject_lists = ListDict() no_tabs = intern('Tabs are not allowed') no_space_tabs = intern('Space followed by tab is not allowed') gnu_no_tabs = intern('Must conform to GNU standards, without tabs:\n%s' % gnu_no_tab_indent_cmd) t = Transaction(TXN, REPOS) for path in t.changed_paths: if not path.is_dir: # FIXME> use ad-hoc multithreading to run these checks in # FIXME> parallel... if (path.props['indentation'] == 'no tabs' and tab_re.search(path.cat)): reject_lists[no_tabs].append(path) elif (path.props['indentation'] == 'no space tabs' and space_tab_re.search(path.cat)): reject_lists[no_space_tabs].append(path) elif (path.props['indentation'] == 'gnu no tabs' and path.cat != gnu_no_tab_indent(path.cat)): reject_lists[gnu_no_tabs].append(path) elif (path.props['indentation'] not in ['', 'no tabs', 'no space tabs', 'gnu no tabs']): s = 'Invalid indentation property value: %s' % ( path.props['indentation'].__repr__()) reject_lists[s].append(path) if reject_lists: print >>sys.stderr, ''' ERROR! Some files in your commit are in violation of code indentation standards (set by the "indentation" property per-file). Please fix the code (or change/remove the "indentation" property) and try again. The files in violation are as follows: ''' for reason in reject_lists: print >>sys.stderr, 'Reason:', reason for path in reject_lists[reason]: print >>sys.stderr, ' %s' % (path.path) print >>sys.stderr sys.exit(-1) sys.exit(0) main()