40

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

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

4

11 回答 11

39

我没有足够的声誉来“评论”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 ,否则您会认为它在提交失败时起作用,但在提交失败时它失败了,因为它无法执行预提交挂钩,而不是因为挂钩起作用。

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

于 2009-03-15T22:58:40.877 回答
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.

于 2009-02-23T01:16:22.173 回答
7

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

它检查提交是否是标记的方式,它首先检查 svnlook 更改是否包含“tags/”。然后它会检查 svnlook changed 是否匹配 "^A.tags /[^/] /$",这意味着它会检查你是否在 tags/ 下添加一个新文件夹。

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

 @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
于 2010-08-23T17:46:54.573 回答
6

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

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

我在我的博客文章中添加了一些输出。

于 2010-08-20T07:17:45.523 回答
4

派对很晚了,但是我写了一个 python 预提交钩子,它基于http://subversion.tigris.org/上的 log-police.py 脚本。

这个脚本应该做你想做的事,但是它也会检查日志消息是否存在,尽管这应该很容易从脚本中删除。

一些警告:

  • 我是 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))
于 2009-01-30T23:30:45.477 回答
4

大多数以前编写的脚本都是不完整的,因为有几个案例没有涉及。这是我的脚本:

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您可以创建它以及所有后续文件夹。/tags任何其他更改都会被阻止。几乎没有一个以前的脚本涵盖了 Subversion 书中描述的所有情况svnlook changed ...

于 2011-11-17T08:46:58.120 回答
3

接受的答案会阻止更新标签中的文件,但不会阻止将文件添加到标签中。以下版本同时处理:

#!/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
于 2011-07-19T15:38:05.133 回答
1

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

#!/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
于 2013-11-11T14:42:03.723 回答
1

由于第一个答案没有阻止添加/支持文件,并阻止了新标签的创建,以及许多其他不完整或有问题的地方,我重新设计了它

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

  • 禁止提交标签(文件添加/抑制/更新)
  • 不要阻止标签的创建

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

#!/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”结束 ---------

并且为每个回购调用它,以使我的所有回购都只读:

--------- 文件“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“root”(在我的例子中是/var/svn/repos/svn)执行那些脚本。顺便说一句,可以将 cron 任务设置为通过每天执行这些脚本来自动修改新的 repos

希望能帮助到你。

于 2014-07-11T18:25:51.990 回答
1

如果您使用 JIRA,则可以使用名为Commit Policy的附加组件来保护存储库中的路径,而无需编写自定义钩子

如何?使用名为Changed files must match a pattern的条件。

它有一个正则表达式类型参数,该参数必须与提交中的每个文件匹配,否则提交将被拒绝。因此,在您的情况下,您应该使用表示“不以前缀 /tags/ 开头”的正则表达式。

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

免责声明:我是一名开发此付费插件的开发人员。

于 2015-09-29T09:26:41.570 回答
0

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

我还想防止出现以下愚蠢情况:

svn copy myrepo/trunk myrepo/tags/newrelease

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

我的 pre-commit 挂钩将查找任何预先存在的 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
于 2015-12-18T15:14:26.943 回答