morzel.net

.net, js, html, arduino, java... no rants or clickbaits.

Short but very usueful regex – lookbehind, lazy, group and backreference

Recently, I wanted to extract calls to external system from log files and do some LINQ to XML processing on obtained data. Here’s a sample log line (simplified, real log was way more complicated but it doesn’t matter for this post):

Call:<getName seqNo="56789"><id>123</id></getName> Result:<getName seqNo="56789">John Smith</getName>

I was interested in XML data of the call:

<getName seqNo="56789">
  <id>123</id>
</getName>

Quick tip: super-easy way to get such nicely formatted XML in .NET 3.5 or later is to invoke ToString method on XElement object:

var xml = System.Xml.Linq.XElement.Parse(someUglyXmlString);     
Console.WriteLine(xml.ToString());

When it comes to log, some things were certain: 

  • call’s XML will be logged after “Call:” text on the beginning of line
  • call’s root element name will contain only alphanumerical chars or underscore
  • there will be no line brakes in call’s data
  • call’s root element name may also appear in the “Result” section

Getting to the proper information was quite easy thanks to Regex class:

Regex regex = new Regex(@"(?<=^Call:)<(\w+).*?</\1>");
string call = regex.Match(logLine).Value;

This short regular expressions has a couple of interesting parts. It may not be perfect but proved really helpful in log analysis. If this regex is not entirely clear to you - read on, you will need to use something similar sooner or later. 

Here’s the same regex with comments (RegexOptions.IgnorePatternWhitespace is required to process expression commented this way):

string pattern = @"(?<=^Call:) # Positive lookbehind for call marker
                   <(\w+)      # Capturing group for opening tag name
                   .*?         # Lazy wildcard (everything in between)
                   </\1>       # Backreference to opening tag name";   
Regex regex = new Regex(pattern, RegexOptions.IgnorePatternWhitespace);
string call = regex.Match(logLine).Value;

Positive lookbehind

(?<=Call:) is a lookaround or more precisely positive lookbehind. It’s a zero-width assertion that lets us check whether some text is preceded by another text. Here “Call:” is the preceding text we are looking for. (?<=something) denotes positive lookbehind. There is also negative lookbehind expressed by (?<!something).  With negative lookbehind we can match text that doesn’t have particular string before it. Lookaround checks fragment of the text but doesn't became part of the match value. So the result of this:

Regex.Match("X123", @"(?<=X)\d*").Value

Will be "123" rather than "X123".

.NET regex engine has lookaheads too. Check this awesome page if you want to learn more about lookarounds. 

Note: In some cases (like in our log examination example) instead of using positive lookaround we may use non-capturing group...

Capturing group

<(\w+) will match less-than sign followed by one or more characters from \w class (letters, digits or underscores). \w+ part is surrounded with parenthesis to create a group containing XML root name (getName for sample log line). We later use this group to find closing tag with the use of backreference. (\w+) is capturing group, which means that results of this group existence are added to Groups collection of Match object. If you want to put part of the expression into a group but you don’t want to push results into Groups collection you may use non-capturing group by adding a question mark and colon after opening parenthesis, like this: (?:something)

Lazy wildcard

.*? matches all characters except newline (because we are not using RegexOptions.Singleline) in lazy (or non-greedy) mode thanks to question mark after asterisk. By default * quantifier is greedy, which means that regex engine will try to match as much text as possible. In our case, default mode will result in too long text being matched:

<getName seqNo="56789"><id>123</id></getName> Result:<getName seqNo="56789">John Smith</getName>

Backreference

</\1> matches XML close tag where element's name is provided with \1 backreference. Remember the (\w+) group? This group has number 1 and by using \1 syntax we are referencing the text matched by this group. So for our sample log, </\1> gives us </getName>. If regex is complex it may be a good idea to ditch numbered references and use named references instead. You can name a group by <name> or ‘name’ syntax and reference it by using k<name> or k’name’. So your expression could look like this:

@"(?<=^Call:)<(?<tag>\w+).*?</\k<tag>>"

or like this:

@"(?<=^Call:)<(?'tag'\w+).*?</\k'tag'>"

The latter version is better for our purpose. Using < > signs while matching XML is confusing. In this case regex engine will do just fine with < > version but keep in mind that source code is written for humans…

