Setting up WordPress projects with Subversion

Tutorial by Traction Alumni, Gabriel Glider

I've done a lot of projects using WordPress, and early on I realized the benefits of keeping my project files in a version control system like Subversion—for example, having a complete history of the changes I had made during my project. Then I started working on a WordPress project in a larger team, and found even more benefits of keeping the project in a Subversion repository. Having a single authoritative source for the project code, which all the team members check out from and commit changes to, makes it much easier to keep things in sync between individual developer environments and your staging and live servers.

It's a lot easier to manage a project with Subversion if you separate the parts of your project that need to be kept in the repository, and the parts that don't. For instance, generally you wouldn't want a file with your database connection info to be kept in the repository, because you should be using a different database on your local copy and your live server so that they can't cause problems for each other. However, separating versioned and non-versioned files can be tricky with WordPress. By default, WordPress installs all of its files at the root of your server, and your theme code and plugins live deep inside that file structure. That leaves you with a couple of equally bad choices. You could keep all the WordPress app files checked into your repository, but that makes updating to new versions of WordPress time-consuming and clunky. Or, you could have all the WordPress files ignored from the repository, but that makes it more difficult to set up a new dev or server environment.

Fortunately, there is a better way! Below, I'll explain the solution that worked well for our team on a recent WordPress project here at Traction. If you have any issues setting up the files I describe, you can check out my sample files at


I'll assume that to start, you have an empty Subversion repository set up. First step is to check that out:

svn co

Replace the URL with your actual repository URL, of course. Now, I like to make a separate “htdocs” directory for the web server to use as the site root, so that if I have notes, maintenance scripts, etc., I can keep them in trunk and not worry about those getting served to the site’s visitors.

mkdir htdocs
svn add htdocs

Add WordPress

Now, we’ll add WordPress as a Subversion external. That means that our repository just stores a reference to WordPress’ own Subversion repository, and when you checkout your working copy, you get a copy of WordPress from their server.

svn propset svn:externals "wp" htdocs
svn commit -m "Adding htdocs with WordPress external"
svn update

When you run 'svn update' you should see Subversion checking out a bunch of files for WordPress into the “htdocs/wp” directory. As you might have guessed from the URL, you are pointing to a specific version (tag) of WordPress, which makes upgrading WordPress super simple. Once WordPress 3.2 is available, for instance, you would just run:

svn propset svn:externals "wp" htdocs
svn commit -m "Upgrading to WordPress 3.2"
svn update

Configure WordPress

Now, there are a few more files to set up to get WordPress to work with this file structure.

svn export htdocs/wp/wp-content htdocs/wp-content
svn export htdocs/wp/wp-config-sample.php htdocs/wp-config.php
touch htdocs/.htaccess

Those commands create a copy of the default WordPress wp-content directory and configuration file in your htdocs directory. Also, we create a .htaccess file. This is what I use for the .htaccess file:

# BEGIN WordPress
<IfModule mod_rewrite.c="">
    RewriteEngine On
    # Make the admin still accessible from /wp-admin
    RewriteCond %{REQUEST_URI} ^/wp-admin/?(.*)
    RewriteRule .*  wp/wp-admin/$1 [L,R=301]
    # Base is the URL path of the home directory
    RewriteBase /
    RewriteRule ^$ wp/index.php [L]
    # Skip real files and directories
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # Otherwise send it to WordPress
    RewriteRule .* wp/index.php [L]
# END WordPress

It’s pretty much the same as WordPress’ default mod_rewrite setup, except that it points to “wp/index.php” instead of the standard “index.php”, since we have WordPress checked out in the “wp” subdirectory. Also, I redirect “wp-admin” to “wp/wp-admin” as a convenience.

Keep the Database Info Separate

Now, turning our attention to the configuration file, there are a few steps I would recommend. First, I make some changes to the section of MySQL settings—it’s the first section, with 6 define() statements that define constants whose names start with “DB_”. I remove those to a separate file that is not checked into the repository, so that different installations can share the same main setup in wp-config.php, but still use a unique database. So, cut that whole database section and paste it into a new file in htdocs called “db-config.sample.php”. Save another copy in htdocs under the name “db-config.php”, and put your actual database connection information in there.

Then, in wp-config.php, where the database stuff used to be, insert the following:

require_once(dirname(__FILE__) . '/db-config.php');

/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
    define('ABSPATH', dirname(__FILE__) . '/wp/');

define('WP_CONTENT_DIR', realpath(ABSPATH.'../wp-content/'));

define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST']);
define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp');
define('WP_CONTENT_URL', WP_HOME . '/wp-content');

This loads in our database config file and tells WordPress where it’s been installed relative to our site root. The last three lines make it possible to use the same installation on multiple domains, which is a common case when you have local development environments—you might set up your local install to be accessed at a fake domain like “project.local”, or you might access it directly at your IP address, so it’s good to tell WordPress to adjust its settings to match the domain name of whatever server it happens to be running on.

Finally, do be sure to follow the instructions in wp-config.php for generating authentication unique keys and salts. It’s easy and it helps secure your site.

Check It In

Now we’ve got all the files set up, and we just need to configure how they’re managed in the repository:

svn add htdocs/.htaccess
svn add htdocs/db-config.sample.php
svn add htdocs/wp-config.php
svn add htdocs/wp-content/
svn propset svn:ignore "db-config.php" htdocs
svn commit -m "Adding WordPress content and configuration files"

That sets it up so that the main configuration and sample database configuration are stored in the repository, but the local database configuration is ignored so you can have a custom one for each installation.

Good To Go

You’re all set! Now, when a collaborator checks out her own working copy from the repository, all she needs to do is copy db-config.sample.php to db-config.php and enter her own local database settings. And whenever you commit a change to the version of WordPress defined in the externals setting, all the developers on your team will be updated to the new version the next time they run 'svn update'.

I hope these instructions prove useful—the techniques here have evolved over several projects and are still sort of a work in progress, so if you have any ideas, questions, or suggestions please leave a comment and let me know.

Traction Digital Agency

Traction is an award-winning agency based in San Francisco that believes everything is interactive. And we kick ass.