local git hook for “git remote update”?

I have a local git repository, created with:

git clone --mirror git://github.com/<user>/<project>.git

Occasionally changes will get pushed to github, and this machine will pull them with:

  • Can I connect from VS 2015 to VSTS and GitHub the same time?
  • I accidentally created a local branch named origin/foo. Now what?
  • Using SASS with Git, what files do I ignore and how?
  • Heroku Rails “Application Error”
  • Git: new branch not getting pushed
  • Why does git sometimes mark added lines as changed lines (i.e. an empty conflict over an added piece of code)
  • git remote update --prune

    This all works fine, but after the update, I want to run a hook, and I’m not sure which to run. I have tried “post-receive” and “post-merge”, but neither seems to execute after the update. In both cases, the contents of the hook file are:

    echo foo > foo.log

    When I run them from the command-line via bash.exe (yes, on Windows), a foo.log file is created, so that works. But nothing happens as a result of the “git remote update”. Between each “git remote update” I am pushing a useless change to make sure there’s something to pull.

  • Integrating MsBuild with Git
  • Git - How to selectively apply changes from one branch to another?
  • How to install latest version of git on CentOS 6.x/7.x
  • Copying only files from git tag
  • github not showing last 9 commits
  • Head commit for all remote branches using Git
  • 2 Solutions collect form web for “local git hook for “git remote update”?”

    I ended up solving this by making a little bash script to wrap the “git remote update” command. The script looks at all refspecs before and after the update, then calls the post-update hook in the same way that git would if it had the missing feature. Credit to @torek and @larsks for the idea in their comments to the original question.

    Note: this script uses bash associative arrays which are only available in bash versions >= 4.0.

    git_dir=$(git rev-parse --git-dir) || exit
    cd "$git_dir"
    declare -A before after all
    # create the 'before' list
    while read commit_hash refspec; do
    done < <(git show-ref --heads)
    # do the remote update
    git remote update --prune
    # create the 'after' list
    while read commit_hash refspec; do
    done < <(git show-ref --heads)
    # see if there were any changes, and if so, run the post-receive hook
    for refspec in "${!all[@]}"; do
      [ "${before[$refspec]}" != "${after[$refspec]}" ] && { changes=1; break; }
    if [ "$changes" == "1" ]; then
      none="$(printf "%0.s0" {1..40})" # forty zeroes, or git's "don't care" ref
      for refspec in "${!all[@]}"; do
        # if the refspec changed, pass it to the post-receive hook
        [ "${before[$refspec]}" != "${after[$refspec]}" ] && \
          echo "${before[$refspec]:-$none} ${after[$refspec]:-$none} $refspec"
      done | hooks/post-receive

    Here’s an awk for Windows people who don’t have bash 4:

    GIT_DIR=`git rev-parse --git-dir` || exit
    cd "$GIT_DIR"
    before=`git show-ref`
    git remote update --prune
    after=`git show-ref`
    { echo "$before"
      echo "$after"
    } | awk '
            /^$/    {++after; next}
            1       { ++seen[$2]
                      if (!after) old[$2]=$1; else new[$2]=$1
            END     { z8="00000000"; none = z8 z8 z8 z8 z8
                      for ( k in seen ) if (old[k] != new[k])
                              print (old[k]?old[k]:none), (new[k]?new[k]:none), k
    ' | hooks/post-receive
    Git Baby is a git and github fan, let's start git clone.