Using Git with WordPress — Part 4
July 2020 by Kristian Lumme

Using Git with WordPress — Part 4

In the last article in our series on using version control with WordPress, we look at the plugins WP Migrate DB Pro and VersionPress and how they help us manage the database in our workflow.

Be sure to check out article #1, #2, and #3 in the series as well!

WordPress and the Database

In the series so far, we've looked at progressively more sophisticated ways of keeping a WordPress site under version control with Git. We've seen how this workflow can give us benefits like easier experimentation, backups, collaboration and deployment. However, there's an aspect that's very much a piece of the puzzle which we haven't discussed much so far. That piece, of course, is the database.

In this final article in the series, we'll widen our scope beyond Git and look at the practical question of how to handle our database in a scenario like those described in earlier articles. For that purpose, we'll consider two plugins and find out what problems they can help us solve.

Shared Responsibilities

Let's begin by looking at the role the database plays in our project. In the previous article, for example, we handled plugins and themes using Composer, and kept most of our files in version control. Among other things, this lets us share the site code with others, and deploy by pulling the latest code from the repository. Still, it's not the complete picture.

For example, if we add a post (or a page) to our local version of the site, commit, push, and then pull down the code on the production site, the post won't appear there. This is because the post exists in the database, not on the file system. To some extent, this makes sense, as it's not a given that we want to have exactly the same content on the local and the production site... but this limitation affects more than just site content. In WordPress, the line between content and the structure of the site itself is quite blurred, as not only posts and pages, but also many options, styles and even layout can be saved in the database. For example, if we install a page builder plugin and create a custom layout for your start page, that layout is likely saved in the database and not on the file system.

What about backups, and conveniently moving back and forth through the history of changes made to our site? Again, Git handles this easily for the code part... but the database won't keep up. If we add a feature to our theme and utilize it on a page, then find out that the feature didn't work as intended and roll back to a previous version of the code, the page using the feature will still remain, possibly resulting in a broken page.

Let's take a look at a couple of plugins that can help us with the issues described above.

WP Migrate DB Pro

(Disclaimer: We reached out to the developers of this plugin, Delicious Brains, who provided us with a license so we could include the plugin in this article series.)

The functionality of the commercial plugin WP Migrate DB Pro is, on the surface, easy to describe. The plugin can take your database on one site and push it to another site, or pull the database from one site to another. In the process, it can perform necessary substitutions in the database to handle things like URLs and paths that may differ between the instances.

It speaks to the quality of this plugin that the practical usage is not much more complicated than that. After installing the plugin on a couple of sites, we can enable pushing and pulling for one site, get a URL which is used to push or pull to that site, enter the URL on the other site, and sync the database between the two sites.

It's worth mentioning at this point that the plugin doesn't merge the database changes, i.e. it can't combine changes from both of the sites. It only lets us overwrite one of the databases with the other.

A common problem when moving a WordPress site is that absolute URLs and paths are saved in the database, serialized in such a way that simply searching and replacing them won't work. WP Migrate DB Pro handles this as well, letting us specify the new path and URL of the site, and even auto-populating the fields.

A Real-World Example

Let's look at an example. We'll assume we have two sites set up based on the Bedrock project, along with Git repositories, as described in the previous article. The second site is based on a clone of the repository of the first, so it has the original site as a Git remote. In addition, WP Migrate DB Pro has been downloaded and installed on both sites, along with its Media Files Addon. The Media Files Addon let's us push and pull the media library between the sites in addition to the database. This is useful because, as you may remember, in the Bedrock project, the uploads folder is added to the .gitignore file so that it will not be tracked using Git. The sites will be referred to as "development" and "production", although in this example, we'll be using two local sites. They could just as well be a local and a remote site.

Note that WP Migrate DB Pro needs to be installed manually, not through Composer, and that installing the plugin by uploading it through the WordPress admin requires the DISALLOW_FILE_MODS option to be set to false. In addition, the project is set up not to include plugins in the Git repository, as these are handled through Composer. To include the WP Migrate DB Pro plugin and its Media Files Addon in our repository, we need to add exceptions for their directories in our .gitignore file.

On the development site, we'll install the page builder plugin Elementor. Remember, in this approach, we handle plugins through Composer, so the plugin is installed by the following command:

$ composer require wpackagist-plugin/elementor

We then commit the change:

$ git commit -am "Install Elementor plugin"

Next, let's do some changes inside WordPress. Through the customizer, we first set the start page to a static page. Next, we activate the Elementor page builder plugin and work on a front page for the site:

