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:

  • What are the commands for using Git Bash in Windows e.g. when in git diff mode?
  • What is a git topic branch?
  • Visual Studio Solution from multiple git repositories
  • How to find the date of the first commit of files in git
  • How to sort git tags by version string order of form rc-X.Y.Z.W?
  • In Jenkins, how to checkout a project into a specific directory (using GIT)
  • 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.

  • Conditional Gitignore
  • Git - SSH - Hosts: How can I delegate different IPs to remote origin, depending on what works each time?
  • Git Switching branch
  • Track git pushes and checkout
  • Find latest git tag from the remote git repository
  • Is there a naming convention for git repositories?
  • 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.