【问题标题】:Skip processing of Git revisions in post-receive hook that have already been previously processed在 post-receive 钩子中跳过之前已经处理过的 Git 修订的处理
【发布时间】:2012-05-02 18:06:55
【问题描述】:

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

  1. 我在 branch1 上做了一些提交,然后推送了 branch1。 post-receive 挂钩正确处理提交。
  2. 我将分支 1 合并到分支 2,然后推送分支 2。 post-receive 挂钩会再次处理所有合并的提交。

我怎样才能避免这种情况?下面是我的 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

【问题讨论】:

    标签: git git-post-receive


    【解决方案1】:

    看看$(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_typecreate 如果$oldrev 是全零,否则它是@987654327 @;$oldrev 是最近从标准输入读取的行中的旧版本 SHA1;$newrev 是新版本 SHA1;$refname 是全名,例如,refs/heads/topic。)

    【讨论】:

    • 谢谢你,它正是我需要的。使用此代码的剩余极端情况是当您进行提交时,将其合并到另一个分支中,然后一次将两个更改推送到一起。在这种情况下,每个分支中的修订都可以从另一个分支访问,因此根本不会处理它。不过,这对我们来说目前是可以接受的,因为这种情况比较少见。
    • 有趣!如果您在 pre-receieve 钩子而不是 post-receive 中执行此操作,它可能会扫描两次,因为这两个 ref 都不会更新。也许如果您在更新钩子中执行它们,它将只扫描一次? (显然,我自己没有尝试过。)
    【解决方案2】:

    我们所做的是将之前处理过的提交的哈希值保存在一个文本文件中。每次钩子运行时,它都会查看该文件以检查给定的提交是否已被处理。如果它尚未处理该提交,请对其进行处理,然后将该提交记录到文件中。

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

    【讨论】:

    • 改善这一点的一种方法是简单地定期修剪文件,这与您关于合并分支频率的政策保持一致。
    • 感谢您的回答。我将采用上面 torek 的解决方案,但您的回答也将避免我在那里提到的剩余极端情况。但是,在文本文件中跟踪提交比我现在想介绍的要复杂得多。
    【解决方案3】:

    我们通过让 post-receive 钩子在遇到合并提交(具有两个或更多父项的提交)时停止处理来做到这一点。这在推动合并时需要一些纪律,以确保不会抛出其他“真实”提交。规则是始终在合并之前推送,然后分别推送合并。

    【讨论】:

      【解决方案4】:

      我完全在接收后挂钩中实现了这一点。它只通知 trac 自上次获取以来的新提交不重复,无论新提交是同时推送到单个分支还是多个分支。此方法在您的 git 目录中保留一个名为 TRAC_HEAD 的文件,用于跟踪哪些提交已被处理。

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

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

      【讨论】:

        猜你喜欢
        • 2014-05-13
        • 1970-01-01
        • 2021-09-30
        • 2019-01-09
        • 1970-01-01
        • 2013-06-26
        • 1970-01-01
        • 2012-02-17
        相关资源
        最近更新 更多