Regular expressions look intimidating, but do yourself a favor and spend few hours practicing them, they are extremely useful (not only for quick log analysis)!

Fast pixel operations in .NET (with and without unsafe)

Bitmap class has GetPixel and SetPixel methods that let you acquire and change color of chosen pixels. Those methods are very easy to use but are also extremely slow. My previous post gives detailed explanation on the topic, click here if you are interested.

Fortunately you don’t have to use external libraries (or resign from .NET altogether) to do fast image manipulation. The Framework contains class called ColorMatrix that lets you apply many changes to images in an efficient manner. Properties such as contrast or saturation can be modified this way. But what about manipulation of individual pixels? It can be done too, with the help from Bitmap.LockBits method and BitmapData class…

Good way to test individual pixel manipulation speed is color difference detection. The task is to find portions of an image that have color similar to some chosen color. How to check if colors are similar? Think about color as a point in three dimensional space, where axes are: red, green and blue. Two colors are two points. The difference between colors is described by the distance between two points in RGB space.

Colors as points in 3D space diff = sqrt((C1R-C2R)2+(C1G-C2G)2+(C1B-C2B)2)

This technique is very easy to implement and gives decent results. Color comparison is actually a pretty complex matter though. Different color spaces are better suited for the task than RGB and human color perception should be taken into account (e. g. our eyes are more keen to detect difference in shades of green that in shades of blue). But let’s keep things simple here…

Our test image will be this Ultra HD 8K (7680x4320, 33.1Mpx) picture* (on this blog it’s of course scaled down to save bandwidth):

Color difference detection input image (scaled down for blog)

This is a method that may be used to look for R=253 G=129 B=84 pixels (aka “pink bra”). It sets matching pixels as white (the rest will be black):

static void DetectColorWithGetSetPixel(Bitmap image, byte searchedR, byte searchedG, int searchedB, int tolerance)
{
    int toleranceSquared = tolerance * tolerance;
    
    for (int x = 0; x < image.Width; x++)
    {
        for (int y = 0; y < image.Height; y++)
        {
            Color pixel = image.GetPixel(x, y);

            int diffR = pixel.R - searchedR;
            int diffG = pixel.G - searchedG;
            int diffB = pixel.B - searchedB;

            int distance = diffR * diffR + diffG * diffG + diffB * diffB;

            image.SetPixel(x, y, distance > toleranceSquared ? Color.Black : Color.White);
        }
    }
}

Above code is our terribly slow Get/SetPixel baseline. If we call it this way (named parameters for clarity):

DetectColorWithGetSetPixel(image, searchedR: 253, searchedG: 129, searchedB: 255, tolerance: 84);

we will receive following outcome:

Color difference detection output image (scaled down)

Result may be ok but having to wait over 84300ms* is a complete disaster! 

Now check out this method:

static unsafe void DetectColorWithUnsafe(Bitmap image, byte searchedR, byte searchedG, int searchedB, int tolerance)
{
    BitmapData imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    int bytesPerPixel = 3;

    byte* scan0 = (byte*)imageData.Scan0.ToPointer();
    int stride = imageData.Stride;

    byte unmatchingValue = 0;
    byte matchingValue = 255;
    int toleranceSquared = tolerance * tolerance;

    for (int y = 0; y < imageData.Height; y++)
    {
        byte* row = scan0 + (y * stride);

        for (int x = 0; x < imageData.Width; x++)
        {
            // Watch out for actual order (BGR)!
            int bIndex = x * bytesPerPixel;
            int gIndex = bIndex + 1;
            int rIndex = bIndex + 2;

            byte pixelR = row[rIndex];
            byte pixelG = row[gIndex];
            byte pixelB = row[bIndex];

            int diffR = pixelR - searchedR;
            int diffG = pixelG - searchedG;
            int diffB = pixelB - searchedB;

            int distance = diffR * diffR + diffG * diffG + diffB * diffB;

            row[rIndex] = row[bIndex] = row[gIndex] = distance > toleranceSquared ? unmatchingValue : matchingValue;
        }
    }

    image.UnlockBits(imageData);
}

It does exactly the same thing but runs for only 230ms over 360 times faster!

