Reputation: 10866
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.
Upvotes: 107
Views: 57106
Reputation: 4090
Imagine you have a pre-commit hook that does this:
#!/bin/bash
echo "--- Dataset Pre-Commit Hook ---"
DATASET_REPO_PATH="$(pwd)"
python3 -m my_script_that_generates_or_modifies_a_file
git -C $DATASET_REPO_PATH add file
echo "--- Done ---"
exit
It will work IF AND ONLY IF, there was already something to be commited. It does not have to be the same file tho. If there was nothing to be commited, the git add git add file
will work, but will not be commited.
Example that works:
gitignore
)$> git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitignore
pre-commit
hook will run and create file
. Then it will commit.--- Dataset Pre-Commit Hook ---
--- Done ---
[master (root-commit) 2ba2f7c] First commit
3 files changed, 34 insertions(+)
create mode 100644 .gitignore
create mode 100644 file
file
was commited. And git status
now outputs:On branch master
nothing to commit, working tree clean
Example that DOES NOT work
git add
but it will do nothing. git status
gives:On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
file
. But the commit message says he did nothing.--- Dataset Pre-Commit Hook ---
--- Done ---
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file
git commit
again for it to work.I tried adding that as a post-commit
hook but got no luck. If someone knows a fix for this case, I would love to know.
Upvotes: 0
Reputation: 19
I was facing same problem in pre-commit hook also. I was modifying one file and committing but it was taking previous file not updated file so by adding git command(as below) in pre-commit hook, it solved.
git add $file
note: $file
is your file to be added.
Upvotes: -2
Reputation: 368
You could use a combination of a pre and post commit script.
In the pre-commit:
.commit
or something. (be sure to add this to .gitignore
)In the post-commit:
if .commit exists you know a commit has just taken place but a post-commit hasn't run yet. So, you can do your code generation here. Additionally, test for .commit
and if it exists:
commit --amend -C HEAD --no-verify
(avoid looping).commit
fileThis is roughly the process I use to store a .metadata
file in the repository generated from metastore.
If anyone knows a better way I'm all ears but it seems to work for now.
Upvotes: 14
Reputation: 799
Since git add was also not working for me in a pre commit, I followed mark's idea of using a .commit file and splitting the process into pre- and post-commit.
Here is some code that should be easy to understand
In the pre-commit:
- Touch a file .commit or something. (be sure to add this to .gitignore)
#!/bin/sh
echo
touch .commit
exit
In the post-commit:
if .commit exists you know a commit has just taken place but a post-commit hasn't run yet. So, you can do your code generation here. Additionally, test for .commit and if it exists:
- add the files
- commit --amend -C HEAD --no-verify (avoid looping)
- delete .commit file
#!/bin/sh
echo
if [ -e .commit ]
then
rm .commit
git add yourfile
git commit --amend -C HEAD --no-verify
fi
exit
Hope this makes it easier for people with few bash knowledge to follow mark's idea.
Upvotes: 79
Reputation: 9418
Yes, you can add generated files automatically on the commit using git hooks! But it requires a tricky script.
Here you can find the problem solved. There, it is updating the file version on every commit, adding a new modified file and amending the commit as you need it to. It is fully working: https://github.com/evandrocoan/.versioning
Then you just replace the 'Version File Replacement' algorithm on the file 'updateVersion.sh', by your algorithm. Maybe you need to change a few things like, remove the branch limitation, because there, the script only runs if you are on the 'develop' branch.
Also, it will only change the specified file, if is staged. If the file is not staged, then it will do nothing than the normal/usual commit. More precisely, it print out what it is doing on every step.
I am going to explain, that trick. It is quite tricky. On the prepare-commit-msg-hook, it detects whether the desired file is being staged and committed. After that, it creates a flag file, and stops the prepare-commit-msg-hook. Later on the post-commit-hook, it checks whether the flag file exists. If yes, it amends the files on the commit.
Attention, it would create a infinity loop because it would call again the prepare-commit-msg-hook (as we are amending). But it does not happen because of the flag file. When the prepare-commit-msg-hook runs and find the flag file, it "knows" what is happening. Then is just deletes the flag file and do not create it again. Doing it, it will block the post-commit-hook from amending again the commits, allowing the commit to finish for good.
Upvotes: 0
Reputation: 9977
#!/bin/sh
#
# .git/hooks/pre-commit
#
git add file.xyz
This worked just fine for me. It will be part of the current commit.
git version 1.7.12.4 (Apple Git-37)
Upvotes: 18
Reputation: 8995
I had the same need and this approach worked pretty well for me:
#!/bin/sh
files='git diff --cached --name-only'
re="<files of importance>"
if [[ $files =~ $re ]]
then
echo "Creating files"
create_my_files && git add my_files
fi
where "create_my_files" should be executable, for example if it is a python file you could execute it as "python create_my_files && git add my_files"
and is true you don't need a pre-commit to commit again (that would create a infinite nasty loop :p)
Upvotes: 1
Reputation: 4906
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
Upvotes: 40
Reputation: 30442
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
.
Upvotes: 3
Reputation: 15679
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://git-scm.com/docs/githooks :
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.
Upvotes: 0