Keeping SugarCRM under Subversion control

by Sander Marechal

I have been mulling a long time how I am going to keep track of SugarCRM, the custom modules I build and and changes to the core code that I need to make. The latter is sometimes unavoidable because some functionality cannot be built any other way. Also, sometimes you run into a bug that you need to fix, but you cannot wait for the next Sugar release. Of course, all of this needs to be done in a way that is easy for developers to work with. I think I have finally found a way that is workable and keeps everything under version control.

I will present my workflow in two articles. This article shows you how I keep SugarCRM itself under version control, how I deal with deployment and how I do upgrades in the face of changes to the core code. In “Build custom SugarCRM modules in Subversion” I will present how my custom modules—which are also under version control—fit into this system. Hat-tip to Leonid Mamchenkov for his work and insights.

0: Table of contents

1: Notes on this setup

The setup I am describing below allows me to keep SugarCRM under control when I make changes to the core code. As I explained above, sometimes you simply have to change the core code. Either to fix a bug that upstream does not fix fast enough or because a feature cannot be implemented in an upgrade safe way. There is however a fundamental problem with keeping any web application under version control: You can version control the source code, but not the database.

The way this dilemma is usually solved is by making the database follow the source code. The source is always the master and the database always the slave. When the source code and the database disagree then the source is leading and the database adapts. Ideally this happens automatically. With SugarCRM this is a problem however. Custom fields created in Studio for example are stored in the database, not in vardef files, but those custom fields are referenced in e.g. the viewdef files. Now you have a circular dependency between the source code and the database and as any programmer can tell you, circular dependencies are evil.

I break this circle by forcing the source code to be leading. Luckily SugarCRM has a very useful function called “Quick rebuild and repair” that can detect changes in the vardefs and make the database follow it. This does however mean that all such changes need to happen in the vardefs to begin with. This boild down to: “Don’t use Studio”. If you use the below setup while you manage custom fields, layouts and relationship with Studio then you will run into conflicts*.

I manage all my custom fields, layouts and relationships with a custom package that I can install through the module loader. This package is also kept under Subversion control but sits in a separate repository. “Build custom SugarCRM modules in Subversion” explains how this works exactly. The base SugarCRM is what I keep under version control with the method shown below. The only updates made to it are my changes and fixes to the core code, and updated releases from SugarCRM upstream. Nothing else.

2: Setting up a development server

For my system I use a separate development server that has been configured in such a way that I can quickly make a subversion checkout and have a new copy Sugar running. No Apache configuration required because it’s all automated away.

Take a look at my article “Easily develop and deploy web applications from subversion” to see how I have set up Apache. What it boils down to is that I can make a Subversion checkout in a predefined directory, and Apache automatically picks up that subdirectory as a separate subdomain. So, if I do this:

  1. svn checkout /var/checkouts/my-checkout

Apache will see the “my-checkout” directory and (thanks to wildcard DNS) make it available on automatically. I did a similar trick for the MySQL database because I don’t want to manually grant database rights every time I create a new SugarCRM checkout. This is easy to do because MySQL supports wildcards in the database name when you grant right. So, manually I created a new database called sugar-vendor-510b (because I will be installing Sugar 5.1.0b today), created a new MySQL user called sugar and then I granted him rights on the database sugar-*. This way the new user will have rights to any database whose name starts with “sugar-”.

3: Set up Subversion and prepare for importing Sugar

Now it is time to import the first SugarCRM. I assume that you have an empty Subversion repository at to which you have rights. If you need help setting up Subversion under Apache, check out my older article “Apache and Subversion authentication with Microsoft Active Directory”. Start by creating a standard Subversion layout, but notice I do not create trunk/ yet.

  1. mkdir -p temp/branches temp/tags temp/vendors/vendors-510b
  2. svn import temp -m "Creating repository layout"