Above code makes use of Bitmap.LockBits method that is a wrapper for native GdipBitmapLockBits (GDI+, gdiplus.dll) function. LockBits creates a temporary buffer that contains pixel information in desired format (in our case RGB, 8 bits per color component). Any changes to this buffer are copied back to the bitmap upon UnlockBits call (therefore you should always use LockBits and UnlockBits as a pair). Bitmap.LockBits returns BitmapData object (System.Drawing.Imaging namespace) that has two interesting properties: Scan0 and Stride. Scan0 returns an address of the first pixel data. Stride is the width of single row of pixels (scan line) in bytes (with optional padding to make it dividable by 4). 

BitmapData layout

Please notice that I don’t use calls to Math.Pow and Math.Sqrt to calculate distance between colors. Writing code like this: 

double distance = Math.Sqrt(Math.Pow(pixelR - searchedR, 2) + Math.Pow(pixelG - searchedG, 2) + Math.Pow(pixelB - searchedB, 2));

to process millions of pixels is a terrible idea. Such line can make our optimized method about 25 times slower! Using Math.Pow with integer parameters is extremely wasteful and we don’t have to calculate square root to determine if distance is longer than specified tolerance.

Previously presented method uses code marked with unsafe keyword. It allows C# program to take advantage of pointer arithmetic. Unfortunately, unsafe mode has some important restrictions. Code must be compiled with \unsafe option and executed for fully trusted assembly. 

Luckily there is a Marshal.Copy method (from System.Runtime.InteropServices namespace) that can move data between managed and unmanaged memory. We can use it to copy image data into a byte array and manipulate pixels very efficiently. Look at this method:

static void DetectColorWithMarshal(Bitmap image, byte searchedR, byte searchedG, int searchedB, int tolerance)
{        
    BitmapData imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

    byte[] imageBytes = new byte[Math.Abs(imageData.Stride) * image.Height];
    IntPtr scan0 = imageData.Scan0;

    Marshal.Copy(scan0, imageBytes, 0, imageBytes.Length);
  
    byte unmatchingValue = 0;
    byte matchingValue = 255;
    int toleranceSquared = tolerance * tolerance;

    for (int i = 0; i < imageBytes.Length; i += 3)
    {
        byte pixelB = imageBytes[i];
        byte pixelR = imageBytes[i + 2];
        byte pixelG = imageBytes[i + 1];

        int diffR = pixelR - searchedR;
        int diffG = pixelG - searchedG;
        int diffB = pixelB - searchedB;

        int distance = diffR * diffR + diffG * diffG + diffB * diffB;

        imageBytes[i] = imageBytes[i + 1] = imageBytes[i + 2] = distance > toleranceSquared ? unmatchingValue : matchingValue;
    }

    Marshal.Copy(imageBytes, 0, scan0, imageBytes.Length);

    image.UnlockBits(imageData);
}

It runs for 280ms, so it is only slightly slower than unsafe version. It is CPU efficient but uses more memory then previous method – almost 100 megabytes for our test Ultra HD 8K image in RGB 24 format.

If you want to make pixel manipulation even faster you may process different parts of the image in parallel. You need to make some benchmarking first because for small images the cost of threading may be bigger than gains from concurrent execution. Here’s a quick sample of code that uses 4 threads to process 4 parts of the image simultaneously. It yields 30% time improvement on my machine. Treat is as a quick and dirty hint, this post is already to long…

static unsafe void DetectColorWithUnsafeParallel(Bitmap image, byte searchedR, byte searchedG, int searchedB, int tolerance)
{
    BitmapData imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    int bytesPerPixel = 3;

    byte* scan0 = (byte*)imageData.Scan0.ToPointer();
    int stride = imageData.Stride;

    byte unmatchingValue = 0;
    byte matchingValue = 255;
    int toleranceSquared = tolerance * tolerance;

    Task[] tasks = new Task[4];
    for (int i = 0; i < tasks.Length; i++)
    {
        int ii = i;
        tasks[i] = Task.Factory.StartNew(() =>
            {
                int minY = ii < 2 ? 0 : imageData.Height / 2;
                int maxY = ii < 2 ? imageData.Height / 2 : imageData.Height;

                int minX = ii % 2 == 0 ? 0 : imageData.Width / 2;
                int maxX = ii % 2 == 0 ? imageData.Width / 2 : imageData.Width;                        
                
                for (int y = minY; y < maxY; y++)
                {
                    byte* row = scan0 + (y * stride);

                    for (int x = minX; x < maxX; x++)
                    {
                        int bIndex = x * bytesPerPixel;
                        int gIndex = bIndex + 1;
                        int rIndex = bIndex + 2;

                        byte pixelR = row[rIndex];
                        byte pixelG = row[gIndex];
                        byte pixelB = row[bIndex];

                        int diffR = pixelR - searchedR;
                        int diffG = pixelG - searchedG;
                        int diffB = pixelB - searchedB;

                        int distance = diffR * diffR + diffG * diffG + diffB * diffB;

                        row[rIndex] = row[bIndex] = row[gIndex] = distance > toleranceSquared ? unmatchingValue : matchingValue;
                    }
                }
            });
    }

    Task.WaitAll(tasks);

    image.UnlockBits(imageData);
}

