xref: /illumos-gate/usr/src/tools/scripts/git-pbchk.py (revision 42a3762d0138d6dd0c0f96964be98d0b21ab6bef)
19f923083SAlexander Pyhalov#!@PYTHON@
28bcea973SRichard Lowe#
38bcea973SRichard Lowe#  This program is free software; you can redistribute it and/or modify
48bcea973SRichard Lowe#  it under the terms of the GNU General Public License version 2
58bcea973SRichard Lowe#  as published by the Free Software Foundation.
68bcea973SRichard Lowe#
78bcea973SRichard Lowe#  This program is distributed in the hope that it will be useful,
88bcea973SRichard Lowe#  but WITHOUT ANY WARRANTY; without even the implied warranty of
98bcea973SRichard Lowe#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
108bcea973SRichard Lowe#  GNU General Public License for more details.
118bcea973SRichard Lowe#
128bcea973SRichard Lowe#  You should have received a copy of the GNU General Public License
138bcea973SRichard Lowe#  along with this program; if not, write to the Free Software
148bcea973SRichard Lowe#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
158bcea973SRichard Lowe#
168bcea973SRichard Lowe
178bcea973SRichard Lowe#
188bcea973SRichard Lowe# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
198bcea973SRichard Lowe# Copyright 2008, 2012 Richard Lowe
2095c635efSGarrett D'Amore# Copyright 2014 Garrett D'Amore <garrett@damore.org>
2193d2a904SPaul Dagnelie# Copyright (c) 2015, 2016 by Delphix. All rights reserved.
2228e2b3adSHans Rosenfeld# Copyright 2016 Nexenta Systems, Inc.
23eabe844aSJohn Levon# Copyright 2018 Joyent, Inc.
248bcea973SRichard Lowe#
258bcea973SRichard Lowe
268bcea973SRichard Loweimport getopt
278bcea973SRichard Loweimport os
288bcea973SRichard Loweimport re
298bcea973SRichard Loweimport subprocess
308bcea973SRichard Loweimport sys
31ff50e8e5SRichard Loweimport tempfile
328bcea973SRichard Lowe
338bcea973SRichard Lowefrom cStringIO import StringIO
348bcea973SRichard Lowe
358bcea973SRichard Lowe#
368bcea973SRichard Lowe# Adjust the load path based on our location and the version of python into
378bcea973SRichard Lowe# which it is being loaded.  This assumes the normal onbld directory
388bcea973SRichard Lowe# structure, where we are in bin/ and the modules are in
398bcea973SRichard Lowe# lib/python(version)?/onbld/Scm/.  If that changes so too must this.
408bcea973SRichard Lowe#
418bcea973SRichard Lowesys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib",
428bcea973SRichard Lowe                                "python%d.%d" % sys.version_info[:2]))
438bcea973SRichard Lowe
448bcea973SRichard Lowe#
458bcea973SRichard Lowe# Add the relative path to usr/src/tools to the load path, such that when run
468bcea973SRichard Lowe# from the source tree we use the modules also within the source tree.
478bcea973SRichard Lowe#
488bcea973SRichard Lowesys.path.insert(2, os.path.join(os.path.dirname(__file__), ".."))
498bcea973SRichard Lowe
50e5587435SJoshua M. Clulowfrom onbld.Scm import Ignore
514ff15898SGordon Rossfrom onbld.Checks import Comments, Copyright, CStyle, HdrChk, WsCheck
5271af3be3SCody Peter Mellofrom onbld.Checks import JStyle, Keywords, ManLint, Mapfile, SpellCheck
538bcea973SRichard Lowe
548bcea973SRichard Lowe
558bcea973SRichard Loweclass GitError(Exception):
568bcea973SRichard Lowe    pass
578bcea973SRichard Lowe
588bcea973SRichard Lowedef git(command):
598bcea973SRichard Lowe    """Run a command and return a stream containing its stdout (and write its
608bcea973SRichard Lowe    stderr to its stdout)"""
618bcea973SRichard Lowe
628bcea973SRichard Lowe    if type(command) != list:
638bcea973SRichard Lowe        command = command.split()
648bcea973SRichard Lowe
658bcea973SRichard Lowe    command = ["git"] + command
668bcea973SRichard Lowe
67ff50e8e5SRichard Lowe    try:
68ff50e8e5SRichard Lowe        tmpfile = tempfile.TemporaryFile(prefix="git-nits")
69ff50e8e5SRichard Lowe    except EnvironmentError, e:
70ff50e8e5SRichard Lowe        raise GitError("Could not create temporary file: %s\n" % e)
71ff50e8e5SRichard Lowe
72ff50e8e5SRichard Lowe    try:
738bcea973SRichard Lowe        p = subprocess.Popen(command,
74ff50e8e5SRichard Lowe                             stdout=tmpfile,
75380fd671SMatthew Ahrens                             stderr=subprocess.PIPE)
76ff50e8e5SRichard Lowe    except OSError, e:
77709afb1dSDillon Amburgey        raise GitError("could not execute %s: %s\n" % (command, e))
788bcea973SRichard Lowe
798bcea973SRichard Lowe    err = p.wait()
808bcea973SRichard Lowe    if err != 0:
81380fd671SMatthew Ahrens        raise GitError(p.stderr.read())
828bcea973SRichard Lowe
83ff50e8e5SRichard Lowe    tmpfile.seek(0)
84ff50e8e5SRichard Lowe    return tmpfile
858bcea973SRichard Lowe
868bcea973SRichard Lowe
878bcea973SRichard Lowedef git_root():
888bcea973SRichard Lowe    """Return the root of the current git workspace"""
898bcea973SRichard Lowe
908bcea973SRichard Lowe    p = git('rev-parse --git-dir')
918bcea973SRichard Lowe
928bcea973SRichard Lowe    if not p:
938bcea973SRichard Lowe        sys.stderr.write("Failed finding git workspace\n")
948bcea973SRichard Lowe        sys.exit(err)
958bcea973SRichard Lowe
968bcea973SRichard Lowe    return os.path.abspath(os.path.join(p.readlines()[0],
978bcea973SRichard Lowe                                        os.path.pardir))
988bcea973SRichard Lowe
998bcea973SRichard Lowe
1008bcea973SRichard Lowedef git_branch():
1018bcea973SRichard Lowe    """Return the current git branch"""
1028bcea973SRichard Lowe
1038bcea973SRichard Lowe    p = git('branch')
1048bcea973SRichard Lowe
1058bcea973SRichard Lowe    if not p:
1068bcea973SRichard Lowe        sys.stderr.write("Failed finding git branch\n")
1078bcea973SRichard Lowe        sys.exit(err)
1088bcea973SRichard Lowe
1098bcea973SRichard Lowe    for elt in p:
1108bcea973SRichard Lowe        if elt[0] == '*':
1118bcea973SRichard Lowe            if elt.endswith('(no branch)'):
1128bcea973SRichard Lowe                return None
1138bcea973SRichard Lowe            return elt.split()[1]
1148bcea973SRichard Lowe
1158bcea973SRichard Lowe
1168bcea973SRichard Lowedef git_parent_branch(branch):
1178bcea973SRichard Lowe    """Return the parent of the current git branch.
1188bcea973SRichard Lowe
1198bcea973SRichard Lowe    If this branch tracks a remote branch, return the remote branch which is
1208bcea973SRichard Lowe    tracked.  If not, default to origin/master."""
1218bcea973SRichard Lowe
1228bcea973SRichard Lowe    if not branch:
1238bcea973SRichard Lowe        return None
1248bcea973SRichard Lowe
12528e2b3adSHans Rosenfeld    p = git(["for-each-ref", "--format=%(refname:short) %(upstream:short)",
12628e2b3adSHans Rosenfeld            "refs/heads/"])
1278bcea973SRichard Lowe
1288bcea973SRichard Lowe    if not p:
1298bcea973SRichard Lowe        sys.stderr.write("Failed finding git parent branch\n")
1308bcea973SRichard Lowe        sys.exit(err)
1318bcea973SRichard Lowe
1328bcea973SRichard Lowe    for line in p:
1338bcea973SRichard Lowe        # Git 1.7 will leave a ' ' trailing any non-tracking branch
1348bcea973SRichard Lowe        if ' ' in line and not line.endswith(' \n'):
1358bcea973SRichard Lowe            local, remote = line.split()
1368bcea973SRichard Lowe            if local == branch:
1378bcea973SRichard Lowe                return remote
1388bcea973SRichard Lowe    return 'origin/master'
1398bcea973SRichard Lowe
1408bcea973SRichard Lowe
1418bcea973SRichard Lowedef git_comments(parent):
1428bcea973SRichard Lowe    """Return a list of any checkin comments on this git branch"""
1438bcea973SRichard Lowe
14427495383SRichard Lowe    p = git('log --pretty=tformat:%%B:SEP: %s..' % parent)
1458bcea973SRichard Lowe
1468bcea973SRichard Lowe    if not p:
1478bcea973SRichard Lowe        sys.stderr.write("Failed getting git comments\n")
1488bcea973SRichard Lowe        sys.exit(err)
1498bcea973SRichard Lowe
15027495383SRichard Lowe    return [x.strip() for x in p.readlines() if x != ':SEP:\n']
1518bcea973SRichard Lowe
1528bcea973SRichard Lowe
1538bcea973SRichard Lowedef git_file_list(parent, paths=None):
1548bcea973SRichard Lowe    """Return the set of files which have ever changed on this branch.
1558bcea973SRichard Lowe
1568bcea973SRichard Lowe    NB: This includes files which no longer exist, or no longer actually
1578bcea973SRichard Lowe    differ."""
1588bcea973SRichard Lowe
1598bcea973SRichard Lowe    p = git("log --name-only --pretty=format: %s.. %s" %
1608bcea973SRichard Lowe             (parent, ' '.join(paths)))
1618bcea973SRichard Lowe
1628bcea973SRichard Lowe    if not p:
1638bcea973SRichard Lowe        sys.stderr.write("Failed building file-list from git\n")
1648bcea973SRichard Lowe        sys.exit(err)
1658bcea973SRichard Lowe
1668bcea973SRichard Lowe    ret = set()
1678bcea973SRichard Lowe    for fname in p:
16893d2a904SPaul Dagnelie        if fname and not fname.isspace() and fname not in ret:
1698bcea973SRichard Lowe            ret.add(fname.strip())
1708bcea973SRichard Lowe
1718bcea973SRichard Lowe    return ret
1728bcea973SRichard Lowe
1738bcea973SRichard Lowe
1748bcea973SRichard Lowedef not_check(root, cmd):
1758bcea973SRichard Lowe    """Return a function which returns True if a file given as an argument
1768bcea973SRichard Lowe    should be excluded from the check named by 'cmd'"""
1778bcea973SRichard Lowe
1788bcea973SRichard Lowe    ignorefiles = filter(os.path.exists,
1798bcea973SRichard Lowe                         [os.path.join(root, ".git", "%s.NOT" % cmd),
1808bcea973SRichard Lowe                          os.path.join(root, "exception_lists", cmd)])
181e5587435SJoshua M. Clulow    return Ignore.ignore(root, ignorefiles)
1828bcea973SRichard Lowe
1838bcea973SRichard Lowe
1848bcea973SRichard Lowedef gen_files(root, parent, paths, exclude):
1858bcea973SRichard Lowe    """Return a function producing file names, relative to the current
1868bcea973SRichard Lowe    directory, of any file changed on this branch (limited to 'paths' if
1878bcea973SRichard Lowe    requested), and excluding files for which exclude returns a true value """
1888bcea973SRichard Lowe
1898bcea973SRichard Lowe    # Taken entirely from Python 2.6's os.path.relpath which we would use if we
1908bcea973SRichard Lowe    # could.
1918bcea973SRichard Lowe    def relpath(path, here):
1928bcea973SRichard Lowe        c = os.path.abspath(os.path.join(root, path)).split(os.path.sep)
1938bcea973SRichard Lowe        s = os.path.abspath(here).split(os.path.sep)
1948bcea973SRichard Lowe        l = len(os.path.commonprefix((s, c)))
1958bcea973SRichard Lowe        return os.path.join(*[os.path.pardir] * (len(s)-l) + c[l:])
1968bcea973SRichard Lowe
1978bcea973SRichard Lowe    def ret(select=None):
1988bcea973SRichard Lowe        if not select:
1998bcea973SRichard Lowe            select = lambda x: True
2008bcea973SRichard Lowe
20138e36c53SJohn Levon        for abspath in git_file_list(parent, paths):
20238e36c53SJohn Levon            path = relpath(abspath, '.')
20393d2a904SPaul Dagnelie            try:
20438e36c53SJohn Levon                res = git("diff %s HEAD %s" % (parent, path))
20593d2a904SPaul Dagnelie            except GitError, e:
20638e36c53SJohn Levon                # This ignores all the errors that can be thrown. Usually, this
20738e36c53SJohn Levon                # means that git returned non-zero because the file doesn't
20838e36c53SJohn Levon                # exist, but it could also fail if git can't create a new file
20938e36c53SJohn Levon                # or it can't be executed.  Such errors are 1) unlikely, and 2)
21038e36c53SJohn Levon                # will be caught by other invocations of git().
21193d2a904SPaul Dagnelie                continue
21293d2a904SPaul Dagnelie            empty = not res.readline()
21338e36c53SJohn Levon            if (os.path.isfile(path) and not empty and
21438e36c53SJohn Levon                select(path) and not exclude(abspath)):
21538e36c53SJohn Levon                yield path
2168bcea973SRichard Lowe    return ret
2178bcea973SRichard Lowe
2188bcea973SRichard Lowe
2198bcea973SRichard Lowedef comchk(root, parent, flist, output):
2208bcea973SRichard Lowe    output.write("Comments:\n")
2218bcea973SRichard Lowe
2228bcea973SRichard Lowe    return Comments.comchk(git_comments(parent), check_db=True,
2238bcea973SRichard Lowe                           output=output)
2248bcea973SRichard Lowe
2258bcea973SRichard Lowe
2268bcea973SRichard Lowedef mapfilechk(root, parent, flist, output):
2278bcea973SRichard Lowe    ret = 0
2288bcea973SRichard Lowe
2298bcea973SRichard Lowe    # We are interested in examining any file that has the following
2308bcea973SRichard Lowe    # in its final path segment:
2318bcea973SRichard Lowe    #    - Contains the word 'mapfile'
2328bcea973SRichard Lowe    #    - Begins with 'map.'
2338bcea973SRichard Lowe    #    - Ends with '.map'
2348bcea973SRichard Lowe    # We don't want to match unless these things occur in final path segment
2358bcea973SRichard Lowe    # because directory names with these strings don't indicate a mapfile.
2368bcea973SRichard Lowe    # We also ignore files with suffixes that tell us that the files
2378bcea973SRichard Lowe    # are not mapfiles.
2388bcea973SRichard Lowe    MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$',
2398bcea973SRichard Lowe        re.IGNORECASE)
2408bcea973SRichard Lowe    NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE)
2418bcea973SRichard Lowe
2428bcea973SRichard Lowe    output.write("Mapfile comments:\n")
2438bcea973SRichard Lowe
2448bcea973SRichard Lowe    for f in flist(lambda x: MapfileRE.match(x) and not
2458bcea973SRichard Lowe                   NotMapSuffixRE.match(x)):
2468bcea973SRichard Lowe        fh = open(f, 'r')
2478bcea973SRichard Lowe        ret |= Mapfile.mapfilechk(fh, output=output)
2488bcea973SRichard Lowe        fh.close()
2498bcea973SRichard Lowe    return ret
2508bcea973SRichard Lowe
2518bcea973SRichard Lowe
2528bcea973SRichard Lowedef copyright(root, parent, flist, output):
2538bcea973SRichard Lowe    ret = 0
2548bcea973SRichard Lowe    output.write("Copyrights:\n")
2558bcea973SRichard Lowe    for f in flist():
2568bcea973SRichard Lowe        fh = open(f, 'r')
2578bcea973SRichard Lowe        ret |= Copyright.copyright(fh, output=output)
2588bcea973SRichard Lowe        fh.close()
2598bcea973SRichard Lowe    return ret
2608bcea973SRichard Lowe
2618bcea973SRichard Lowe
2628bcea973SRichard Lowedef hdrchk(root, parent, flist, output):
2638bcea973SRichard Lowe    ret = 0
2648bcea973SRichard Lowe    output.write("Header format:\n")
2658bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.h')):
2668bcea973SRichard Lowe        fh = open(f, 'r')
2678bcea973SRichard Lowe        ret |= HdrChk.hdrchk(fh, lenient=True, output=output)
2688bcea973SRichard Lowe        fh.close()
2698bcea973SRichard Lowe    return ret
2708bcea973SRichard Lowe
2718bcea973SRichard Lowe
2728bcea973SRichard Lowedef cstyle(root, parent, flist, output):
2738bcea973SRichard Lowe    ret = 0
2748bcea973SRichard Lowe    output.write("C style:\n")
2758bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.c') or x.endswith('.h')):
2768bcea973SRichard Lowe        fh = open(f, 'r')
2778bcea973SRichard Lowe        ret |= CStyle.cstyle(fh, output=output, picky=True,
2788bcea973SRichard Lowe                             check_posix_types=True,
2798bcea973SRichard Lowe                             check_continuation=True)
2808bcea973SRichard Lowe        fh.close()
2818bcea973SRichard Lowe    return ret
2828bcea973SRichard Lowe
2838bcea973SRichard Lowe
2848bcea973SRichard Lowedef jstyle(root, parent, flist, output):
2858bcea973SRichard Lowe    ret = 0
2868bcea973SRichard Lowe    output.write("Java style:\n")
2878bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.java')):
2888bcea973SRichard Lowe        fh = open(f, 'r')
2898bcea973SRichard Lowe        ret |= JStyle.jstyle(fh, output=output, picky=True)
2908bcea973SRichard Lowe        fh.close()
2918bcea973SRichard Lowe    return ret
2928bcea973SRichard Lowe
2938bcea973SRichard Lowe
29495c635efSGarrett D'Amoredef manlint(root, parent, flist, output):
29595c635efSGarrett D'Amore    ret = 0
29671af3be3SCody Peter Mello    output.write("Man page format/spelling:\n")
29795c635efSGarrett D'Amore    ManfileRE = re.compile(r'.*\.[0-9][a-z]*$', re.IGNORECASE)
29895c635efSGarrett D'Amore    for f in flist(lambda x: ManfileRE.match(x)):
29995c635efSGarrett D'Amore        fh = open(f, 'r')
30095c635efSGarrett D'Amore        ret |= ManLint.manlint(fh, output=output, picky=True)
30171af3be3SCody Peter Mello        ret |= SpellCheck.spellcheck(fh, output=output)
30295c635efSGarrett D'Amore        fh.close()
30395c635efSGarrett D'Amore    return ret
30495c635efSGarrett D'Amore
3058bcea973SRichard Lowedef keywords(root, parent, flist, output):
3068bcea973SRichard Lowe    ret = 0
3078bcea973SRichard Lowe    output.write("SCCS Keywords:\n")
3088bcea973SRichard Lowe    for f in flist():
3098bcea973SRichard Lowe        fh = open(f, 'r')
3108bcea973SRichard Lowe        ret |= Keywords.keywords(fh, output=output)
3118bcea973SRichard Lowe        fh.close()
3128bcea973SRichard Lowe    return ret
3138bcea973SRichard Lowe
3144ff15898SGordon Rossdef wscheck(root, parent, flist, output):
3154ff15898SGordon Ross    ret = 0
3164ff15898SGordon Ross    output.write("white space nits:\n")
3174ff15898SGordon Ross    for f in flist():
3184ff15898SGordon Ross        fh = open(f, 'r')
3194ff15898SGordon Ross        ret |= WsCheck.wscheck(fh, output=output)
3204ff15898SGordon Ross        fh.close()
3214ff15898SGordon Ross    return ret
3228bcea973SRichard Lowe
3238bcea973SRichard Lowedef run_checks(root, parent, cmds, paths='', opts={}):
3248bcea973SRichard Lowe    """Run the checks given in 'cmds', expected to have well-known signatures,
3258bcea973SRichard Lowe    and report results for any which fail.
3268bcea973SRichard Lowe
3278bcea973SRichard Lowe    Return failure if any of them did.
3288bcea973SRichard Lowe
3298bcea973SRichard Lowe    NB: the function name of the commands passed in is used to name the NOT
3308bcea973SRichard Lowe    file which excepts files from them."""
3318bcea973SRichard Lowe
3328bcea973SRichard Lowe    ret = 0
3338bcea973SRichard Lowe
3348bcea973SRichard Lowe    for cmd in cmds:
3358bcea973SRichard Lowe        s = StringIO()
3368bcea973SRichard Lowe
3378bcea973SRichard Lowe        exclude = not_check(root, cmd.func_name)
3388bcea973SRichard Lowe        result = cmd(root, parent, gen_files(root, parent, paths, exclude),
3398bcea973SRichard Lowe                     output=s)
3408bcea973SRichard Lowe        ret |= result
3418bcea973SRichard Lowe
3428bcea973SRichard Lowe        if result != 0:
3438bcea973SRichard Lowe            print s.getvalue()
3448bcea973SRichard Lowe
3458bcea973SRichard Lowe    return ret
3468bcea973SRichard Lowe
3478bcea973SRichard Lowe
3488bcea973SRichard Lowedef nits(root, parent, paths):
3498bcea973SRichard Lowe    cmds = [copyright,
3508bcea973SRichard Lowe            cstyle,
3518bcea973SRichard Lowe            hdrchk,
3528bcea973SRichard Lowe            jstyle,
3538bcea973SRichard Lowe            keywords,
35495c635efSGarrett D'Amore            manlint,
3554ff15898SGordon Ross            mapfilechk,
3564ff15898SGordon Ross	    wscheck]
3578bcea973SRichard Lowe    run_checks(root, parent, cmds, paths)
3588bcea973SRichard Lowe
3598bcea973SRichard Lowe
3608bcea973SRichard Lowedef pbchk(root, parent, paths):
3618bcea973SRichard Lowe    cmds = [comchk,
3628bcea973SRichard Lowe            copyright,
3638bcea973SRichard Lowe            cstyle,
3648bcea973SRichard Lowe            hdrchk,
3658bcea973SRichard Lowe            jstyle,
3668bcea973SRichard Lowe            keywords,
36795c635efSGarrett D'Amore            manlint,
3684ff15898SGordon Ross            mapfilechk,
3694ff15898SGordon Ross	    wscheck]
3708bcea973SRichard Lowe    run_checks(root, parent, cmds)
3718bcea973SRichard Lowe
3728bcea973SRichard Lowe
3738bcea973SRichard Lowedef main(cmd, args):
3748bcea973SRichard Lowe    parent_branch = None
375eabe844aSJohn Levon    checkname = None
3768bcea973SRichard Lowe
3778bcea973SRichard Lowe    try:
378*42a3762dSJoshua M. Clulow        opts, args = getopt.getopt(args, 'b:c:p:')
3798bcea973SRichard Lowe    except getopt.GetoptError, e:
3808bcea973SRichard Lowe        sys.stderr.write(str(e) + '\n')
381eabe844aSJohn Levon        sys.stderr.write("Usage: %s [-c check] [-p branch] [path...]\n" % cmd)
3828bcea973SRichard Lowe        sys.exit(1)
3838bcea973SRichard Lowe
3848bcea973SRichard Lowe    for opt, arg in opts:
385*42a3762dSJoshua M. Clulow        # We accept "-b" as an alias of "-p" for backwards compatibility.
386*42a3762dSJoshua M. Clulow        if opt == '-p' or opt == '-b':
3878bcea973SRichard Lowe            parent_branch = arg
388eabe844aSJohn Levon        elif opt == '-c':
389eabe844aSJohn Levon            checkname = arg
3908bcea973SRichard Lowe
3918bcea973SRichard Lowe    if not parent_branch:
3928bcea973SRichard Lowe        parent_branch = git_parent_branch(git_branch())
3938bcea973SRichard Lowe
394eabe844aSJohn Levon    if checkname is None:
3958bcea973SRichard Lowe        if cmd == 'git-pbchk':
396eabe844aSJohn Levon            checkname = 'pbchk'
397eabe844aSJohn Levon        else:
398eabe844aSJohn Levon            checkname = 'nits'
399eabe844aSJohn Levon
400eabe844aSJohn Levon    if checkname == 'pbchk':
4018bcea973SRichard Lowe        if args:
4028bcea973SRichard Lowe            sys.stderr.write("only complete workspaces may be pbchk'd\n");
4038bcea973SRichard Lowe            sys.exit(1)
404eabe844aSJohn Levon        pbchk(git_root(), parent_branch, None)
405eabe844aSJohn Levon    elif checkname == 'nits':
406eabe844aSJohn Levon        nits(git_root(), parent_branch, args)
407eabe844aSJohn Levon    else:
408eabe844aSJohn Levon        run_checks(git_root(), parent_branch, [eval(checkname)], args)
4098bcea973SRichard Lowe
4108bcea973SRichard Loweif __name__ == '__main__':
4118bcea973SRichard Lowe    try:
4128bcea973SRichard Lowe        main(os.path.basename(sys.argv[0]), sys.argv[1:])
4138bcea973SRichard Lowe    except GitError, e:
4148bcea973SRichard Lowe        sys.stderr.write("failed to run git:\n %s\n" % str(e))
4158bcea973SRichard Lowe        sys.exit(1)
416