65.9K
CodeProject 正在变化。 阅读更多。
Home

TortoiseSVN 提交前钩子 (Pre-commit Hook) in C# - 避免麻烦!

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (25投票s)

2013年1月15日

CPOL

4分钟阅读

viewsIcon

70424

你是否曾经提交过本应保留在自己机器上的代码?我们都会添加临时的hack来让开发或调试更轻松... 而且很容易忘记它们!

几乎每个创建或调试程序的人都会对代码进行临时更改,使当前任务更容易,但永远不应该进入存储库。而且几乎每个人都曾意外地将这样的代码放入下一个修订版中。如果幸运的话,错误会很快被发现,结果只会有一点羞愧,如果不是...

如果有一种方法可以标记“不可提交”的代码就好了...

你可以做到,而且很简单!

TortoiseSVN 允许你设置所谓的提交前钩子 (pre-commit hook)。它是一个在用户点击“SVN提交”窗口中的“确定”按钮时运行的程序(或脚本)。例如,钩子可以检查已修改文件的内容,并在认为合适时阻止提交。Tortoise 钩子与 Subversion 钩子的区别在于它们是在本地执行的,而不是在托管存储库的服务器上执行。因此,你不必担心你的钩子是否会被管理员接受,或者它是否在服务器上运行(例如,服务器可能没有安装 .NET),你也不会影响存储库其他用户的体验。客户端钩子也更快。

关于钩子的详细描述可以在 Tortoise 的帮助文件的“4.30.8. 客户端钩子脚本”一章中找到。

Tortoise 支持 7 种钩子:start-commit, pre-commit, post-commit, start-update, pre-update, post-update 和 pre-connect。我们关注的是pre-commit动作。钩子的本质是检查添加或修改的文件中是否包含临时代码标记。我们的标记可以是放置在临时代码上方的注释中的“NOT_FOR_REPO”文本。

这是整个钩子的代码 – 一个简单的控制台应用程序,可以拯救你。 Smile | :)

using System;
using System.IO;
using System.Text.RegularExpressions;

namespace NotForRepoPreCommitHook
{
    class Program
    {
        const string NotForRepoMarker = "NOT_FOR_REPO";

        static void Main(string[] args)
        {              
            string[] affectedPaths = File.ReadAllLines(args[0]);

            Regex fileExtensionPattern = new Regex(@"^.*\.(cs|js|xml|config)$", RegexOptions.IgnoreCase);

            foreach (string path in affectedPaths)
            {
                if (fileExtensionPattern.IsMatch(path) && File.Exists(path))
                {
                    if (ContainsNotForRepoMarker(path))
                    {
                        string errorMessage = string.Format("{0} marker found in {1}", 
                                              NotForRepoMarker, path);
                        Console.Error.WriteLine(errorMessage);    
                        Environment.Exit(1);  
                    }
                }
            }             
        }

        static bool ContainsNotForRepoMarker(string path)
        {
            StreamReader reader = File.OpenText(path);

            try
            {
                string line = reader.ReadLine();

                while (line != null)
                {
                    if (line.Contains(NotForRepoMarker))
                    {
                        return true;
                    }

                    line = reader.ReadLine();
                }
            }
            finally
            {
                reader.Close();
            }  

            return false;
        }
    }
}

TSVN 使用四个参数调用 pre-commit 钩子。我们只对第一个感兴趣。它包含到 *.tmp 文件的路径。在此文件中,有一个当前提交影响的文件的列表。每一行是一个路径。加载列表后,文件按扩展名过滤(如果你不想处理所有类型的文件,则很有用)。检查文件是否存在也很重要 – 来自 *.tmp 文件的列表也包含已删除文件的路径!由 NotForRepoMarker 常量表示的标记的检测是通过 ContainsNotForRepoMarker 方法实现的。尽管它很简单,但它提供了良好的性能。在我的(中端)笔记本电脑上,处理 100 MB 的文件需要不到一秒钟。如果找到标记,程序将以错误代码(不同于 0 的值)退出。在退出之前,有关包含标记的文件的信息将发送到标准错误输出(通过 Console.Error)。此消息将显示在 Tortoise 窗口中。

代码很简单,不是吗?此外,钩子的安装也很简单!

要附加钩子,请从 Tortoise 的上下文菜单中选择“设置”项。然后选择“钩子脚本”元素并单击“添加…”按钮。将出现如下窗口

TSVN hooks configuration window

将“钩子类型”设置为“提交前钩子 (Pre-Commit Hook)”。在“工作副本路径”字段中填写包含存储库本地副本的目录的路径(不同的文件夹可以有不同的钩子)。在“要执行的命令行”字段中,设置实现钩子的应用程序的路径。选中“等待脚本完成”(这很重要!)和“在运行时隐藏脚本”选项(后者将防止显示控制台窗口)。按下“确定”按钮,就完成了,钩子就安装好了!

现在用“NOT_FOR_REPO”注释标记一些代码,并尝试执行提交。你应该看到类似这样的内容

Operation blocked by pre-commit hook

请注意“无钩子重试”按钮 – 它允许通过忽略钩子来完成提交。

我们现在有一个钩子,可以防止临时代码的提交。人们可能还想创建一个钩子来强制填写日志消息,阻止提交 *.log 文件等。你的私人钩子 – 你来决定!如果其中一些钩子对整个团队有用,你随时可以将它们改造成 Subversion 钩子。

在 TortoiseSVN 1.7.8/Subversion 1.7.6 上测试。

更新 2014 年 3 月 24 日:强调了检查“等待脚本完成”选项 - 如果没有它,钩子将不会阻止提交!

更新 2013 年 9 月 17 日(附加信息):你可以在包含多个存储库检出的父文件夹上设置钩子。如果你愿意牺牲一点性能来获得额外的保护,你可以在检查 NotForRepoMarker 标记之前放弃过滤文件。

© . All rights reserved.