* Originally I had some triangles and squares as an illustration, but Victoria's Secret models (source) are better, huh? :) 

* .NET 4 console app, executed  on MSI GE620 DX laptop: Intel Core i5-2430M 2.40GHz (2 cores, 4 threads), 4GB DDR3 RAM, NVIDIA GT 555M 2GB DDR3, HDD 500GB 7200RPM, Windows 7 Home Premium x64.

Radio buttons for list items in MVC 4 – problem with id uniqueness

Let's suppose that we have some model that has a list property and we want to render some radio buttons for items of that list. Take the following basic setup as an example.

Main model class with list:

using System.Collections.Generic;

public class Team
{
    public string Name { get; set; }
    public List<Player> Players { get; set; }
}

List item class:

public class Player
{
    public string Name { get; set; }
    public string Level { get; set; }
}

There are three accepted values for player’s skill Level property: BEG (Beginner), INT (Intermediate) and ADV (Advanced) so we want three radio buttons (with labels) for each player in a team. Yup, normally we would rather use enum instead of a string for Level property, but let’s skip it here for the sake of simplicity…

Controller action method that returns sample data:

public ActionResult Index()
{
    var team = new Team() {
        Name = "Some Team",
        Players = new List<Player> {
               new Player() {Name = "Player A", Level="BEG"},
               new Player() {Name = "Player B", Level="INT"},
               new Player() {Name = "Player C", Level="ADV"}
        }
    };

    return View(team);
}

Here is our Index.cshtml view:

@model Team

<section>
    <h1>@Model.Name</h1>        

    @Html.EditorFor(model => model.Players)            
</section>

Notice that markup for Players is not created inside a loop. Instead, EditorTemplate is used. It’s a good practice since it makes code more clear and maintainable. Framework is smart enough to use code from template for each player on a list, because Team.Players property implements IEnumerable interface...

And here is Players.cshtml EditorTemplate:

@model Player
<div>
    <strong>@Model.Name:</strong>

    @Html.RadioButtonFor(model => model.Level, "BEG")
    @Html.LabelFor(model => model.Level, "Beginner")

    @Html.RadioButtonFor(model => model.Level, "INT")
    @Html.LabelFor(model => model.Level, "Intermediate")

    @Html.RadioButtonFor(model => model.Level, "ADV")
    @Html.LabelFor(model => model.Level, "Advanced")        
</div>

The code looks fine, nice strongly typed helpers that relay on lambda expressions (for compile-time checking and easier refactoring)... but there’s a catch: HTML markup that is generated by such code is actually seriously flawed. Check this snipped of web page source generated for the first player:

<div>
    <strong>Player A:</strong>  
 
    <input name="Players[0].Level" id="Players_0__Level" type="radio" checked="checked" value="BEG">
    <label for="Players_0__Level">Beginner</label>
 
    <input name="Players[0].Level" id="Players_0__Level" type="radio" value="INT">
    <label for="Players_0__Level">Intermediate</label>
 
    <input name="Players[0].Level" id="Players_0__Level" type="radio" value="ADV">
    <label for="Players_0__Level">Advanced</label>        
</div>

Players_0__Level id is used for three different radio buttons! Lack of uniqueness not only violates HTML specification and makes scripting hard but also causes label tags to not work properly (clicking on them doesn’t check their corresponding input element). 

