Skip processing of Git revisions in post-receive hook that have already been previously processed

git, git-post-receive

Solution

Look at `$(prefix)/share/git-core/contrib/hooks/post-receive-email`, which does just what (I think) you want. Basically it uses `git for-each-ref` to find the names of all branches, and then exclude every commit that's reachable from some branch other than the one being updated:

if [ "$change_type" = create ]
then
    # Show all revisions exclusive to this (new) branch.
    revspec=$newrev
else
    # Branch update; show revisions not part of $oldrev.
    revspec=$oldrev..$newrev
fi

other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
     grep -F -v $refname)
git rev-parse --not $other_branches | git rev-list --pretty --stdin $revspec

(I've simplified it here, and hopefully not damaged anything in my cut-and-paste job. The inputs here are: `$change_type` is `create` if `$oldrev` is all-zeros, otherwise it's `update`; `$oldrev` is the old rev SHA1 from the line recently-read from stdin; `$newrev` is the new rev SHA1; and `$refname` is the full name, e.g., `refs/heads/topic`.)

Problem

I have a git post-receive hook that extracts all the revisions that were added during a "git push" and does some processing on each one (such as sending notification emails). This works great except when merging; e.g.: - I make some commits on branch1 and then push branch1. The post-receive hook processes the commits correctly. - I merge branch1 into branch2 and then push branch2. The post-receive hook processes all the merged commits a second time. How can I avoid this? Below is the beginning of my post-receive hook where I extract the commits that should be processed (at the end $COMMITS holds the list of commits to process). ``` #!/bin/sh REPO_PATH=`pwd` COMMITS='' SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # for each ref that was updated during the push while read OLD_REV NEW_REV REF_NAME; do OLD_REV="`git rev-parse $OLD_REV`" NEW_REV="`git rev-parse $NEW_REV`" if expr "$OLD_REV" : '0*$' >/dev/null; then # if the branch was created, add all revisions in the new branch; skip tags if ! expr "$REF_NAME" : 'refs/tags/' >/dev/null; then REF_REV="`git rev-parse $REF_NAME`" REF_NAME="`git name-rev --name-only $REF_REV`" COMMITS="$COMMITS `git rev-list $REF_NAME | git name-rev --stdin | grep -G \($REF_NAME.*\) | awk '{ print $1 }' | tr '\n' ' '`" fi elif expr "$NEW_REV" : '0*$' >/dev/null; then # don't think branch deletes ever hit a post-receive hook, so we should never get here printf '' else # add any commits in this push COMMITS="$COMMITS `git rev-parse --not --all | grep -v $(git rev-parse $REF_NAME) | git rev-list --reverse --stdin $(git merge-base $OLD_REV $NEW_REV)..$NEW_REV | tr '\n' ' '`" fi done ```

Original source