lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi] Writing a git pre-commit hook


From: Greg Chicares
Subject: [lmi] Writing a git pre-commit hook
Date: Tue, 8 Nov 2016 22:13:57 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Icedove/45.3.0

[I've already solved the problems that I'm aware of, but it has been
difficult enough that I wanted to post some notes here.]

A proprietary repository has this layout:

/opt/lmi/proprietary[0]$ls --classify
data/  hooks/  src/  test/

and I want a pre-commit hook to test each those subdirectories except
hooks/ itself. It is sufficient for now to automate this cut-and-paste
code from lmi's 'gwc/develop2.txt':

  # Make some changes, then test concinnity (before every commit)

cd /opt/lmi/proprietary/data/
cd ../data; make src_dir=/opt/lmi/src/lmi -f /opt/lmi/src/lmi/GNUmakefile 
check_concinnity 2>&1 |less -S
cd ../src ; make src_dir=/opt/lmi/src/lmi -f /opt/lmi/src/lmi/GNUmakefile 
check_concinnity 2>&1 |less -S
cd ../test; make src_dir=/opt/lmi/src/lmi -f /opt/lmi/src/lmi/GNUmakefile 
check_concinnity 2>&1 |less -S

The first thing I did was to symlink the repo's hooks/ :
  cd /opt/lmi/proprietary
  ln --symbolic --force --no-dereference ../hooks .git
and then I tried the following script, which is defective:

----8<--------8<--------8<--------8<--------8<--------8<--------8<----
#!/bin/sh

# git pre-commit hook

check_concinnity()
{
    output=$( \
        make src_dir=/opt/lmi/src/lmi -f /opt/lmi/src/lmi/GNUmakefile 
check_concinnity \
        2>&1 | sed \
          -e'/^make[[]/d' \
          -e'/^  Problems detected by xmllint:$/d' \
          -e'/^  Miscellaneous problems:$/d' \
          -e'/^ *[0-9][0-9]* \(source files\|source lines\|marked defects\)$/d'
      )
    if [ -n "$output" ]; then
        printf '\n%s\n' "$output"
        printf "COMMIT ABORTED\n"
        exit 1
    fi
}

printf "checking "
printf "src..."
cd $(git rev-parse --show-toplevel)/src  && check_concinnity
printf "data..."
cd $(git rev-parse --show-toplevel)/data && check_concinnity
printf "test..."
cd $(git rev-parse --show-toplevel)/test && check_concinnity
printf "okay\n"
---->8-------->8-------->8-------->8-------->8-------->8-------->8----

When I ran it at the command line, it worked:

/opt/lmi/proprietary[0]$./hooks/pre-commit
checking src...data...test...okay

However, when I let 'git commit' run it, it failed to navigate the
subdirectories:

/opt/lmi/proprietary[0]$git commit --all -m'Update test results

See lmi commit 6faf7bb7640bb2fe9cf5416ca716fc48242f36db.'
checking src...data...fatal: Not a git repository: '.git'
.git/hooks/pre-commit: 26: cd: can't cd to /data
test...fatal: Not a git repository: '.git'
.git/hooks/pre-commit: 28: cd: can't cd to /test
okay
[master 61cc82a] Update test results
 1 file changed, 25 insertions(+), 25 deletions(-)
/opt/lmi/proprietary[0]$

It still "succeeds", and the change is committed, but apparently it's
not checking the data/ and test/ subdirectories.

(1) How can we test a pre-commit hook in the context in which git runs
it, which is evidently not the same thing as running it at the command
prompt?

This article:
  http://stackoverflow.com/questions/11512155/how-to-test-git-hooks
suggests adding 'set -x' inside the script (good idea), then running
it in a throwaway clone of the repository (yick--I'd rather commit
a nonsensical change and then 'reset --hard'). But I stumbled upon a
method I like better: just run 'git commit' in a clean repository;
that runs the hook and then commits nothing.

(2) How does running the script at the command line differ from having
git run it?

Searching the web for "fatal: Not a git repository: '.git'" suggests that
  http://stackoverflow.com/questions/2314500/git-post-receive-hook-not-working
| GIT_DIR and GIT_WORK_TREE are set when the hook runs
which leads to advice like
  
http://stackoverflow.com/questions/3542854/calling-git-pull-from-a-git-post-update-hook
| git --git-dir=.git pull
| git -C [directory] [subcommand]
(but I don't want to hard-code an absolute path)
| env -i git [subcommand]
(but that's an awfully large hammer to hit this problem with) or even
  unset $(git rev-parse --local-env-vars)
which seems unlikely to help because 'rev-parse' seems to be the
subcommand that's failing in my case.

I was about to add 'env -i' before 'git rev-parse' just to see whether
that would magically solve the (unknown) problem, when I figured that
if I was going to go to all that trouble, I should first refactor:

+ toplevel=$(git rev-parse --show-toplevel)
- cd $(git rev-parse --show-toplevel)/src  && check_concinnity
+ cd $toplevel/src  && check_concinnity
- cd $(git rev-parse --show-toplevel)/data && check_concinnity
- cd $(git rev-parse --show-toplevel)/test && check_concinnity
+ [...similarly]

...and then it simply worked (so I never had reason to experiment with
'env -i'). This leads to a better question:

(3) What was the error message above:
  checking src...data...fatal: Not a git repository: '.git'
  .git/hooks/pre-commit: 26: cd: can't cd to /data
  test...fatal: Not a git repository: '.git'
  .git/hooks/pre-commit: 28: cd: can't cd to /test
really saying...and not saying?

When I ran the script interactively, it did this (ignoring the
check_concinnity() function call):

  /opt/lmi/proprietary[0]$cd $(git rev-parse --show-toplevel)/src
  /opt/lmi/proprietary/src[0]$cd $(git rev-parse --show-toplevel)/data
  /opt/lmi/proprietary/data[0]$cd $(git rev-parse --show-toplevel)/test
  /opt/lmi/proprietary/test[0]$

Of those three 'cd' commands, the error message does not suggest that
the first failed. Instead, it says that the others failed, apparently
because $(git rev-parse --show-toplevel) evaluated to an empty string.
I don't know why that happened--maybe it's a bad idea, inside a git
hook, to cd to a subdirectory that might conflict with GIT_WORK_TREE,
and then invoke any git facility. (Perhaps this article:
  
http://serverfault.com/questions/107608/git-post-receive-hook-with-git-pull-failed-to-find-a-valid-git-directory
sheds some light on that.) If I experimentally add this:

  toplevel=$(git rev-parse --show-toplevel)
  printf "toplevel is $toplevel\n"
  printf "PWD is $PWD\n"
  cd gwc
  toplevel=$(git rev-parse --show-toplevel)
  printf "toplevel is $toplevel\n"
  printf "PWD is $PWD\n"

to the pre-commit hook for lmi above, I get:

  /opt/lmi/src/lmi[0]$git commit
  toplevel is /opt/lmi/src/lmi
  PWD is /opt/lmi/src/lmi
  fatal: Not a git repository: '.git'

so, whatever the ultimate cause, that's a reproducible problem. I'll
go ahead and commit pre-commit scripts that avoid that problem and seem
to work properly.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]