2

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:

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:

#!/bin/sh 
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.

Josh Santangelo
  • 918
  • 3
  • 11
  • 22
  • 1
    `git remote update` just runs some (possibly many) `git fetch` operations, and those do not run hooks. You could grab all refspecs pre- and post-update, and do your own thing, whatever that is, with the changes. – torek Feb 05 '14 at 02:47
  • 1
    Consider wrapping your `git remote update` command in a script that runs commands after the update operation is complete. – larsks Feb 05 '14 at 06:00
  • Glad to hear I wasn't just losing my mind. I think you're both right, I'll have to do a bit of manual scripting. – Josh Santangelo Feb 05 '14 at 17:02
  • @JoshSantangelo, did you manage to get it work? – mrts Feb 20 '15 at 14:38

2 Answers2

3

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.

#!/bin/bash

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
  before[$refspec]="$commit_hash"
  all[$refspec]=''
done < <(git show-ref --heads)

# do the remote update
git remote update --prune

# create the 'after' list
while read commit_hash refspec; do
  after[$refspec]="$commit_hash"
  all[$refspec]=''
done < <(git show-ref --heads)

# see if there were any changes, and if so, run the post-receive hook
changes=0
for refspec in "${!all[@]}"; do
  [ "${before[$refspec]}" != "${after[$refspec]}" ] && { changes=1; break; }
done

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
fi
Community
  • 1
  • 1
joelsmith
  • 31
  • 5
1

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

#!/bin/sh
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 
  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
jthill
  • 55,082
  • 5
  • 77
  • 137