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

梦想的初衷 提交于 2019-11-30 23:13:31

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

What we do is to keep the hash of the previously processed commits in a text file. Every time the hook runs, it looks in that file to check if a given commit has already been processed or not. If it did not process that commit yet, process it and then log that commit to the file.

This is not very scalable, as the text files would only grow as more commits are added to the repository and the time to check for a given commit would also grow.

We did this by having the post-receive hook stop processing when it encountered a merge commit (a commit with two or more parents). This requires a bit of discipline when pushing merges to ensure that other "real" commits aren't thrown out. The discipline is to always push before merging and then push the merge separately.

I implemented this completely in a post-receive hook. It notifies trac of only new commits since the last fetch without duplicating, regardless of whether the new commits were pushed to a single branch or multiple branches at the same time. This method keeps a file called TRAC_HEAD in your git directory for tracking which commits have already been processed.

It is recommended that you run cat refs/heads/* > TRAC HEAD in your .git directory before enabling the hook.

#!/bin/sh
#
# Reads and notifies trac of only new commits that have not yet been dealt with.
#
# The "post-receive" script is run after receive-pack has accepted a pack
# and the repository has been updated.  It is passed arguments in through
# stdin in the form
#  <oldrev> <newrev> <refname>
# For example:
#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
#

TRAC_PATH="/path/to/trac/env"

# Read the standard input
while read oldrev newrev refname ; do

        echo "Processing branch: $refname"

        # Read the last revisions for each branch from TRAC_HEAD
        exclusions=$(cat TRAC_HEAD | uniq |  sed -e 's/^/^/' -e 's/ / ^/g' | xargs echo)

        echo "Exclusion list: $exclusions"

        git rev-list --reverse $newrev $exclusions | while read rev ; do
                trac-admin $TRAC_PATH changeset added '(default)' $rev
                echo "Processed: $rev"
        done

        # Add to the exclusions file the latest revision from this branch
        echo $newrev >> TRAC_HEAD

done

# Update the TRAC_HEAD file
cat refs/heads/* > TRAC_HEAD
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!