twm's homepage logo
From someone who traveled abroad to learn appreciating his own country ...
Delphi and SubVersion
Deutsch English
Google
Search dummzeuch.de
Search WWW

Introduction

I have posted about SubVersion several times in the Borland newsgroups, the most recent post was quite extensive so I thought I should extend it into an article and publish it in a more permanent form.

What is SubVersion?

There are quite a few good articles on the internet and even an online book about SubVersion. So I will only state that SubVersion is the sourcecode versioning system I have been using for the recent years, after dumping CVS in its favour. If you use it, you probably also want to have a look at TortoiseSVN.

Structure of a repository

Usually a repository contains only one project and has the following structure:

                      
\-- trunk 
\-- branches 
\-- tags 
  \-- 1.0 Release 
  \-- 1.1 Beta 
  \-- 1.1 Release
                    

Trunk contains the current version of the source code, branches contains special versions that are developed in parallel to the trunk (I haven't used that yet) and tags contains copies of the trunk as it was at some particular time e.g. there is a tag for each release of the project.

Libraries

Internal libraries can be managed in their own repository. So the structure is the same as above, but usually you don't do formal releases for them, so the tags are used differently. Every time you do a release of a project that uses a library, you add a tag to the library's repository like this:

                    
\-- trunk 
\-- branches 
\-- tags 
  \-- Project A - 1.0 Release
  \-- Project A - 1.1 Beta
  \-- Project B - 1.0 Release
  \-- Project A - 1.1 Release
                  

This way you can later get to the exact source code that was used to build a particular release of a project.

3rd party libraries

If you are like me, you probably use a few 3rd party libraries. To keep track of them I have got a special subversion repository "3rd" which looks like this:

                  
\-- JCL 
|  \-- official -> contains the JCL sources as released by JEDI
|  |  \-- 2.0.0
|  |  \-- 2.0.3
|  \-- internal -> contains an internal release of the sources
|     \-- 2.0
|        \-- 1 -> first internal release (copy of the official release)
|        \-- 2 -> second internal release with some bugfixes
\-- Indy
  \-- official
  |  \-- 9.0
  |  \-- 10.0
  \-- internal
     \-- 9.0
     \-- 10.0
        \-- 1 -> first internal release (copy of the official release)
        \-- 2 -> second internal release with some bugfixes
                

So, every time I start using a new release of the JCL I add it to the official branch of the repository and make a copy of this branch under internal\<version>\1. If, for whatever reason, I need to make changes to the sources (e.g. I change the output directory or I fix a bug), I create a new internal release.

You get the picture. Since copies in SubVersion are cheap because it only creates a reference rather than copying the content, this won't pose a space limitation. And since every internal release will be available for all of eternity (a good backup system taken for granted), you can easily refer to the internal release of a library you were using for a project's release.

Externals

Now, after you have read the above, you will probably think I keep notes somewhere which version of which library was used for which version of a project and that it is a pain in the lower back to always make sure to check out the correct branches. No, I am much too lazy for that and fortunately SubVersion allows me to stay that way. Meet svn:externals, a special property that can be used to link repositories and branches.

Lets assume your 1.0 Release was still using Indy10, internal release 1, so it has an external reference on its main directory like this:

svn:external:

                  
libs/indy https://yourserver/svn/3rd/Indy/internal/10.0/1
                

If you check out your project from subversion it will automatically create a subdirectory libs and will check out the contents of the given subversion repository under that:

              
\-- project
  \-- src -> source code of your project
  \-- libs
     \-- indy -> your first internal release of Indy 10.0
            

When developing version 1.1 you found a minor bug in Indy and fixed it, releasing it internally as version 2. To reflect that you change your external reference to:

svn:external:

              
libs/indy https://yourserver/svn/3rd/Indy/internal/10.0/2
            

Which means, that you will automatically get the new internal release when you check out your project:

          
\-- project
   \-- src -> source code of your project
   \-- libs
      \-- indy -> your second internal release of Indy 10.0
        

If, for whatever reason you later on need to know with which version of Indy your project was released, all you have to do is look at the external reference.

To actually use this, I remove the directories added to the library path by the installer and instead add references to the subdirectories under libs to the search path of the project:

        
libs\indy;libs\jcl\source;libs\jcl\source\common;libs\jcl\source\windows
      

(Note the relative paths! The Delphi IDE will add absolute paths, so you have to correct it manually.) This way I ensure that the program will always be compiled the correct sources of the 3rd party library.

In the same way I link to the version of any internal library.

Output directories

Since I am being a little anal retentive about mixing sources and the compiler output (.dcu, .exe and other files) I usually create a subdirectory in my projects for these:

      
\-- project1
   \-- src -> sources of the project
   \-- dcu -> dcu output directory
    

The project file itself will go under src and also all .pas and .dfm files. I set the dcu output directory to "..\dcu" and the output directory for the .exe to "..".

Now, this might be a little bit too orderly for most of you, but it has got the advantage of putting all .dcu files in one place so I can easily delete them if I want to make sure there aren't any old .dcu files for which the sources have been deleted.

If you try this suggestion, you will find, that just after checking out a project and opening it in the IDE, you can't compile because the IDE tries to create files in a subdirectory that doesn't exist. Yes, of course, you can create that directory manually every time, no big deal, but I said I am lazy, didn't I? So why not let SubVersion do that for you? Just add an empty dcu subdirectory to your repository! The next time you check it out, it will be created automatically.

What to add to a repository?

Most of the files must be added of course, that's the whole point of having a source code versioning system, but there are a few exceptions:

  • .dcu files, if you have got the source code, can be omitted. Why keep the binary form if you can easily recreate them from the sources?
  • <project>.cfg files will be recreated by the Delphi IDE every time you save your project. No need to keep them in the repository
  • .local and .identcache files are recreated by the Delphi IDE whenever it needs them.

You want to save your .dfm files as text (default for recent Delphi versions with recent meaning something like 10 years), so you can easily compare the different versions to find out what you actually changed.

And then, there are the corner cases:

  • <project>.res files
  • <project>.dsk files

The <project>.res file contains two things:

  • your project's version information
  • your application icon

The version info is also stored in the .bdsproj (older Delphi versions: .dof). When you open a project in the IDE it will automatically create a .res file from the information in the .bdsproj file, so why keep it in the repository? Unfortunately it isn't that simple: The IDE takes the version info from the .res file as the master copy, if it exists. If you do not check in the .res file, somebody else changes the version information and commits his .bdsproj file, you check out that file but have got an old .res file lurking around, the next time you do a build the IDE will take the version info from your .res file and overwrite the the .bdsproj file. The changes are lost.

Also, the application icon is only stored in the .res file so if you don't add it to the repository you must add it again every time you check out the project.

Add this to the above and you will probably think that you want to add the .res files to your repository. Why wouldn't you want to do that anyway?

Well, .res files are binary files and they change with every build. SubVersion can handle binary files just fine, but it cannot show the differences in any usefull form. So you will be forced to commit changes to the .res file every time you commit any changes without being able to later on check what actually did change. I for one don't like this at all, so I don't add .res files to the repository and live with the inconvenience.

Another corner case is the <project>.dsk file. This file contains your project's IDE settings, like open files, bookmarks and breakpoints. Usually you don't need that but sometimes you may have set up an elaborate set of breakpoints with special options to log variable values and call stacks. This can represent quite a bit of work which you will lose if you don't add this file to the repository. Even though, I usually don't add it.

How to reduce clutter?

If you don't add some files to the repository they will always show up in the list of changed files, making it difficult to spot the actually new source code files. Subversion offers another special property svn:ignore which is meant for solving that problem. Just add these files to your ignore list and they will stop bothering you.

TortoiseSVN

So far I haven't talked about GUIs. SubVersion itself comes with some commandline based tools, notably svn and svnadmin. While you can work with these tools alone, it isn't much fun and some point and click interface would be nice. You aren't the only one who thinks so, that's why there are several GUI frontends for SubVersion. The one I use is called TortoiseSVN. It is a plugin for the Windows explorer which adds additional items to the context menu and also changes the icons to represent the file's svn status. In addtion you can also configure the explorer to show additional columns in the detailed list view. Also nice is the fact, that it not only works in the explorer window but also in the file open and save dialogs.

There are also some tools to integrate SubVersion into the Delphi IDE. I haven't found one yet, that I really like. Most of them don't really make much sense to me, but you might think differently, so go ahead and try them.

(c) Copyright 2007 by Thomas Mueller, all rights reserved



This document was generated using AFT v5.096

last updated: 2012-10-14 twm
Post to del.icio.us Best Viewed With Open EyesValid XHTML 1.0!