跳过已经处理过的后接收挂钩中Git修订的处理

时间:2012-05-02 18:06:55

标签: git git-post-receive

我有一个git post-receive hook,它提取在“git push”期间添加的所有修订,并对每个修订进行一些处理(例如发送通知电子邮件)。这种方法很有效,除非合并时; e.g:

  1. 我在branch1上做了一些提交,然后推送branch1。 post-receive挂钩正确处理提交。
  2. 我将branch1合并到branch2然后推入branch2。 post-receive挂钩第二次处理所有合并的提交。
  3. 我该如何避免这种情况?下面是我的post-receive钩子的开头,我提取了应该处理的提交(最后$ COMMITS保存了要处理的提交列表)。

    #!/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
    

4 个答案:

答案 0 :(得分:6)

看看$(prefix)/share/git-core/contrib/hooks/post-receive-email,这就是我想要的(我认为)。基本上它使用git for-each-ref来查找所有分支的名称,然后排除除了正在更新的分支之外的某个分支可以访问的每个提交:

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

(我在这里对其进行了简化,希望在我的剪切和粘贴作业中没有任何损坏。这里的输入是:$change_type create $oldrev如果update全部 - 零,否则它是$oldrev; $newrev是最近从stdin读取的行中的旧rev SHA1; $refname是新的rev SHA1; refs/heads/topic是全名例如,{{1}}。)

答案 1 :(得分:1)

我们所做的是将先前处理的提交的哈希值保存在文本文件中。每次钩子运行时,它都会查找该文件以检查是否已经处理了给定的提交。如果它还没有处理该提交,请处理它,然后将该提交记录到该文件中。

这不是很可扩展,因为文本文件只会随着更多提交添加到存储库而增长,并且检查给定提交的时间也会增加。

答案 2 :(得分:0)

我们通过在遇到合并提交(具有两个或更多父项的提交)时使用post-receive挂钩停止处理来完成此操作。这在推送合并时需要一些纪律,以确保不会抛弃其他“真正的”提交。该规则是在合并之前始终推送,然后分别推送合并。

答案 3 :(得分:0)

我在post-receive hook中完全实现了这个。无论新提交是同时推送到单个分支还是多个分支,它都会在上次提取 之后仅通知trac,而不会复制 。此方法在git目录中保留一个名为TRAC_HEAD的文件,用于跟踪已处理的提交。

建议您在启用挂钩之前在cat refs/heads/* > TRAC HEAD目录中运行.git

#!/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