Methodology
I initially asked this question with the goal of setting up two repositories: a production one on the site and a local one for development, a workflow for which there's very little guidance or documentation on. Since then I've also come around to the more frequent usecase of including Github in the workflow, for the advantage that it gives of being of an offsite backup for the code.
Git has no problems with being used either way, because it was designed with both in mind, so this is a matter of personal preference, and whether you consider doing git push
twice (to both the production server and Github) a reasonable tradeoff for the extra layer of data security. I've detailed both options below, where each one focuses on propagating an up-to-date version of your live site to all other repositories.
Prerequisites
This answer assumes that a local environment and database (whether up to date with the live site or not) have previously been set up due to having been used to develop your Wordpress website locally before taking it live, as was my specific situation when asking this question. If this isn't the case, you'll first need to set up a local environment and database using something like XAMPP before continuing with the rest of this guide.
Additional prerequisites are:
Two repositories (production → development)
From the production server
In your Wordpress root directory - where the wp-config.php
file is located - initialise a remote Git repository, and create the .gitignore file:
git init
nano .gitignore
Edit .gitignore to include only the files you want to version control. I use a slightly improved version of Bill Erickson's brilliant sample .gitignore for Wordpress (see the description of my Gist for how my .gitignore differs from Bill's).
Once you've saved your .gitignore file, run:
git add . # Prepare all non-ignored files to be committed
git commit -m "Initial commit"
git config receive.denyCurrentBranch updateInstead
From the development machine
Initialise a local Git repository in your local server's Wordpress root (for example, XAMPP/.../wordpress
):
git init
Configure Git, add the production server as a remote, and finally pull from it:
git config --global user.email "youremail@example.com"
git config --global user.name "Your Name"
git remote add live ssh://user@hostname:path/to/server/repo/
git branch -u live/master master
git pull live
From now on, after making local changes to some files, commit and push them to the production server:
git add . :/ # Prepare all modified and added files to be committed
git commit -m "Message describing changes made in the commit"
git push live
Three repositories (production → central → development)
From the production server
git init
nano .gitignore
Edit .gitignore to include only the files you want to version control. I use a slightly improved version of Bill Erickson's brilliant sample .gitignore for Wordpress (see the description of my Gist for how my .gitignore differs from Bill's).
Once you've saved your .gitignore file, run:
git add . # Prepare all non-ignored files to be committed
git commit -m "Initial commit"
From Github
Create a new empty repository without a .gitignore file.
To associate your server's public SSH key with your account: go to https://github.com/settings/keys and click New SSH Key. Do cat ~/.ssh/id_rsa.pub
to output the contents of your server's public key file. Copy this output into the Key field, and save the key.
You'll need to repeat this process for your development machine if it uses different SSH keys to that of your server - or alternatively, you can have your development machine use the same keypair as your server.
From the production server
Add the Github repository as a remote and push the production site to it:
git remote add central git@github.com:Kaos-Industries/industryroadmosque.git
git config receive.denyCurrentBranch updateInstead
git push -u central master
From the development machine
Initialise a Git repository in your local server's Wordpress root (for example, XAMPP/.../wordpress
) and add the remotes:
git init
git remote add live ssh://user@hostname:path/to/server/repo/
git remote add central git@github.com:Kaos-Industries/industryroadmosque.git
git fetch central master
git merge central master
git reset --hard central/master
git config --global user.email "youremail@example.com"
git config --global user.name "Your Name"
From now on, after making local changes to some files, commit and push them to both the central Github repository and the production server:
git add . :/ # Prepare all modified and added files to be committed
git commit -m "Message describing changes made in the commit"
git push central # Push to GitHub, as a backup
git push live # Push to the production server, to make changes live
Keeping the database updated
Go to the live website's PHPMyAdmin panel, ensure the Wordpress database is selected, and go to the Export tab.
Go to the local server's PHPMyAdmin panel and ensure the Wordpress database is selected. Drop all the database's tables by selecting Check All and then under the dropdown menu, Drop. Go to the Import tab, and import the database file exported from the live site.
Finally, to search and replace the URLs in the database, go to the SQL tab and run the following, making sure to replace the URLs with those of your live site and local site respectively:
UPDATE wp_options SET option_value = replace(option_value, 'https://www.example.com', 'http://localhost/wordpress') WHERE option_name = 'home' OR option_name = 'siteurl';
UPDATE wp_posts SET post_content = replace(post_content, 'https://www.example.com', 'http://localhost/wordpress');
UPDATE wp_postmeta SET meta_value = replace(meta_value,'https://www.example.com','http://localhost/wordpress');
Note that if your live site had HTTPS enabled then explicitly including the http://
protocol before localhost
is required - without it, all pages of your local site will return 404s.
Dealing with the Uploads folder
The only thing left to consider at this point is Wordpress' wpcontent/uploads
folder, which I didn't want to version with Git because of how large it is and how much larger it's likely to get. The good news is that uploads don't need to be pulled from the production server at all. Instead, the smarter way to handle this is to use .htaccess
rewrite rules to make missing images on the development site link to their counterparts on production. This cleverly sidesteps the problem of needing to keep the Uploads folder in sync at all.
Add the two lines below to your development site's .htaccess
file, directly after the RewriteRule ^index\.php$ - [L]
line:
# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
# If images not found on development site, load from production
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^wp-content/uploads/[^/]+/.+\.(jpe?g|png|gif)$ https://www.example.com/$0 [R=302,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Note that for the above rewrite rule to work, it's crucial that both your development and production sites aren't sharing a single .htaccess
file (i.e. that the .htaccess
file isn't being tracked in Git). Otherwise you'll need to modify the above rewrite rule to conditionally check whether an image is being loaded from production or development and code separate rewrite rules for each.
Finally, add the following to your active theme's functions.php
file to prevent Wordpress from quietly replacing anything in your .htaccess file:
// Stop WordPress from modifying .htaccess permalink rules
add_filter('flush_rewrite_rules_hard','__return_false');