Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#

TortoiseSVN Pre-commit Hook in C# - Save Yourself Some Troubles!

Rate me:
Please Sign up or sign in to vote.
4.98/5 (25 votes)
24 Mar 2014CPOL4 min read 67.7K   38   9
Have you ever committed code that was meant to stay on your machine? We all add temporary hacks to make development or debugging easier... And it's so easy to forget about them!

Probably everyone who creates or debugs a program happens to make temporary changes to the code that make the current task easier but should never get into the repository. And probably everyone has accidentally put such code into the next revision. If you are lucky enough, mistakes 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 the whole hook’s code – simple console application, that may save your ass. Smile | :)

C#
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” (it's important!) 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 useful 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 information): 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Poland Poland

Comments and Discussions

 
QuestionHow to make Console.WriteLine work? Pin
gqqnbig19-Jan-17 7:27
gqqnbig19-Jan-17 7:27 
QuestionHow to find the path of *.tmp file which will contain the list of files to be committed Pin
Member 1139443522-Jan-15 2:20
Member 1139443522-Jan-15 2:20 
GeneralWell done Pin
Rohan Deore4-Sep-14 20:52
professionalRohan Deore4-Sep-14 20:52 
GeneralNice work! Pin
ilikebeets19-Feb-14 18:00
ilikebeets19-Feb-14 18:00 
QuestionMy vote of 5 Pin
Mike Hankey6-Nov-13 4:34
mveMike Hankey6-Nov-13 4:34 
GeneralMy vote of 5 Pin
John B Oliver14-Feb-13 9:49
John B Oliver14-Feb-13 9:49 
GeneralWhat if Pin
Nerevar16-Jan-13 0:07
Nerevar16-Jan-13 0:07 
GeneralRe: What if Pin
morzel16-Jan-13 0:38
morzel16-Jan-13 0:38 
GeneralRe: What if Pin
Nerevar16-Jan-13 1:19
Nerevar16-Jan-13 1:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.