SVN hooks not working

commithooksvn

I have a server repository of branch and trunk. The branch is all team members' repositories. I'm trying to use svn hooks only in my repo under branch, but it doesn't seem to work fine. In the following are the steps I tried to approach:

  1. checked out my_repo from the remote server's branch/my_repo

  2. since the local repo my_repo doesn't have any content, I created a new svn repo locally and copied over everything including the /hooks folder to my_repo.

  3. I created an empty file in my_repo and added a line of text. Then svn add this file.

  4. modified the my_repo/hooks/pre-commit.tmpl file and make it always not pass with error code 1. Now it looks like:

#!/bin/sh
exit 1
  1. copy the pre-commit.tmpl to pre-commit and add execute permit of pre-commit to myself

  2. The server contains other people's now the server's structure is like:

- server
    - branch
        - my_repo
            - myfile
            - hooks
                - pre-commit
        - Tom's repo
        - other team member's repo
    - trunk
  1. in the checked out repo I committed the changes using: svn commit -m "dumb change"

Now from here I should not be able to commit and it should give me an error with code 1 right? But I don't see it anywhere.

  1. Then I tried putting the hooks folder at the top level and at same level as branch and trunk. i.e. the structure looks like:
-server
    - branches
        - my_repo
            - myfile
        - Tom's repo
        - other team member's repo
    - trunk
    - hooks
        - pre-commit

But, still not working. sigh…

HOWEVER, with David's help, I figured out what to do and what was wrong:
1. To highlight: the ownership of the hooks folder should be the same as who created to repository. Thus I had to ask the owner to add the hooks files to the server. I didn't create the repository on the server, thus the files are invisible in my working directory.
2. now here's what I tried:

  1) on my own Linux system, I `svnadmin create` a new repository, maybe called test_server: in that there is folders: confs, db, hooks, locks; files: format, readme.txt
  2) on the same level, mkdir a new folder (called working_dir) as your local working directory and checkout from the test_server. Now the working_dir contains a folder called test_server and it's empty. You can't see any of the folders or files in step 1
  3) modify the test_server's hooks file as described above. 
  4) try to add a file and add a new line to the file in the working_dir/test_server folder and commit. 
  5) now you should see commit failed with message: svn: Commit blocked by pre-commit hook(exit code 1) with no output. 

Thank you so much David and anyone put comments earlier!

Best Answer

When you run a hook, the STDOUT (what normally is produced by an echo statement) is disabled. This means your script cannot use STDOUT to print out anything, even if it has been redirected to a file.

Instead, you need to open another file descriptor instead of using 1 (STDOUT). You can use the exec command to open another file descriptor, and then pipe that into a file:

exec 4> $my_file  #Opening my_file for writing
echo "This is going to $my_file" >&4

STDERR is also redirected. The output of STDERR is collected and sent back to the calling client, but only when a hook returns a non-zero exit code. This gives you a way to communicate with the client why a hook failed.

You also must be careful because the environment that the hook runs in is scrubbed. Even $PATH is null.

These are some of the reasons why a hook script will run fine from the command line, but not when it is executed as a hook.

If you don't believe a hook is working, just set it so it exits with a non-zero exit code. If you get a message back from Subversion that the transaction failed, you know the hook script executed.

I also recommend that you use, at a minimum, svnserve to act as a Subversion server -- even if you're the only one using the repository. I never use file:// even if I'm the only one using the repository. The svnserve process is pretty simple to use and it's fairly lightweight.

Also, never use svn in hook scripts. Use svnlook instead.


Addendum

I want to be very, very clear on this. We need to agree with some definitions:

  • The SERVER is the machine which is running the Subversion repository. You used the command svnadmin create foo to create a foo directory that will act as the repository itself.
  • The REPOSITORY DIRECTORY is the directory on the server created by the svnadmin create command. This is the SERVER SIDE of the repository. You will not see any files you've checked into Subversion here. Instead, you will see a hooks directory and a db directory. This is what the server uses to track its changes.
  • The WORKING DIRECTORY is the directory you've done a svn checkout to checkout a particular revision of your project.
  • The REPOSITORY is the VIRTUAL view of the REPOSITORY DIRECTORY you get when you use the various svn commands like svn ls or svn log or svn co. This is NOT the REPOSITORY DIRECTORY but a view of the repository.

Okay, now we have this settled:

Hook scripts are stored in the REPOSITORY DIRECTORY under the hooks directory. When you create the REPOSITORY DIRECTORY, there will already be a sub-directory called hooks with some templates for hook scripts. These will have a *.tmpl suffix. To make a hook, you need to replace one of these scripts with your hook script, and remove that *.tmpl suffix. The hook script has to have executable permission and be owned by the user that is running the Subversion SERVER process. (The user running httpd or the svnservecommand on the server).

Hooks are for the entire repository. You can't tell a hook not to fire only when a particular branch is affected. However, your hook script can see where a file is located and take action based upon that. I have a pre-commit hook that does just that. It uses a control file to determine the action it needs to take based upon the location of a file. However, every time a commit happens, this hook will fire even if it doesn't have to do anything.

I hope this answers your questions.