Can a Git hook automatically add files to the commit?

40,318

Solution 1

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.

Solution 2

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

Solution 3

#!/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)

Solution 4

You could use a combination of a pre and post commit script.

In the pre-commit:

  • Touch a file .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:

  • add the files
  • commit --amend -C HEAD --no-verify (avoid looping)
  • delete .commit file

This 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.

Solution 5

You can use update-index:

git update-index --add my_files

Share:
40,318

Related videos on Youtube

Ian Terrell
Author by

Ian Terrell

Updated on November 24, 2021

Comments

  • Ian Terrell
    Ian Terrell over 2 years

    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.

    • Martin Jambon
      Martin Jambon about 6 years
      You need to remove the execs from this code (see man sh). It's impossible for any shell command to be executed after an exec, since exec clobbers the current process, i.e. the shell that is used to interpret commands.
  • rfunduk
    rfunduk almost 14 years
    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 :)
  • Ian Terrell
    Ian Terrell almost 14 years
    They can't be generated anywhere: they're being deployed from source control to a read-only filesystem.
  • Ian Terrell
    Ian Terrell almost 14 years
    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.
  • rfunduk
    rfunduk almost 14 years
    There you have it! :) You might try to put the generation step into your deploy script, but that might also be impractical.
  • Ian Terrell
    Ian Terrell almost 14 years
    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.
  • Craig Trader
    Craig Trader almost 14 years
    @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).
  • rfunduk
    rfunduk almost 14 years
    It's possible that another option to update-index will be what you need, too: ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/…
  • rfunduk
    rfunduk almost 14 years
    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 :)
  • Ian Terrell
    Ian Terrell almost 14 years
    I will definitely look at that as an option. Thanks!
  • kojiro
    kojiro over 12 years
    I don't know about @IanTerrell, but I'm still stuck on this. I tried both git add and git update-index --add. In both cases the files get added to the repository (so they'd be in the next commit), but not to the current commit.
  • Rubycut
    Rubycut almost 12 years
    This is not working in recent git (I use 1.7.9), git add in pre-commit adds the files for future commit, not the one you just engaged.
  • ben
    ben almost 12 years
    Works for me in git 1.7.10. The files are not added to the commit message, but they are commited. It seems that the 'git status' in the commit message is generated before pre-commit. This seems like a bug to me, but I suspect it was done on purpose for some reason. What you can do is add a line to the end of pre-commit that looks something like 'git status; echo -n Hit enter to continue...; read'. There is likely a better way to solve this, but that was a quick fix for me.
  • inf3rno
    inf3rno about 11 years
    I forgot mention, I have git version 1.7.7. This feature is available only version 1.7.10+ according to previous comments.
  • jbasko
    jbasko about 10 years
    I have git 1.8.3.4 and whatever is added in pre-commit hook doesn't appear to be staged until the next commit.
  • Albert Netymk
    Albert Netymk over 8 years
    Shouldn't -a be -e to test if .commit exist?
  • erichlf
    erichlf over 8 years
    rfunduk's response worked perfectly for me on git 2.6.2, i.e. git update-index --add my_files
  • Flimm
    Flimm over 8 years
    git add in a pre-commit hook doesn't work as intended for me in Git 2.6.4
  • Yi Jiang
    Yi Jiang almost 8 years
    Actually, I can confirm this works in git 2.7.4 I'm using right now. It doesn't appear that way when editing the commit message, but when you actually commit you will see the files you added during the pre-commit hook appear in the commit.
  • IanS
    IanS over 7 years
    This worked for me, too, except I needed to change the working directory of my script using cd $(git rev-parse --show-toplevel)
  • Eliran Malka
    Eliran Malka about 7 years
    @AlbertNetymk - there is practically no difference; the -a primary was introduced as a KornShell proposal and made its way to POSIX, it's only supported today for backwards-compatibility. later -e was added to avoid confusing the -a primary with the -a binary operator.
  • Albert Netymk
    Albert Netymk about 7 years
    @EliranMalka Could you provide a ref? I have trouble locating -a. linux.die.net/man/1/test The only use I found is for logical and.
  • Eliran Malka
    Eliran Malka about 7 years
    @AlbertNetymk, sure, here is the POSIX test command man page on unix.com. search the page for the term "An early proposal used the KornShell -a primary"
  • Albert Netymk
    Albert Netymk about 7 years
    @EliranMalka I see. Thank you very much. -a should probably never be used other than as a boolean operator just to avoid the confusion.
  • superarts.org
    superarts.org over 6 years
    Not work in 2.11.0 either. Changes are added but not committed.
  • Iulian Onofrei
    Iulian Onofrei over 6 years
    This also doesn't work if you have another file in the index which you don't want to add to the commit.
  • laurent
    laurent over 6 years
    Worked for me too. If doing just git commit, the added files won't appear in the automatically generated commit message, but they will be added anyway.
  • jcollum
    jcollum about 4 years
    git-scm.com/docs/githooks post-commit runs after the commit is complete -- you can't amend it at that point afaics
  • jcollum
    jcollum about 4 years
    I don't get the purpose of .commit here -- from messing with this it seems like post-commit will only run after a commit is complete making the .commit file irrelevant. Maybe you meant the .commit file to indicate a special state like "cleanup needed"?
  • rfunduk
    rfunduk about 4 years
    git commit --amend changes the previous commit (which as you said has just happened)
  • Gabriella Gonzalez
    Gabriella Gonzalez about 4 years
    @jcollum: Apparently, the .commit file prevents an infinite loop. Specifically, --no-verify doesn't prevent the post-commit hook from running, so the post-commit runs twice: once when the .commit file is present, which then runs git commit --amend … which triggers the post-commit hook again, but this time with the .commit file being gone, which ends the loop.
  • Gabriella Gonzalez
    Gabriella Gonzalez about 4 years
    I believe this needs a final exit command at the end of the pre-commit script
  • mhucka
    mhucka about 4 years
    This scheme works for me, with the critical modification that I had to move the git add to the pre-commit hook instead of the post-commit hook. I'm using git version 2.25.1 on macOS.
  • mhashim6
    mhashim6 over 3 years
    should be --amend not --ammend.