views:

175

answers:

4

I'd like to add an automatically generated file to the same commit using a pre- or post-commit hook in Git, dependent on the files that were modified in that commit. How would I go about this?

I've tried this as a pre-commit hook, but no luck:

#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
  echo "Creating files"
  exec bundle exec create_my_files
  exec git add my_files
  exec git commit --amend -C HEAD
fi

This successfully adds them to the repository, but does not add them to the commit. I've also tried using the last two exec lines in a post-commit hook along with the pre-commit inspection, but no good either.

A: 

What happens when you try this hook? Do you get an error? Are the files created? What is the output of git status afterwards?

Seems to me you shouldn't need the last line since the commit hasn't actually happened yet (pre-commit :)).

thenduks
I updated the question with the information. There are no errors. The files are created. Git status shows they've been added but not committed.
Ian Terrell
Ok, maybe try `git update-index --add my_files`?
thenduks
It's possible that another option to `update-index` will be what you need, too: http://ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/git-update-index.html
thenduks
I will definitely look at that as an option. Thanks!
Ian Terrell
A: 

If the files are automatically generated, and they can be generated anywhere (implicit in your desire to build them in the Git pre-commit hook) then you shouldn't be putting them under source control in the first place. You should only control source files -- generated files should be generated as part of the build scripts.

The only reason to put a generated file under source control is when it requires unique/privileged resources to generate (such as a licensed program) or it requires a significant amount of time to generate.

Added

From http://www.kernel.org/pub/software/scm/git/docs/githooks.html :

pre-commit This hook is invoked by git commit, and can be bypassed with --no-verify option. It takes no parameter, and is invoked before obtaining the proposed commit log message and making a commit. Exiting with non-zero status from this script causes the git commit to abort.

The default pre-commit hook, when enabled, catches introduction of lines with trailing whitespaces and aborts the commit when such a line is found.

All the git commit hooks are invoked with the environment variable GIT_EDITOR=: if the command will not bring up an editor to modify the commit message.

The intent of the pre-commit hook is to be a pass-fail check on the state of the workspace and the contents of the commit, prior to making the commit. Attempting to change the contents of the commit won't work.

My recommendation would be add two steps to your build scripts: (1) a step that will build all of the out-of-date files that needs to be generated (and adds them to the workspace), and (2) a step that will check to ensure that all of the generated files are up-to-date, and return a non-zero status code. Your Git pre-commit hook should run the second step. Your developers should be trained to run the first step as necessary.

Craig Trader
True, but doesn't answer the question. He might have a very good reason for putting the generated file under source control, that's not for us to decide :)
thenduks
They can't be generated anywhere: they're being deployed from source control to a read-only filesystem.
Ian Terrell
There you have it! :) You might try to put the generation step into your deploy script, but that might also be impractical.
thenduks
The deployment is automated with a git push (it's a Rails app on Heroku), so it's not terribly practical to put it there. Pre-commit is really the place for it, as I can test to see if any dependent files have changed and only rebuild the generated files if they have.
Ian Terrell
@Ian, it really sounds like this is something for your build scripts, before you commit, instead of trying to use Git to automate the generation. If anything should be put into the pre-commit step, it should be a check to ensure that the files are up-to-date before committing (and fail the commit if they are out of sync).
Craig Trader
Looks like you're right about pre-commit... unfortunately for Ian's developers, there are likely no 'build scripts' involved in his project -- unless you count tests :)
thenduks
A: 

How about writing a post-commit script instead which generates your files, and then have that do (something along the lines of) git add my_files; git commit --amend.

thenduks
Unfortunately this does not work either.
Ian Terrell
+3  A: 

It's possible to do what you want using pre-commit hooks. We do something similar for a heroku deployment (compiling coffeescript to javascript). The reason your script isn't working is because you used the exec command improperly.

From the man page:

The exec builtin is used to replace the currently running shells process image with a new command. On successful completion, exec never returns. exec can not be used inside a pipeline.

Only your first exec command is running. After that your script is basically terminated.

Give something like this a try (as a pre-commit hook):

#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
  echo "Creating files"
  bundle exec create_my_files
  git add my_files
fi
Jim Garvin