代码之家  ›  专栏  ›  技术社区  ›  Wim Deblauwe

SVN预提交钩子,用于避免更改标签子目录

  •  40
  • Wim Deblauwe  · 技术社区  · 16 年前

    是否有人对如何添加预提交钩子以避免更改标签子目录有明确的说明?

    我已经在互联网上搜索了很多。我找到了这个链接: SVN::Hooks::DenyChanges ,但我似乎无法汇编东西。

    11 回复  |  直到 11 年前
        1
  •  39
  •   apinstein    16 年前

    我没有足够的声誉来“评论”Raim上面的回答,但他的工作做得很好,除了一个例外,他的grep模式是错误的。

    我只是使用下面的钩子作为我的预提交钩子(我没有现有的钩子,在这种情况下你需要合并):

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/opt/local/bin/svnlook
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" | grep "^U\W.*\/tags\/" && /bin/echo "Cannot commit to tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    

    Raim的grep模式的唯一问题是,它只匹配位于仓库“根”的“标签”。由于我的仓库中有几个项目,他编写的脚本允许在标记分支上进行提交。

    此外,一定要按照指示使用chmod+x,否则你会认为它奏效了——b/c提交失败了,但b/c失败了——它无法执行预提交钩子,不是因为钩子奏效了。

    这真的很棒,谢谢Raim。比所有其他建议更好、更轻,因为它没有依赖关系!

        2
  •  16
  •   raimue    16 年前

    以下是一个简短的shell脚本,用于防止在创建标签后提交标签:

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/usr/bin/svnlook
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" | grep "^U\W*tags" && /bin/echo "Cannot commit to tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    

    将此保存在 hooks/pre-commit 对于您的Subversion存储库,并使其可执行 chmod +x .

        3
  •  7
  •   mcdon    15 年前

    这是我的windows批处理文件预提交钩子。如果用户是管理员,则将跳过其他检查。它检查提交消息是否为空,以及提交是否指向标签。注意:在其他平台上,findstr是grep的替代品。

    它检查提交是否指向标记的方式是,首先检查svnlook changed是否包含“tags/”。然后检查svnlook是否更改了匹配项“^A”。 标签/[^/] /$”,这意味着它将检查您是否在tags/下添加新文件夹。

    允许用户创建新项目。预提交钩子允许用户创建trunk/tags/和branches/文件夹。不允许用户删除trunk/tags/和branches/文件夹。这将适用于单个或多个项目存储库。

     @echo off
     rem This pre-commit hook will block commits with no log messages and blocks commits on tags.
     rem Users may create tags, but not modify them.
     rem If the user is an Administrator the commit will succeed.
    
     rem Specify the username of the repository administrator
     rem commits by this user are not checked for comments or tags
     rem Recommended to change the Administrator only when an admin commit is neccessary
     rem then reset the Administrator after the admin commit is complete
     rem this way the admin user is only an administrator when neccessary
     set Administrator=Administrator
    
     setlocal
    
     rem Subversion sends through the path to the repository and transaction id.
     set REPOS=%1%
     set TXN=%2%
    
     :Main
     rem check if the user is an Administrator
     svnlook author %REPOS% -t %TXN% | findstr /r "^%Administrator%$" >nul
     if %errorlevel%==0 (exit 0)
    
     rem Check if the commit has an empty log message
     svnlook log %REPOS% -t %TXN% | findstr . > nul
     if %errorlevel% gtr 0 (goto CommentError)
    
     rem Block deletion of branches and trunk
     svnlook changed %REPOS% -t %TXN% | findstr /r "^D.*trunk/$ ^D.*branches/$" >nul
     if %errorlevel%==0 (goto DeleteBranchTrunkError)
    
     rem Check if the commit is to a tag
     svnlook changed %REPOS% -t %TXN% | findstr /r "^.*tags/" >nul
     if %errorlevel%==0 (goto TagCommit)
     exit 0
    
     :DeleteBranchTrunkError
     echo. 1>&2
     echo Trunk/Branch Delete Error: 1>&2
     echo     Only an Administrator may delete the branches or the trunk. 1>&2
     echo Commit details: 1>&2
     svnlook changed %REPOS% -t %TXN% 1>&2
     exit 1
    
     :TagCommit
     rem Check if the commit is creating a subdirectory under tags/ (tags/v1.0.0.1)
     svnlook changed %REPOS% -t %TXN% | findstr /r "^A.*tags/[^/]*/$" >nul
     if %errorlevel% gtr 0 (goto CheckCreatingTags)
     exit 0
    
     :CheckCreatingTags
     rem Check if the commit is creating a tags/ directory
     svnlook changed %REPOS% -t %TXN% | findstr /r "^A.*tags/$" >nul
     if %errorlevel% == 0 (exit 0)
     goto TagsCommitError
    
     :CommentError
     echo. 1>&2
     echo Comment Error: 1>&2
     echo     Your commit has been blocked because you didn't enter a comment. 1>&2
     echo     Write a log message describing your changes and try again. 1>&2
     exit 1
    
     :TagsCommitError
     echo. 1>&2
     echo %cd% 1>&2
     echo Tags Commit Error: 1>&2
     echo     Your commit to a tag has been blocked. 1>&2
     echo     You are only allowed to create tags. 1>&2
     echo     Tags may only be modified by an Administrator. 1>&2
     echo Commit details: 1>&2
     svnlook changed %REPOS% -t %TXN% 1>&2
     exit 1
    
        4
  •  6
  •   coudenysj    15 年前

    这个anwser比日期晚了很多,但我发现了svnlook-changed命令的--copy info参数。

    此命令的输出在第三列中添加了一个“+”,因此您知道这是一个副本。您可以检查对tags目录的提交,并且只允许存在“+”的提交。

    我在中添加了一些输出 my blog post .

        5
  •  4
  •   Nick Brooks    16 年前

    聚会很晚了,但我写了一个基于log-policy.py脚本的python预提交钩子 http://subversion.tigris.org/ .

    此脚本应该执行您想要的操作,但它也会检查日志消息是否存在,尽管这应该很容易从脚本中删除。

    一些注意事项:

    • 我是Python新手,所以它很可能会写得更好
    • 它只在Windows 2003上使用Python 2.5和Subversion 1.4进行了测试。

    要求:

    • 颠覆
    • python
    • Python的Subversion绑定

    最后,代码:

    #!/usr/bin/env python
    
    #
    # pre-commit.py:
    #
    # Performs the following:
    #  - Makes sure the author has entered in a log message.
    #  - Make sure author is only creating a tag, or if deleting a tag, author is a specific user
    #
    # Script based on http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/log-police.py
    #
    # usage: pre-commit.py -t TXN_NAME REPOS
    # E.g. in pre-commit.bat (under Windows)
    #   python.exe {common_hooks_dir}\pre_commit.py -t %2 %1
    #
    
    
    import os
    import sys
    import getopt
    try:
      my_getopt = getopt.gnu_getopt
    except AttributeError:
      my_getopt = getopt.getopt
    
    import re
    
    import svn
    import svn.fs
    import svn.repos
    import svn.core
    
    #
    # Check Tags functionality
    #
    def check_for_tags(txn):
      txn_root = svn.fs.svn_fs_txn_root(txn)
      changed_paths = svn.fs.paths_changed(txn_root)
      for path, change in changed_paths.iteritems():
        if is_path_within_a_tag(path): # else go to next path
          if is_path_a_tag(path):
            if (change.change_kind == svn.fs.path_change_delete):
              if not is_txn_author_allowed_to_delete(txn):
                sys.stderr.write("\nOnly an administrator can delete a tag.\n\nContact your Subversion Administrator for details.")
                return False
            elif (change.change_kind != svn.fs.path_change_add):
              sys.stderr.write("\nUnable to modify " + path + ".\n\nIt is within a tag and tags are read-only.\n\nContact your Subversion Administrator for details.")
              return False
            # else user is adding a tag, so accept this change
          else:
            sys.stderr.write("\nUnable to modify " + path + ".\n\nIt is within a tag and tags are read-only.\n\nContact your Subversion Administrator for details.")
            return False
      return True
    
    def is_path_within_a_tag(path):
      return re.search('(?i)\/tags\/', path)
    
    def is_path_a_tag(path):
      return re.search('(?i)\/tags\/[^\/]+\/?$', path)
    
    def is_txn_author_allowed_to_delete(txn):
      author = get_txn_property(txn, 'svn:author')
      return (author == 'bob.smith')
    
    #
    # Check log message functionality
    #
    def check_log_message(txn):
      log_message = get_txn_property(txn, "svn:log")
      if log_message is None or log_message.strip() == "":
        sys.stderr.write("\nCannot enter in empty commit message.\n")
        return False
      else:
        return True
    
    def get_txn_property(txn, prop_name):
      return svn.fs.svn_fs_txn_prop(txn, prop_name)
    
    def usage_and_exit(error_msg=None):
      import os.path
      stream = error_msg and sys.stderr or sys.stdout
      if error_msg:
        stream.write("ERROR: %s\n\n" % error_msg)
      stream.write("USAGE: %s -t TXN_NAME REPOS\n"
                   % (os.path.basename(sys.argv[0])))
      sys.exit(error_msg and 1 or 0)
    
    def main(ignored_pool, argv):
      repos_path = None
      txn_name = None
    
      try:
        opts, args = my_getopt(argv[1:], 't:h?', ["help"])
      except:
        usage_and_exit("problem processing arguments / options.")
      for opt, value in opts:
        if opt == '--help' or opt == '-h' or opt == '-?':
          usage_and_exit()
        elif opt == '-t':
          txn_name = value
        else:
          usage_and_exit("unknown option '%s'." % opt)
    
      if txn_name is None:
        usage_and_exit("must provide -t argument")
      if len(args) != 1:
        usage_and_exit("only one argument allowed (the repository).")
    
      repos_path = svn.core.svn_path_canonicalize(args[0])
    
      fs = svn.repos.svn_repos_fs(svn.repos.svn_repos_open(repos_path))
      txn = svn.fs.svn_fs_open_txn(fs, txn_name)
    
      if check_log_message(txn) and check_for_tags(txn):
        sys.exit(0)
      else:
        sys.exit(1)
    
    if __name__ == '__main__':
      sys.exit(svn.core.run_app(main, sys.argv))
    
        6
  •  4
  •   Michael-O    13 年前

    之前编写的大多数脚本都是不完整的,因为有几个案例没有涵盖在内。这是我的剧本:

    contains_tags_dir=`$SVNLOOK changed --copy-info -t "$TXN" "$REPOS" | head -1 | egrep "+\/tags\/.*$" | wc -l | sed "s/ //g"`
    
    if [ $contains_tags_dir -gt 0 ]
    then
      tags_dir_creation=`$SVNLOOK changed --copy-info -t "$TXN" "$REPOS" | head -1 | egrep "^A       .+\/tags\/$" | wc -l | sed "s/ //g"`
      if [ $tags_dir_creation -ne 1 ]
      then
        initial_add=`$SVNLOOK changed --copy-info -t "$TXN" "$REPOS" | head -1 | egrep "^A \+ .+\/tags\/.+\/$" | wc -l | sed "s/ //g"`
        if [ $initial_add -ne 1 ]
        then
          echo "Tags cannot be changed!" 1>&2
          exit 1
        fi
      fi
    fi
    

    这可能看起来很复杂,但你必须确保自己 /tags 您可以创建 /标签 如果它不存在,以及所有后续文件夹。任何其他更改都被阻止。之前的脚本几乎都没有涵盖Subversion书中描述的所有情况 svnlook changed ... .

        7
  •  3
  •   Vinay Sahni    14 年前

    接受的答案会阻止更新标记中的文件,但不会阻止向标记中添加文件。以下版本同时处理这两个问题:

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    SVNLOOK="/home/staging/thirdparty/subversion-1.6.17/bin/svnlook"
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" --copy-info| grep -v "^ " | grep -P '^[AU]   \w+/tags/' && /bin/echo "Cannot update tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    
        8
  •  1
  •   Marián Černý    11 年前

    我的版本只允许创建和删除标签。这应该能处理所有特殊情况(如添加文件、更改属性等)。

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    SVNLOOK=/usr/local/bin/svnlook
    
    output_error_and_exit() {
        echo "$1" >&2
        exit 1
    }
    
    changed_tags=$( $SVNLOOK changed -t "$TXN" "$REPOS" | grep "[ /]tags/." )
    
    if [ "$changed_tags" ]
    then 
        echo "$changed_tags" | egrep -v "^[AD] +(.*/)?tags/[^/]+/$" && output_error_and_exit "Modification of tags is not allowed."
    fi 
    
    exit 0
    
        9
  •  1
  •   Ferenc Kiss    9 年前

    如果您使用的是JIRA,则可以使用名为 Commit Policy 保护存储库中的路径 无需编写自定义挂钩 .

    怎么用?使用名为的条件 Changed files must match a pattern .

    它有一个正则表达式类型参数,必须与提交中的每个文件匹配,否则提交将被拒绝。所以,在你的例子中,你应该使用一个正则表达式,意思是“不以前缀/tags/开头”。

    (您可以使用同一插件实现许多其他智能检查。)

    免责声明:我是一名开发人员,正在开发这个付费插件。

        10
  •  1
  •   Balmipour    9 年前

    由于第一个答案没有阻止添加/支持文件,也没有阻止创建新标签,以及许多其他不完整或有错误的地方,我对其进行了修改

    这是我的预提交钩子: 目标是:

    • 不允许对标签进行提交(文件添加/抑制/更新)
    • 不要阻止创建标签

    ---------文件“预提交”(放入存储库 文件夹)---------

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/usr/bin/svnlook
    
    #Logs
    #$SVNLOOK changed -t "$TXN" "$REPOS" > /tmp/changes
    #echo "$TXN" > /tmp/txn
    #echo "$REPOS" > /tmp/repos
    
    # Committing to tags is not allowed
    # Forbidden changes are Update/Add/Delete.  /W = non alphanum char  Redirect is necessary to get the error message, since regular output is lost.
    # BUT, we must allow tag creation / suppression
    
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^A\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 101
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^U\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 102
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^D\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 104
    
    # All checks passed, so allow the commit.
    exit 0;
    

    ---------文件“预提交”结束---------

    此外,我制作了2个shell脚本,在svn的每个项目中复制我的钩子: 将repo设置为只读:

    ---------脚本“setOneRepoTagsReadOnly.sh”---------

    #!/bin/sh
    
    cd /var/svn/repos/svn
    zeFileName=$1/hooks/pre-commit
    /bin/cp ./CUSTOM_HOOKS/pre-commit $zeFileName
    chown www-data:www-data $zeFileName
    chmod +x $zeFileName
    

    ---------文件末尾“setOneRepoTagsReadOnly.sh”---------

    一个是为每个repo调用它,使我的所有repo都是只读的:

    ---------文件“makeTagsReadOnly.sh”---------

    #!/bin/shs/svn                                                                                                                                                                         
    #Lists all repos, and adds the pre-commit hook to protect tags on each of them
    find /var/svn/repos/svn/ -maxdepth 1 -mindepth 1 -type d -execdir '/var/svn/repos/svn/setOneRepoTagsReadOnly.sh' \{\} \;
    

    ---------文件“makeTagsReadOnly.sh”的末尾---------

    我直接从svn“根”(在我的例子中是/var/svn/repos/svn)执行这些脚本。 顺便说一句,可以设置一个cron任务,通过每天执行这些脚本来自动修改新的存储库

    希望它能有所帮助。

        11
  •  0
  •   Dan Lowe    9 年前

    列出的答案很好,但没有一个完全符合我的要求。我想允许轻松创建标签,但一旦创建,它们应该是完全只读的。

    我还想防止这种愚蠢的情况,如果你这样做:

    svn copy myrepo/trunk myrepo/tags/newrelease
    

    第一次一切都很好。但第二次,如果标签已经存在,你最终会得到 myrepo/tags/newrelease/trunk .

    我的预提交钩子将查找任何预先存在的SVN目录匹配项 (repo)/tags/(tag)/ 如果找到则失败:

    $SVNLOOK tree -N --full-paths "$REPOS" "`$SVNLOOK changed -t "$TXN" "$REPOS" \
      | sed 's/[A-Z][[:space:]]*\([^/]*\)\/tags\/\([^/]*\)\/.*/\1\/tags\/\2\//' \
      | head -n 1`" \
      && echo "Tag already exists, commit rejected." >&2 \
      && exit 1