To make the Elementor design transfer successfully to the new site, we need to go into its advanced settings and set "CSS Print Method" to "Internal Embedding" instead of "External File". By default, Elementor saves its CSS as an external file in the uploads folder but outside the media library — meaning the file wasn't picked up either by Git (thanks to the .gitignore file) or the Media Files Addon (which only works on the media library).

Great! Now, we obviously want to deploy these changes to the production site. In the real world, we would probably push the changes to a hosting service like GitHub, Bitbucket or GitLab, then pull down the latest changes to the site. For this example, we're running both sites locally, with one of them set up as the remote of the other, so we can pull down the changes directly in the directory of our production site:

$ git pull

Finally, committing the plugin installation in the previous step only added the plugin to the list of dependencies — in order to actually install the plugin on our production site, we need to run the following command:

$ composer install

You can probably guess what happens next. On the production site, even though we've pulled down the latest code, the page we just created doesn't exist, the front page of the site is still the post listing, and the Elementor plugin isn't even activated. This is because all of these changes exist in the database, not on the file system. At this point, we'll turn to WP Migrate DB Pro.

To begin with, let's go to the WP Migration DB Pro settings for the production site and enable "pull" and "push". We also copy the connection info listed here:

On the development site, we go to the WP Migrate DB Pro admin page, choose the push operation and paste the connection info. Note that WP Migrate DB Pro has filled in the find-and-replace fields for us with a URL and a path. We also enable "media files" to make sure the images on the page are included when we push (this is where the Media Files Addon comes into play).

We then click "Push" and wait for the operation to finish.

That's it! Our changes are now pushed to the production site! (In reality, the push changed the name of the site as well. I went in and changed it after the push to make it clear which of the sites we're on at the moment. With some coding, you can get around this).

Plugin Strengths

We've now looked at one way to synchronize not just code changes, but also database changes, between sites. Let's consider which problems this plugin solves for us, and which it doesn't. WP Migrate DB Pro has export and import operations, which could allow us to take backups of the database along with the code backups provided by using Git, and to export the database before implementing a tricky feature, allowing us to "roll back" the database along with the code if needed. However, these are manual operations and do not provide the flexibility that Git does. Rather, the strength of this plugin lies in its push and pull functionality, allowing us to apply the changes on one site to another. This is very useful for deployment and collaboration, and so WP Migrate DB Pro proves a very useful companion to Git in such scenarios.

The plugin we'll take a look at next takes a different approach to the database. Not only does it adhere more closely to the Git way of doing things — it is, in fact, an attempt at putting the WordPress database under version control using Git, along with the files!


VersionPress is an interesting and very ambitious plugin, aiming to integrate WordPress directly with Git. In particular, database updates are now version-controlled, meaning things like changing an option or creating a new post will result in Git commits. Behind the scenes, VersionPress relies on pure Git, letting us use standard Git commands and workflows to work on our site.

According to the official site, VersionPress is currently a developer preview and should not be used in production, although some people are certainly doing this. Making all or most WordPress operations version-controlled is a complicated undertaking, and many things can go wrong. Plugin compatibility, or lack thereof, is a factor in how well VersionPress works, and it is not clear how actively it is being developed at the moment. On the other hand, the solutions it offers are enticing, and if VersionPress does prove to work in your specific scenario, you could get a lot of benefit from it. At the very least, you should probably make sure to have backups that are independent from VersionPress.

The benefit of the plugin are probably best shown with a few examples. For this article, we're using the latest release of the plugin found on GitHub at the time of writing, which is version 4.0-beta2.

In Practice

This time, let's start from a completely vanilla installation of WordPress — no subdirectory install, no submodules — everything standard. After downloading VersionPress from GitHub, installing and activating it, we get a VersionPress page in the WordPress admin, which lists the history of changes on the site. Currently, there's just the change where we activated VersionPress.

Each of the changes here correspond to a commit in Git. Demonstrating that this is indeed the case, here's what's shown when we run git log --oneline in the project directory:

e2f8f2b (HEAD -> master) [VP] Activated VersionPress 4.0-beta2

Let's create a new post in the WordPress admin the usual way. After this, the VersionPress page in the admin lists our new change. Notice also the options to undo or roll back to each change:

As before, our change shows up in the Git log as well:

af55763 (HEAD -> master) [VP] Created post 'My New Post'
e2f8f2b [VP] Activated VersionPress 4.0-beta2

In an earlier article, we talked about how reverting a commit in Git isn't enough to roll back a change if we've made changes in the database as well as on the file system. Let's take a look at how VersionPress lets us handle such a scenario. This is also a good opportunity to illustrate how we can use normal Git commands on our repository as well. First, we'll make a change to our theme (in this case, the default Twenty Twenty theme of WordPress). We'll edit the functions.php file in the theme to add a new widget area ("sidebar", in WordPress terms) called "Header".

