git hook syntax check

Trying to make a php syntax check hook for a git repository. I was happy when I found a code snippet that does exactly this.

But it seems that the snippet script has a flaw. It executes

  • Git fetch says “success” but nothing is downloaded
  • GIT : New branch is pointing to the dev branch
  • Building Eclipse IDE from scratch - how to specify the target OS?
  • Git log with color and name-rev
  • git-svn with file-based access to repository
  • How can I update the usernames/emails in Git commits?
  • git diff-index --cached --name-only HEAD
    

    to get a list of files in the index. Now it runs php -l for each file on that list. The flaw is that a file might differ between the working copy and the staging area. If the staging area php has a syntax error, but the working copy version doesnt, no syntax error is found and the committ succeeds which was the thing to prevent.

    Is this a nontrivial problem to solve, or is there some way to run php -l on the staging-version of each file?

  • How to delete a file with prefix “:” in git
  • Is there a way for git to checksum a repository for sanity?
  • git merge with renamed files
  • intellij idea: how to disable pre-commit code analysis for Git-backed projects
  • Entire git history of commits is doubled up
  • GIT: Problems Understanding Branch Naming Convention
  • 3 Solutions collect form web for “git hook syntax check”

    I’m very happy using this php syntax validation hook. Hope it will fit your needs too.

    It uses git diff-index --cached --full-index.

    I am not sure if there is a problem here.

    The snippet you mention probably comes from the blog post Don’t Commit That Error.
    And it includes:

    Next, we call git diff-index with a few parameters.
    First, we add --cached to tell Git we only want files that are going to be committed.
    Then, we add --name-only to tell Git to only output the name of the files that are being committed.

    It seems the files which are about to be committed are precisely the ones a pre-commit hook would want to inspect.
    Even if they differ from the files in the working directory, it is their version (in the index) which is to be committed. And it is that same version which will be send to the php -l process.


    Actually, the problem is not in the git diff-index itself (with or without --full-index) is in the way you will read the file content in the index

    • The “PHP Advent 2008” hook will simply try to access the file from its name (with the risk of accessing the working copy)
    exec("php -l " . escapeshellarg($file), $lint_output, $return);
    
    • The “phpbb3” hook (mentioned by takeshkin in his answer) will ask Git for the actual content:
    result=$(git cat-file -p $sha | /usr/bin/env $PHP_BIN -l 2>/dev/null)
    

    Using git cat-file is the key here, to access an object in the Git repo (i.e. not in the “working directory”)

    Cause pre-commit hook is not good for working in a team you should use pre-receive hook installed on server side to refuse all commits with invalid php syntax and incorrect coding standard.

    I’ve created pre-receive python script for that pupose:

    # A server-side git hook script for checking PHP syntax and validating coding standard
    # Depends on: PHP_CodeSniffer (http://pear.php.net/package/PHP_CodeSniffer/)
    # Install: copy this script to <server side repo location>/hooks/pre-receive
    #!/usr/bin/python
    
    import os
    import sys
    oldrev, newrev, ref = sys.stdin.read().strip().split(' ')
    test_file = os.popen('mktemp').read().strip()
    coding_standards = 'PSR2'
    for line in os.popen('git diff --name-only %s %s' % (oldrev, newrev)).readlines():
        extension = line.split('.')[-1].strip()
        file_name = line.strip()
    
        if(extension == 'php'):
            os.system("git cat-file -p %s:%s > %s" % (newrev, file_name, test_file))
            if 0 != os.system('php -l ' + test_file + ' > /dev/null'):
                print "PHP Syntax error in file %s" % (file_name)
                sys.exit(1)
    
            if 0 != os.system("phpcs -n --standard=%s %s" % (coding_standards, test_file)):
                print "Coding standards fail in file %s" % (file_name)
                sys.exit(2)
    
    Git Baby is a git and github fan, let's start git clone.