SVN hooks not working

commit, hook, svn

Solution

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 `svnserve`command 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.

Problem

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: checked out `my_repo` from the remote server's `branch/my_repo` 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`. I created an empty file in `my_repo` and added a line of text. Then `svn add` this file. 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 ``` copy the `pre-commit.tmpl` to pre-commit and add execute permit of pre-commit to myself 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 ``` - 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. - 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!

Original source