Click here to Skip to main content
15,883,705 members
Articles / Desktop Programming / Windows Forms

Subversion (SVN) Post-Commit Organizer for Shadow Copy

Rate me:
Please Sign up or sign in to vote.
4.54/5 (7 votes)
13 Apr 2009CPOL3 min read 36.8K   391   23   4
Subversion (SVN) Post-Commit Organizer for Shadow Copy

Introduction

Subversion, popularly abbreviated as SVN, is a powerful open source version control system. A version control system organizes the source code in such a way that you can keep track of the changes you make, mark some milestones throughout the evolution of the software and perhaps differentiate some specialized versions according to some certain needs, i.e. platform or OS, professional or standard, etc. Subversion accomplishes these needs as it should and goes well beyond, too.

If you are developing software, you have to have some conventions. Separating the production environment on which the software actually runs to deliver service to clients and the development environment on which you debug your code or experiment on new features.

In our organization, we had already adopted SVN for managing the desktop applications but had been using Visual Sourcesafe for web applications as the version control and code sharing system. Everything was straightforward until we found out that SVN did not have a built-in shadow copy feature. As the name implies, this feature enables us to have a synchronized copy of the code-base at some specified location.

We were not aware of this fact as we did not need it for desktop applications. But for web applications, we needed to have the latest copy of the code-base somewhere on the web server. After examining a few links Hook Scripts, we discovered the post commit hook - Repository Hooks - which is a notification of a successful commit and it has a wide range of usability. See also: Creating a post-commit hook for Subversion.

If you simply put an executable file with the name post-commit (for example EXE, BAT, CMD on Windows, SH, PL, PY on Linux) in the hooks folder under a repository, it is run everytime a successful commit occurs. There are some Perl, Python and lisp scripts for a variety of needs already for Linux systems. (Using Subversion Hooks to send out build Emails).

We tried some Perl scripts, but consequently were not satisfied with the results as we are no Perl hackers.

We decided to have our own post-commit.bat batch file which invokes a C# executable to synchronize our shadow copy folder. The executable is a simple console application that spawns a process to issue an SVN checkout if the repository is newly created or an SVN update if a file is changed. It, additionally, keeps a log about what happened. SVNPostCommitConfiguration.exe is a WinForms utility that keeps the settings, generates the appropriate batch file in the specified repository.

I would like to evolve this simple project and the article into a general hook manager if a need in this context arises. I would also like to thank Thomas Weller for his comment and supportive critique to enhance this article.

Using the Code

SVNPostCommit.exe makes a shadow copy of your repository with incoming parameters, and it logs your commits.

SVNHelper.cs

C#
public static class SvnHelper
{
    const int EXPECTED_ARGS_COUNT  = 6;
    const string LOGDIRECTORY_NAME = "logs";
    const string LOGFILE_EXTENSION = ".log";
    
    static readonly string _startupPath = 
	Path.GetDirectoryName( Assembly.GetEntryAssembly().Location );
    
    public static void ExecuteSvnCommand
	( string svnExePath, string destFolder, string repoName
         , string repoUrl, string userName, string password )
    {        
        if (String.IsNullOrEmpty(svnExePath))
            throw new
               ArgumentNullException("svnExePath");

        if (String.IsNullOrEmpty(repoName))
            throw new
               ArgumentNullException("repoName");

        //Building svn commandtype
        string svnCheckout = 
	BuildSvnArgs("checkout", userName, password, repoUrl, destFolder);

        string svnUpdate = BuildSvnArgs("update", userName, password, "", destFolder);

        string svnCommand = svnUpdate;

        if (!Directory.Exists(destFolder))
        {
            Directory.CreateDirectory(destFolder);
            svnCommand = svnCheckout;
        }

        if (Directory.GetFiles(destFolder).Count() == 0)
	        svnCommand = svnCheckout;
        
        string svnExecutable = Path.Combine(svnExePath, "svn.exe");
        //Making process to run SVN executable
        Process process = PrepareSVNProcess(svnCommand, svnExecutable);
            
        process.Start();

        string line = null;
        //Write process output to log file
        while ((line = process.StandardOutput.ReadLine()) != null)
        {            
            WriteLogLine( repoName, line );
        }
        
        process.Close();

    }//ExecuteSvnCommand

    public static Process PrepareSVNProcess(string arg, string cmd)
    {
        Process process = new Process();

        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.UseShellExecute = false;

        process.StartInfo.Arguments = arg;
        process.StartInfo.FileName = cmd;

        return process;
    }

    static string BuildSvnArgs(string commandType,  string username, string password,
                               string repoUrl, string destFolder)
    {
        if (String.IsNullOrEmpty(commandType))
            throw new
               ArgumentNullException("commandType");        

        if (repoUrl == null)
            throw new
               ArgumentNullException("repoURL");

        var command = string.Empty;

        if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
            throw new
                ArgumentNullException("username or password required");

        command = string.Format(
                  " {0} --ignore-externals --username {1} --password {2} {3} {4}",
                  commandType,username,password,repoUrl, destFolder);            
       
        return command;

    }//BuildArguments

    public static void WriteLogLine(string repoName, string logText)
    {
        string logFolder = Path.Combine(_startupPath, LOGDIRECTORY_NAME );
        if ( ! Directory.Exists(logFolder) )
            Directory.CreateDirectory( logFolder );

        string logFile = Path.Combine( logFolder, repoName + LOGFILE_EXTENSION );
        string logLine = 
		String.Format( "[{0}] {1}\r\n", DateTime.Now.ToString(), logText );

        File.AppendAllText( logFile, logLine , Encoding.UTF8);

    }//WriteLogLine

}//class
}//namespace

Main void

C#
static void Main(string[] args)
{
    //
    //Expecting six commandline parameters 
    //Usage : SVNPostCommit username password repoURL destFolder svn location repoName
    try
    {
        //Console.ReadLine();
        
        if ( args.Length == 0 ) 
           PrintUsage();
        
        string repoUrl = args[2];
        string repoName = args[5];
         string destFolder = args[3];
        
        string svnExePath = args[4];
        
        SvnHelper.ExecuteSvnCommand( svnExePath, destFolder, 
				repoName, repoUrl, args[0], args[1] ); 
     }
    catch (Exception ex)
    {
        const string LOGFILE = "Application";
        SvnHelper.WriteLogLine(LOGFILE, string.Format
				(FORMAT_ERROR, DateTime.Now.ToString()));
        SvnHelper.WriteLogLine(LOGFILE, ex.Message);
    }
}

SVNPostCommitConfiguration.exe helps you to manage the saved post-commit hooks. It uses an XML file as the database.

History

  • 30th March, 2009: Initial post

License

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


Written By
Software Developer Istanbul Bilgi University
Turkey Turkey
- Software developer from İstanbul/Turkey
- Programmed with C#.NET, ASP.NET, ASP, PHP, T-SQL
- SQL Server Administration
- Experiencing about NHibernate

Comments and Discussions

 
GeneralNot working Pin
luisthesaint4-Oct-10 14:02
luisthesaint4-Oct-10 14:02 
GeneralBranch vs Trunk Pin
Pancini Paolo28-May-09 21:41
Pancini Paolo28-May-09 21:41 
GeneralRe: Branch vs Trunk Pin
Ahmet Göktaş28-May-09 22:59
Ahmet Göktaş28-May-09 22:59 
GeneralA bit more context please Pin
Thomas Weller30-Mar-09 8:17
Thomas Weller30-Mar-09 8:17 

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.