Now go to your Apache checkout directory, make a checkout of the vendor branch, unzip the Sugar tarball and put the files in the checked out directory.

  1. cd /var/checkouts
  2. svn checkout
  3. wget
  4. unzip
  5. mv SugarCE-Full-5.1.0b/* sugar-510b/
  6. mv SugarCE-Full-5.1.0b/.htaccess sugar-510b/
  7. rmdir SugarCE-Full-5.1.0b

Before you can install Sugar you need to make sure that it can write to the directories. When you upgrade Sugar it wants to be able to write to all the files, so give it full access.

  1. sudo chgrp -R www-data sugar-510b
  2. chmod -R g+w sugar-510b

Now you can go to to and install Sugar using the webinterface. Use the database and MySQL user that you created earlier. After installation we need to edit a few files before we can perform the actual import. SugarCRM stores the name of the database and the full URL of your sugar installation in config.php. We need to abstract these so that we can easily change these without having to edit config.php itself. I create two files name .hthost and .htdatabase that contain the hostname and database name respectively. Then I change the config.php script so that these variables are read from the file.

  1. cd sugar-510b
  2. echo "" > .hthost
  3. echo "sugar-vendor-510b" > .htdatabase

In config.php

  1. <?php
  2. $sugar_config = array (
  3.   ...
  4.   'dbconfig' =>
  5.   array (
  6.     'db_host_name' => 'localhost',
  7.     'db_host_instance' => 'SQLEXPRESS',
  8.     'db_user_name' => 'sugar5',
  9.     'db_password' => '<password>',
  10.     'db_name' => trim(file_get_contents('.htdatabase')),
  11.     'db_type' => 'mysql',
  12.   ),
  13.   ...
  14.   'host_name' => trim(file_get_contents('.hthost')),
  15.   ...
  16. );
  17. ?>

You can even automate it further, but then you will need to change the config file manually when you deploy to a different server. Notice that the database name is now “sugar-510b”.

  1.   'db_name' => basename(dirname(__FILE__)),
  2.   ...
  3.   'host_name' => basename(dirname(__FILE__)) . '',

Next we need to add a few directories to the cache/ directory. The install script does not automatically create these but we do want to keep track of them (so we can ignore their contents later on). Please read my previous article The SugarCRM cache directory demystified to find out why you can ignore the contents of these directories.

  1. mkdir cache/blowfish cache/diagnostic cache/dashlets

The last thing to do is to rename the sugar-vendor-510b database. I do this so that I don’t accidentally write to the database anymore. I renamed it to vendor-sugar-510b so that it does not start with sugar- anymore and the sugar MySQL user does not have any rights to it.

4: Import Sugar into Subversion

Now you can import Sugar into Subversion. Start off by setting some ignore properties on the root directory. Note that to make it multiline you press Ctrl+Return, not just Return!

  1. svn propset svn:ignore "install.log
  2.   sugarcrm.log
  3.   .hthost
  4.   .htdatabase" .

Next, add the cache directory, its subdirectories and set ignores on those as well.

  1. svn add --depth=empty cache
  2. cd cache/
  3. svn add --depth=empty blowfish csv dashlets diagnostic feeds generated_forms images import jsLanguage layout modules pdf smarty upload xml
  4. svn add csv/index.html feeds/index.html images/index.html import/index.html layout/index.html pdf/index.html upload/index.html xml/index.html
  5. svn propset svn:ignore '*' blowfish csv dashlets diagnostic feeds generated_forms images import jsLanguage layout modules pdf smarty upload xml

Now we can add everything that is not ignored and commit it

  1. cd ..
  2. svn add --force
  3. svn commit -m "Importing sugar-510b vendor drop"

The last thing to do is to copy this vendor branch to the trunk and copy the vendor-sugar-510b database to sugar-trunk.

  1. svn copy \
  2. \
  3.   -m "Copying sugar-510b vendor branch to trunk"

Check out a working copy

Making a checkout is pretty easy in this setup. First I make a copy of the vendor database. I copied vendor-sugar-510b to sugar-test. Then I make a checkout using the following commands.

  1. cd /var/checkouts
  2. svn checkout test
  3. sudo chgrp -R www-data test
  4. chmod -R g+w test
  5. echo "sugar-test" > test/.htdatabase
  6. echo "" > test/.htdatabase

Now I can go to and log into my working copy of SugarCRM.

5: Import a new release

Importing a new release into Subversion is quite easy. First I copy the vendor branch to a new branch that will get the update.

  1. svn copy \
  2. \
  3.   -m "Copying vendor branch in preparation for update to 510c"

I copy the vendor-sugar-510b database to sugar-vendor-510c, make a checkout of the new vendors/sugar-510c in my checkouts directory and then I install the update package from SugarCRM on that installation. After the update completes I commit vendors/sugar-510c, but do check the config.php file before you do so. SugarCRM has a tendency to regenerate its config file and when it does, you need to re-apply the changes that load the database and hostname from .htdatabase and .hthost.

Finally rename the database to vendor-sugar-510c to protect it against accidental writes. I now have a clean copy of both the 510b source code and database as well as the 510b and 510c database.

6: Update the trunk and working copies to a new release

Updating the trunk or any other working copy is slightly more work, because it is a two-step process. You don't know what kind of changes the update will make to your database so you need to actually install the update. You cannot simply update the source code and run “Quick rebuild and repair”. Sometimes SugarCRM makes very invasive changes to your database that are not reflected in the vardefs. For example, the update from Sugar 5.0.0a to 5.0.0b encrypted the SMTP password field in the database. But installing an update overwrites the source code files so you will loose any changes that you made to core files. The solution is to update the source code and database separately.

6.1: Update the database

If you want to update a working copy then you will need to commit your changes. For safety you may want o backup your database. Now make another checkout (or export) of that branch or trunk to a temporary directory. For example, make a checkout of the trunk to /var/checkouts/temp. Configure this checkout to user the sugar-trunk database (or whatever database you want to upgrade). Now go to this temporary SugarCRM and install the update in the regular way. When the update tells you that it has found local changes, tell it to overwrite all files with the new version. When the update is complete then your database has been upgraded. You can remove the temporary checkout directory. Note: Do not commit this temporary checkout because all your custom changes have been undone!

6.2: Update the source code

Updating the source code is done using a regular Subversion merge so that any modifications we have made will remain intact. Remember that we copied vendors/sugar-510b to vendors/sugar-510c? We can use this to generate the exact changeset between the two versions. Below you can see how I would update the source code of my checkout of the trunk to the new Sugar version.

  1. svn merge \
  2. \
  3.   /var/checkouts/trunk

Resolve any conflicts between the SugarCRM update and your own changes and commit.

  1. svn commit -m "Updating to SugarCRM 5.1.0c"

Other people can now simply run “svn update” on their working copies of the trunk. Now you have updated both the database and the source code so you can use Sugar again. The only tricky part about this bit is that the database and the source code should be updated at the same time. If you are working with multiple developers who use their own working copies of the trunk but share a common sugar-trunk database then one developer needs to make this update. The other developers cannot use Sugar in the period in between the database update and the source code update.

7: Deploying and updating live installations

Deploying a live installation is simple. It is exactly the same as checking out a working copy. The only difference is that this time you probably need to configure Apache because most likely you will not be running the live copy out of your /var/checkouts directory.

Updating a live installation is also pretty much the same. First update the database and then update the source code. If there have been no new SugarCRM updates then there is no need to update the database. If there were then you need to check out an appropriate working copy from your repository and use that to update the database by installing the SugarCRM update. It works the same way as updating a working copy because a live deployment is in essence just another working copy.

After the database has been updated you can run “svn update“ followed by a “Quick rebuild and repair” to update the codebase and propagate the last changes into the database layout.

8: Footnotes

…conflicts. Such circular dependencies are one of the many ways in which SugarCRM is hostile towards maintenance under Subversion. Another one is its tendency to simply delete and recreate some cache directories during upgrades, after which you lose your .svn directories and Subversion will start complaining. I have no idea what revision control system is used by the SugarCRM developers—if they use any at all—but I would love to know how they handle these kinds of issues. Unfortunately the development process of SugarCRM is just as closed as that of Microsoft or Apple (something I commented on earlier) so we may never know.

Creative Commons Attribution-ShareAlike


#1 (

You could get rid of the risk of Sugar rebuilding config.php by using config_override.php to fill the values with the . files.

#2 Sander Marechal (

That's what I though at well, but I found that some actions in the Sugar admin panel cause Sugar to rewrite config_override.php as well. I found that Sugar Pro 5.1b overwrote the config_override file when I switched developer mode off/on in the admin section. I don't know if that still happens with Sugar 5.2 though.

Oh well, yet another nonsensical inconsistency on the list... :-/

#3 (

I just tested in 5.2.0, and you're right unfortunately... :(

In 4.2.1, I know for sure that config_override.php wouldn't be completely overwritten...seems somewhere along the way someone got nervous..

#4 Loek van Gool (

When upgrading SugarCRM, there are 3 things to consider:

1. Changes to the code (beautifully managed with your svn merge)
2. Changes to the database (need to be ran using the Sugar upgrader, as you cannot extract the queries themselves (actually you can, but Sugar includes all queries, even skipped ones, so that you cannot use this as a database upgrade script))
3. Post/pre install scripts (like moving all emailaddresses to their own tables while upgrading to Sugar 5)

For running installs, 2 and 3 can cause problems which are unsolvable using svn merge to upgrade Sugar. Sugar lacks a way of knowing what actions need to be taken in order to complete an upgrade in case the code is upgraded while the database hasn't. It either happens while upgrading using the SugarCRM Upgrade Wizard, or it never happens. I don't think upgrading can be done this way safely. This is unfortunate, it is so nice in theory.

#5 Sander Marechal (

Loek, that is why I made upgrading a two-step process in section 6 in this article. Upgrade the database by running the Upgrade Wizard on a throwaway (working-)copy of the source code. You need that throwaway copy because the upgrade process will clobber your source code changes.

Then upgrade the real source code from Subversion using an `svn update`. That code still has your local changes thanks to Subversion's merging.

That mostly solves your #2 and #3. The only downside is that the source and the database must be updated at the same time. As you said, it's not possible to update one but not the other. I recommend that you create a new Subversion branch every time you upgrade your database. Always use a database with the correct source code branch. This way you also won't accidentally `svn update` your source code without doing a database change, because you would need to explicitly `svn switch` to the new branch.

It's not perfect, but it works.

#6 Damien

First of all, thanks for this usefull tutorial.
The only issue I have is with:
$ svn add --force
I had to use *:
$ svn add * --force

#7 dasho

Hi Sander,

I have studied your tutorials about how to manage a SugarCRM project with Subversion and they are really helpful. Me too have
tried to manage SugarCRM projects with Subversion and I have
encountered most of the problems that you describe.

Right now I am trying to reorganize my project and after
scratching my head for a little while (and after reading your tutorials) I came up with a setup that I am going to describe below. Since you have a lot of experience with SugarCRM, I would like to discuss it with you, just in case that I am missing
something important, which may create me problems later.
Right now I don't have a blog space yet, but I will try to get
one and I am going to describe there my setup in more details.

Basically, the structure of the svn repository is going to be
something like this:
  +-- vendor
  |     |
  |     +-- v520d
  |     |
  |     +-- v520e
  |     |
  |     +-- v520f
  +-- patched
  |     |
  |     +-- v520d
  |     |
  |     +-- v520e
  |     |
  |     +-- v520f
  +-- myapp
        +-- trunk
        +-- branches
        +-- tags

The vendor directory contains all the sugar releases, unmodified.

The patched directory keeps all the changes that are not upgrade compatible (with all the flexibility of sugarcrm, not everything can be done in an upgrade compatible manner, as you have pointed out as well). I think to include here the
plugins as well, which may or may not be upgrade compatible (like ZuckerReports).

The directory myapp/trunk contains the application that I am building, with custom modules, customizations and everything.

The workflow is like this:

  • Initially I import sugar on sugar/vendor/v520d, then I make a copy of it to sugar/patched/v520d and to sugar/myapp/trunk. Each of them has its own separate database, which is a copy of the vendor database.

  • Modifications that are not upgrade compatible are done on the patched version and are also merged on trunk. The same for plugins.

  • New modules and upgrade compatible customizations are made on trunk.

When the time comes to upgrade to a new version, it is done like this:

  1. Copy vendor/v520d to vendor/v520e. Copy the database as well.

  2. Apply the upgrade patch on vendor/v520e

  3. Copy patched/v520d to patched/v520e. Copy the database as well.

  4. Apply the upgrade patch on patched/v520e

  5. Most probably, some of the modifications are erased by the upgrade patch, so we should apply the difference between patched/v520d and vendor/v520d on patched/v520e. Maybe some things will need to be resolved.

  6. Apply the upgrade patch on myapp/trunk.

  7. Find the differences between patched/v520e and vendor/v520e and apply them on myapp/trunk. Some of the differences may be already there, so they will be skipped, some other things may need to be resolved, etc.

Of course, while developing on trunk I should be careful to make changes on the code, so that they can be applied to the database. You say that the best way for this is to not use Studio at all. However I don't like this and I will try out to find any other workaround. The best way, without doubt, would have been if Sugar developers fix Studio so that it makes changes only on the code and then applies them to the database.

Thank you for any comment or advice.

#8 Sander Marechal (

Hi Dasho. Your way seems pretty clean to me. The only trouble I see is with step 7 which could get messy IMHO. Perhaps it would be better to take the difference from patched/v520e after step 4 and after step 5 and apply that to myapp/trunk. As I see it, in step 5 you re-apply the patches that SugarCRM's upgrade system overwrote. That's exactly the changeset that you need to apply to myapp/trunk as well.

As for not using Studio, yes it would be great if SugarCRM fixes studio to only make code changes, but don't count on it anywhere in the near future. Doing it manually is slightly more work (especially in the beginning when you're still figuring things out) but it does pay off in the end. As an added benefit you also get much cleaner code because you can pick sensible variable names. Studio and the Module Builder generate very nonsensical names, especially for relationships.

#9 dasho

Thanks for your feedback. I think that you are right. Since we sort out the mess on step 5, then step 7 should be clean. So, step 7 should be like this:

7. Find the changeset of step 5 on patched/v520e and apply it on the trunk.

I am going to try out this setup and if it works well I am going to describe it on some blog page.

#10 dasho

I am going to try out this setup and if it works well I am going to describe it on some blog page.

I have already created these wiki pages where I describe my experience about using SugarCE:

#11 S. Horst

What about live and running environments with lots of stuff allready in the cache-directory. How do I get these under version control. ?

#12 Sander Marechal (

@S. Horts: Pretty much the same way. Just make sure that you add the various directories non-recursively (i.e. just the directory itself, not the files in it). Then set the proper svn:ignore tags on those directories so you exclude all the files you do not want in your SVN. Only then do a recursive import.

#13 Egor

Thanks, Sander, for the very good tutorial!
But what do you think of approach regarding development environment given here ? Not taking into consideration main code bug removal Is it not enough to have under CVS only custom/ directory ignoring Sugar-generated *.ext.php files and some nested directories like custom/history/, custom/metadata/ , custom/modulebuilder/, custom/working/, custom/Extension/application/Ext/ (hope I didn't miss anything important) . All the code “magic” (both manual and through Studio) happens inside custom/ directory (Am I right?). And I think in this case our custom code is pretty good master for the database if we don't forget to Quick Repair after deploying code on production. In case of building custom module in Module Builder we could build it on devel server, pack it and then deploy on production. Isn't it good enough? Can someone turn into some problems with such a workflow? I'm really interesting in your opinion.
Thanks again!

#14 Sander Marechal (

Hi Egor,

The deveopment aproach that was posted could work, but I am a little apprehensive about step 1 and 2. How is the code moved from localhost to the development server? Not via CVS, because that's step 3. It can work for a case where there is just one developer, but I don't see it working when there are multiple developers.

As for only keeping custom/ in CVS, that can work for module development. But you loose the ability to upgrade from one version of SugarCRM to the next using CSV. In my case, you can install a Sugar update on development, test it, commit it and then simply use a CSV update on the production server. The goal of my setup is that *every* change happens on the development server, inclusing installing modules and updates.

#15 Anonymous Coward

You do know that the /cache/ directory (contrary to it's name) holds live content such as uploaded imaged? You can look up that fact in the sugar forums and documentation....

#16 Sander Marechal (

Yes, I know. I wrote about it previously. See The SugarCRM cache directory demystified. But the fact that it holds content is no excuse to put it under Subversion control. Subversion is for application code (and perhaps configuration). Not for user generated data. That's what backups are for.

#17 Anonymous Coward

Hi Sander,

Great posts here about sugar development...

I wonder a few things...

How would you suggest to arrange customizations to the standard modules (like new fields, renaming of fields, relationships, etc...)? Would you keep them on the trunk repository, or put them on a separate repository, as you descriped for the custom modules? => my trend goes to put them under standard trunk repository...

How shall I work with language packages? => I think put them to the standard trunk repo would make sense?

The same question I wonder is for 3rd party application modules (plugins)... => also this gonna be difficult to put in a seperate repository I think, because of you never know, if they touch standard modules or not...

Thanks a lot for your response,


#18 Sander Marechal (

With the setup I descriped all your customisations can simply go on the trunk, just like in any other Subversion repository. That's what this (somewhat complicated) setup is for: being able to change core code without tripping over yourself (too much).

The same goes for 3rd party modules. You can treat invasive modules just like a SugarCRM update. Install the module and commit the changes. Just remember that you need to do this for all your databases, just like core code upgrades.

#19 jak

Useful tutorial - I was keenly looking for a mechanism for sugar development with SVN. I am new to sugar.

I hope things hasn't changed much (studio vs builder) in 6.4.x now.

I am planning to use Intellij so I hope I will be able to manage these merge changes easier (esp 6.1/6.2 when I get to it)

Few clarifications.

# when you rename db to vendor-sugar-510b, will you be able to launch the sugar-510b - doesn't it hang without database?.
# Likewise, when trunk is synced by other developers how they will get the database created?

My understanding (so far) is that once installed you can neither run install.php nor launch sugar to rebuild/repair.
Is there a way to trigger Quick rebuild and repair (DB setup) without launching sugar UI?


#20 Sander Marechal (

Hi jak. I pretty much stopped using SugarCRM after 5.1/5.2 so I can only answer this for these old versions. I have no idea how the 6.x versions are.

when you rename db to vendor-sugar-510b, will you be able to launch the sugar-510b - doesn't it hang without database?

Yes, it will stop working. It's supposed to stop working. After this, you should throw away your working copy (source code) and never, ever touch that database again. That database is like a subversion tag. Don't ever change it.

When you need a Sugar 5.1b instance you create a new branch, make a checkout, make a copy of the database and configure your checkout to use that copy.

Likewise, when trunk is synced by other developers how they will get the database created?

They will have to configure it manually after they checked out the code. That is why the database name is not in the configuration but in .htdatabase (which you should not check into Subversion. Every developer has to create it manually after checkout).

#21 jak


Appreciate you taking time in spite of you not working on this at present.

Since we maintain the base version of different release- I thought it is just few MBs away to maintain the DBdump of each of them.

Also I would like to keep versions of the db dump everytime I install a custom package to keep track (version) at every level.

The way you have mentioned below it looks trivial but I couldn' figure out how to do it beyond changing .htdatabase to some local db name and creating the database in mysql. How do we ask the sugar to deal with this local database and import/generate the db schema.

They will have to configure it manually after they checked out the code.

I can post my experience on 644 post this for others looking for something similar or share their experience.

#22 Sander Marechal (

I think you had better look into a build system of some kind. I like Phing because it's written in PHP but something like Apache Ant or CMake will work just as well.

Use the build system to create databases, set configuration parameters and generate files like .htdatabase. You shouldn't try to setup your application from inside the application. Do it from the outside with a build system. That doesn't apply to just SugarCRM but to any kind of website.

Build systems are great at automating tasks like this. I highly recommend them. Back in 2008 when I wrote this atricle I didn't know about them yet. If I had, this article would have been a lot different!

#23 jak

Need one more help.

Since modulebuilder doesn't allow core modules(such as Accounts), what is the right way to extend them without using studio. Tried using studio, modelled extensions, export customization, installed two packages separately but it is overwriting each other. Also it is nightmarish merging them into a single package.

Appreciate some pointers.

#24 Sander Marechal (

Sorry, I can't help you there. I only ever used a single package with all my customisations in it. I never tried having multiple packages extend the same core module.

Comments have been retired for this article.