Fortunately MVC framework contains TemplateInfo class that has GetFullHtmlFieldId method. This method returns id for DOM element. That id is constructed by appending name provided as method argument to an automatically determined prefix. This prefix takes into account nesting level and list item's index. Internally, GetFullHtmlFieldId uses TemplateInfo.HtmlFieldPrefix property and TagBuilder.CreateSanitizedId method so even if you pass some illegal characters to id suffix they will be replaced.

Here is modified EditorTemplate
@model Player
<div>
    <strong>@Model.Name:</strong>

    @{string rbBeginnerId = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("rbBeginner"); }
    @Html.RadioButtonFor(model => model.Level, "BEG", new { id = rbBeginnerId })
    @Html.LabelFor(model => model.Level, "Beginner",  new { @for = rbBeginnerId} )

    @{string rbIntermediateId = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("rbIntermediate"); }
    @Html.RadioButtonFor(model => model.Level, "INT", new { id = rbIntermediateId })
    @Html.LabelFor(model => model.Level, "Intermediate",  new { @for = rbIntermediateId })

    @{string rbAdvancedId = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("rbAdvanced"); }
    @Html.RadioButtonFor(model => model.Level, "ADV", new { id = rbAdvancedId })
    @Html.LabelFor(model => model.Level, "Advanced",  new { @for = rbAdvancedId })
</div>

Calls to ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId method let us obtain ids for radio buttons which are also used to set for attributes of labels. In MVC 3 there was no overload of LabelFor extension method that accepted htmlAttributes object. Luckily version 4 has it build-in.

Above code produces such markup:

<div>
    <strong>Player A:</strong>
 
    <input name="Players[0].Level" id="Players_0__rbBeginner" type="radio" checked="checked" value="BEG">
    <label for="Players_0__rbBeginner">Beginner</label>
 
    <input name="Players[0].Level" id="Players_0__rbIntermediate" type="radio" value="INT">
    <label for="Players_0__rbIntermediate">Intermediate</label>
 
    <input name="Players[0].Level" id="Players_0__rbAdvanced" type="radio" value="ADV">
    <label for="Players_0__rbAdvanced">Advanced</label>
</div>

Now inputs ids are unique and labels properly reference radio buttons via for attribute. Alright :) 

BTW: the weird name “radio buttons” for mutually exclusive option elements comes from buttons on radio receivers that were used to switch between stations (pushing one in automatically pushed the others out).

Coordinate system in HTML5 Canvas, drawing with y-axis value increasing upwards

Coordinate system in HTML5 Canvas is set up in such a way that its origin (0,0) is in the upper-left corner. This solution is nothing new in the world of screen graphics (e.g. the same goes for Windows Forms and SVG). CRT monitors, which were standard in the past, displayed picture lines from top to bottom and image within a line was created from left to right. So locating origin (0,0) in the upper-left corner was intuitive and it made creating hardware and software for handling graphics easier.

Unfortunately sometimes default coordinate system in canvas is a bit impractical. Let’s assume that you want to create projectile motion animation. It seems natural that for ascending projectile, the value of y coordinate should increase. But it will result in a weird effect of inverted trajectory:

Default coordinate system (y value increases downwards)

You can get rid of this problem by modifying y value that is passed to drawing function:

context.fillRect(x, offsetY - y, size, size);

For y = 0, projectile will be placed in a location determined by offsetY (to make y = 0 be the very bottom of the canvas, set offsetY equal to height of the canvas). The bigger the value of y the higher a projectile will be drawn. The problem is that you can have hundreds of places in your code that use y coordinate. If you forget to use offsetY just once the whole image may get destroyed. 

Luckily canvas lets you make changes to coordinate system by means of transformations. Two transformation methods will be useful for us: translate(x ,y) and scale(x, y). The former allows us to move origin to an arbitrary place, the latter is for changing size of drawn objects, but it may also be used to invert coordinates.

Single execution of the following code will move origin of coordinate system to point (0, offsetY) and establish y-axis values as increasing towards the top of the screen:

context.translate(0, offsetY);
context.scale(1, -1);

Translation and scaling of coordinate system. Click to enlarge...