Now, if we take a look at the VersionPress page, VersionPress notes that we have uncommitted changes in our repository, but it doesn't create a commit by itself. We will create the commit ourselves in Git. Let's use the Tower Git client for this one:

... and the commit shows up on the VersionPress admin page, along with the changes made by VersionPress:

We then make a change in the database that depends on the code change in the previous step. Let's go to the "Widgets" admin page and move all widgets from the other sidebars into the "Header" sidebar.

Again, VersionPress lists our changes:

Now we're in the scenario described before: we've made a change (moving widgets to a new sidebar) that touches both the code and the database, and just rolling back the code change does not restore the state before it was introduced. However, with VersionPress, we can roll back the change! Just click "Roll back to this" on the change before we added the new sidebar in functions.php, and things will be back as they were before!

This is one example of the magic of VersionPress. Note here that the rollback command doesn't just revert some old commits, it also needs to update the database. Therefore, we still can't jump around in history using purely the Git command-line interface, but need to use the VersionPress interface (though we could accomplish the same thing using pure Git and the WP-CLI apply-changes command, which we'll get back to later).


VersionPress also has operations to clone a site and push and pull changes between sites, but these work only locally — they don't directly allow us to synchronize work between a local site and one on a server, for example. However, using Git and the WP-CLI apply-changes command of VersionPress, such a workflow can still be accomplished! Let's take a look at how.

The VersionPress clone and merge operations, as well as the apply-changes and restore-site commands used here require WP-CLI. WP-CLI is a command-line interface to WordPress, allowing us to accomplish many operations in WordPress through the command-line or through scripts. An introduction to WP-CLI is outside the scope of this article, but installation and usage instructions can be found on the project homepage.

One awesome feature of VersionPress is that it can restore a site, including the database, from just a Git repository. To set up the next example, I've done the following: I've taken my example site using VersionPress and cloned the site to another location:

$ git clone my-versionpress-site my-versionpress-clone

To go from cloned repository to working site, I've used the WP-CLI restore-site command. This requires some extra steps, which are listed in the official documentation for VersionPress. Let's appreciate how useful this is for a moment: we get a complete, working copy of the previous site, including the database, based on nothing but a Git repository! In this case, both sites are running on the same machine, but if we were to use a service like GitHub to push and pull commits between the projects, they could just as easily be running on different machines.

Now let's look at something else that's possible because the change tracking is based on Git. When looking at the WP Migrate DB Pro plugin, we noted that pushing or pulling a database in that plugin always overwrote the changes on the receiving site — we couldn't merge changes between databases. As every change in VersionPress is represented by a commit, merging changes between sites is, indeed, possible!

Suppose we create a new post on the original site. At the same time, someone else creates a new post on the cloned site. Now, on both of these sites, VersionPress has introduced a commit for the change, and both sites have changes we want to keep — we want to retain the new posts on both sites.

The Git repository of the clone has the original site as a remote, so let's pull down the latest changes on the clone:

$ git pull

Git asks us for a merge commit message as the changes from the remote are merged into the current branch, but this runs without problems, as we don't have any conflicting changes. Pushing locally is a little tricky, so let's instead add the clone as a remote of the original site (so both sites have the other one as a remote — this is where you would probably just push to and pull from a service like GitHub in a real-world scenario). Then, to get the changes into the original site, let's pull to that site from the clone. In the directory of the original site:

$ git remote add clone ../my-versionpress-clone
$ git pull clone master

Now we have the commits for both posts in the repositories of both sites, but not in the database. We've only pushed around the commits, not actually made the changes apply in the database. That happens next. In both projects, we run the WP-CLI command apply-changes:

$ wp apply-changes

This will take what's in the commits and apply it to the database. After the above command has been run for both sites, both sites have the latest changes — the post created on the original site as well as the post created on the clone!

Should I Use It?

The apply-changes and restore-site commands, along with everything being accessible in Git, has quite some potential! Our Git repository gives us a history of development, providing backups and possibility for rolling back changes, not only to our code but to the database as well. Collaboration and deployment can benefit greatly from these features. On the flip side, VersionPress tries to solve a very complicated problem and, as mentioned, the developers don't recommend this plugin for production use. You're going to have to decide for yourself whether this plugin can have a place in your workflow or not.

Final Words

This concludes our article series on using Git with WordPress. We've covered a lot of ground, and there are many considerations, challenges and questions we didn't bring up here. Hopefully, though, you've found this series useful, and found some new ideas you can bring into your workflow.

Make sure to get notified when new content becomes available on our blog — simply by signing up for our newsletter below or by following us on Twitter.

Your Download is in Progress…

Giveaways. Cheat Sheets. eBooks. Discounts. And great content from our blog!