But there’s a catch: the result of providing -1 as scale’s method second argument is that the whole image is created for inverted y coordinate. This applies to text too (calling fillText will render letters upside-down). Therefore before writing any text, you have to restore default y-axis configuration. Because manual restoring of canvas state is awkward, methods save() and restore() exist. These methods are for pushing canvas state on the stack and popping canvas state from the stack, respectively. It is recommended to use save method before doing transformations. Canvas state includes not only transformations but also values such as fill style or line width...

context.save();
 
context.fillStyle = 'red';
context.scale(2, 2);
context.fillRect(0, 0, 10, 10);
 
context.restore();
 
context.fillRect(0, 0, 10, 10);

Above code draws 2 squares: 

First square is red and is drawn with 2x scale. Second square is drawn with default canvas settings (color black and 1x scale). This occurs because right before any changes to scale and color, canvas state was save on the stack, later on it was restored before second square drawing.

TortoiseSVN pre-commit hook in C# - save yourself some troubles!

Probably everyone who creates or debugs a program happens to make temporary changes to the code that make current task easier but should never get into the repository. And probably everyone has accidentally put such code into next revision. If you are lucky enough, mistake will be revealed quickly and the only result will be a bit of shame, if not...

If only there was a way to mark “uncommitable” code...

You can do it and it’s pretty simple!

TortoiseSVN lets you set so-called pre-commit hook. It’s a program (or script) that is run when user clicks “OK” button in “SVN Commit” window. Hook can for example check content of modified files and block commit when deemed appropriate. Tortoise hooks differ from Subversion hooks in that they are executed locally and not on the server that hosts the repository. You therefore don’t have to worry whether your hook will be accepted by the admin or if it works on the server (e.g. server may not have .NET installed), you also don’t affect the experience of other users of the repository. Client-side hooks are quicker too.

Detailed description of hooks can be found in „4.30.8. Client Side Hook Scripts” chapter of Tortoises help file.

Tortoise supports 7 kinds of hooks: start-commit, pre-commit, post-commit, start-update, pre-update, post-update and pre-connect. We are concerned with pre-commit action. The essence of the hook is to check whether one of added or modified files contains temporary code marker. Our marker may be a “NOT_FOR_REPO” text put into a comment placed above temporary code.

This is whole hook’s code – simple console application, that may save your ass :)

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 calls pre-commit hook with four parameters. We are interested only in the first one. It contains a path to *.tmp file. In this file there is a list of files affected by current commit. Each line is one path. After loading the list, files are filtered by extension (useful if you don’t want to process files of all types). Checking if file exists is also important – the list from *.tmp file contains paths for deleted files too! Detection of the marker represented by NotForRepoMarker constant is realized by ContainsNotForRepoMarker method. Despite its simplicity it provides good performance. On mine (middle range) laptop, 100 MB file takes less than a second to process. If marker is found, program exits with error code (value different than 0). Before quitting, information about which file contains the marker is sent to standard error output (via Console.Error). This message will get displayed in Tortoise window.

The code is simple, isn’t it? In addition, hook installation is also trivial!

To attach hook, choose “Settings” item from Tortoise’s context menu. Then select “Hook scripts” element and click “Add…” button. Such window will appear:

TSVN hooks configuration window

Set „Hook Type” to „Pre-Commit Hook”. Fill “Working Copy Path” field with a path to the directory that contains local copy of the repo (different folders can have different hooks). In “Command Line To Execute” field, set path to the application that implements the hook. Check “Wait for the script to finish” and “Hide the script while running” options (the latter will prevent console window from showing). Press “OK” button and voila, hook is installed!

Now mark some code with “NOT_FOR_REPO” comment and try to execute commit. You should see something like that:

Operation blocked by pre-commit hook

Notice the „Retry without hooks” button – it allows commit to be completed by ignoring hooks.

We now have a hook that prevents from temporary code submission. One may also want to create a hook that enforces log message to be filled, blocks *.log files commits etc. Your private hooks – you decide! And if some of the hooks will be usefull for the whole team, you can always remake them as Subversion hooks.

Tested on TortoiseSVN 1.7.8/Subversion 1.7.6.

Update 24.03.2014: Added emphasis to checking "Wait for the script to finish" option - without it hook will not block commits!

Update 17.09.2013: (additional info): You may set hook on a parent folder which contains multiple repositories checkouts. If you are willing to sacrifice a bit of performance for added protection you may resign from filtering files before checking for NotForRepoMarker marker.