My server does not have any FTP services, since I don’t think they are necessary (I use SSH for administration). However, this also means WordPress cannot update itself. Managing WordPress updates is easy enough that I don’t mind doing them on my own.
However, managing updates can soon become annoying once you start to have your own changes to WordPress or its plugins. For instance, I have some changes to the WP-Statistics Plugin that make the output a little more understandable. It becomes quite annoying to manage updates and make sure I don’t lose any of my changes. If I had changes to WordPress core as well, I would likely go insane.
To manage updates along side my own custom changes, I decided to use Git.
Git is a distributed revision control system. Briefly, revision control systems are software that allow multiple users to collaborate on software development. They help the users combine their work, find problems in code, and deal with conflicting changes.
Git can also be used to manage a WordPress installation. While you may be the only developer on your site, you still have to merge in new WordPress releases and updates to your plugins and themes. Using Git to manage changes between you and the WordPress developers makes upgrading much easier than trying to track them manually.
Since Git is distributed, you can easily add a third party to the mix: your local computer. If you have SSH access to your webserver, you can create a development version of your website. You can run Apache on your local computer and do development work there. Using git branches, you can test many different ideas without worrying that other users will be When your changes are ready, you can push them to your server.
This guide assumes the following basic setup:
- Ubuntu 14.04 LTS server or other recent Ubuntu server version
- LAMP stack
- WordPress
- your server is managed using SSH, and you have the credentials to access it, do scp, and such
- local system running Ubuntu
If you are running a different configuration, please note that some commands might be slightly different.
When commands are given, [square brackets] indicate fields that should be replaced with the applicable value for your setup.
Finally, understand that Git in this setup will only track changes to WordPress files, not the database. So you can’t use this guide to manage revisions of your pages or posts, and your development version will not have the same content as your actual site.
Getting the Server Ready
edit firewall to only allow connections from your ip
On my server, it goes something like this:
sudo vim /etc/iptables.firewall.rules
add "-s [YOUR_IP]" to the allow statements for ports 80 and 443
sudo iptables-restore < /etc/iptables.firewall.rules
If your server is accessible through IPv6, then also make sure to change those firewall rules.
shutdown apache
sudo service apache2 stop
make a site backup
This is always good practice before you go making big changes to how you do things.
See my WordPress backup script.
Server-Side Configuration
install git
sudo apt-get install git
By default, the .git directory will be in the root of your project. However, with a webserver, the root will also be the document root of the website, and thus will be publicly visible. Since the .git directory contains all the revisions of your website files, you do not want anyone being able to extract sensitive information from it (such as the plaintext of PHP files or your wp-config.php file. This is a very real attack that has actually happened to website owners.
To alleviate this problem, we will place our .git directory outside the website’s document root. Alternate solutions would be to set the file permissions of the .git directory so the webserver process cannot access it (you should be able to combine this with moving the directory, which is what my server did by default), or to set Apache configuration statements that deny access to files and folders beginning with “.” However, it seems best just to place this directory completely out of reach of the webserver and not have to fear that something might slip through.
configure git environment variables
Git has multiple environment variables that can be set to change its behavior. We will use two of them in order to allow Git to operate with its .git directory in a non-standard location.
export GIT_DIR=[/PATH/TO/WHERE/YOU/WANT/THE/.GIT/DIRECTORY/git]
export GIT_WORK_TREE=[/PATH/TO/YOUR/WEBSITE/DOCUMENT/ROOT]
You can call the git directory whatever you want, but on mine I called it git, and placed it parallel to the document root. You should be able to place it basically anywhere.
Also, you want these environment variables to be set every time you start a terminal. There is the .bashrc file in your home directory for that purpose:
vim /home/[USERNAME]/.bashrc
add the following:
# Git Configuration for Managing Website export GIT_DIR=[/PATH/TO/WHERE/YOU/WANT/THE/.GIT/DIRECTORY/git] export GIT_WORK_TREE=[/PATH/TO/YOUR/WEBSITE/DOCUMENT/ROOT]
That the same thing as above.
While you are here, you might want to also set your preferred editor so that all the git commands (and other commands) will use it. I currently use Vim, so I added the following to my .bashrc:
export EDITOR=vim
initialize the git repository
cd [/PATH/TO/YOUR/WEBSITE/DOCUMENT/ROOT]
git init
Your website is now a Git repository. Check the following to make sure everything worked:
Check to make sure there is no .git directory in your website’s document root:
ls -a
Check to see that Git has put some stuff in the repo.
ls -a [/PATH/TO/WHERE/YOU/MADE/THE/.GIT/DIRECTORY/git]
some files git should ignore
Before we start using Git, there are some files we don’t want it to track, or even to bother us with. These files can be put in a .gitignore file in the root of the project, but as before, we want to avoid putting any Git stuff in the website’s document root. Instead, you can use the $GIT_DIR/info/exclude file in the git repository to set things that should be ignored.
vim [/PATH/TO/WHERE/YOU/MADE/THE/.GIT/DIRECTORY/git]/info/exclude
add the following (you don’t need the comments, they are just there for explanation):
# WordPress configuration file # We don't want this being transfered, since our local system will have a different configuration file when we set up the development site. wp-config.php wp-config-sample.php #Text editor backup files # Many text editors save backup files when you are editing. Sometimes, these files can remain after you are done working. # While having backup files might be useful so you don't lose your work, they present a security issue if they are transfered to the webserver. # Without proper configuration, the webserver might show unauthorized users backup files as plaintext, while it might deny access to the actual file. # You don't want this to happen, especially with files like wp-config.php or other setting files. # If you know of any other backup files that an editor you use might create, add them here. *~ *.sw *.swo *.swp *.swap *.sav *.bak #Directory Files #Dolphin (KDE file manger) seems to create this file to store settings, which is fine, but I don't want configuration for a file manager stored on my server. .directory
If you have anything else (plugins, uploads, etc.) that you do not want to be synchronized with your development side, read the .gitignore documentation and find the appropriate rule to exclude that from git.
make the initial Git commit
git status
You should see that nothing is staged yet.
Assuming that there is nothing you do not want tracked that is not already excluded in the exclude file we edited earlier, you can add all the files in your document root:
git add .
If you are cautious, you might want to look through all the files that you added to make sure nothing is in there that you don’t want:
git status | less
Before you can commit, you need to configure the name and email your commits will appear under:
git config user.name "[name]"
git config user.email "[email]"
I use “Aidan Montare (server)” as my name just to denote which commits come from the server and which come from the client, which we will configure later.
Now you can make a commit:
git commit
This will open an editor and allow you to add a commit message.
If you run git status again, you will see that there are no changes listed.
configure the server to receive commits properly
Once we are done setting everything up, we will have changes on our local copy of the site that we will want to push to the server. However, we would then have to run git checkout -f to update the contents of website’s document root with our changes. Otherwise, Git sees the differences between the old files and the changes you pushed, and actually views the working directory as having lots of unstaged changes that you want to commit (except you don’t, because that would actually undo the development you did).
To make this easier, we will configure the server to automatically checkout the latest master branch after it receives a push. Git hooks allow you to do this.
Also, Git does not always seem to track file permissions the way you intend it too. While there are some extensions to Git that allow this, I am not in the habit of setting the correct file permissions when I am developing, so I think its just easier to reset all the file permissions after a push. This enforces the file permissions you want, regardless of whatever Git tries to do or lazy development leads you to forget.
vim [PATH_TO_GIT_DIR/git]/hooks/post-receive
add the following:
#!/bin/bash GIT_DIR=[PATH_TO_GIT_DIR/git] GIT_WORK_TREE=[PATH_TO_WEBSITE_DOCUMENT_ROOT] git checkout -f chmod -R 755 [PATH_TO_WEBSITE_DOCUMENT_ROOT]
Then change the file permissions to allow git to execute the script:
chmod +x [PATH_TO_GIT_DIR/git]/hooks/post-receive
Now, after we set the client up, you can run git push origin master and have the changes appear on your website right away.
Client-Side Configuration
This configuration applies to the local computer you are using to manage your website.
install git
sudo apt-get install git
If you want some useful Git visual tools (for those new to Git), run the following
sudo apt-get install gitk git-gui gitg
Read their man pages for more information, you might find them helpful as you learn Git.
clone your repository from your server
Make a new folder in a logical place on your client. Call it something useful. Mine is called “aidanmontare.net-dev”.
Clone the repository into this folder.
git clone [SERVER_USERNAME]@[SERVER_ADDRESS]:[/PATH/TO/GIT/FOLDER/ON/YOUR/SERVER]
Enter into this folder, and you should see all of your files.
configuring Git to make commits
As on the server, you need to set the username and email before you can commit anything:
git config user.name "[name]"
git config user.email "[email]"
exclude certain files
For redundancy, we will also add the exclusion list we set earlier to the client as well:
vim .git/info/exclude
add the following:
# WordPress configuration file wp-config.php wp-config-sample.php #Text editor backup files *~ *.sw *.swo *.swp *.swap *.sav *.bak #Directory Files .directory
change file permissions
As mentioned above, Git and file permissions can quickly become a mess. That’s why my post-receive hook is set to checkout the new changes and then reapply the correct file permissions. However, if you were to run git status on the webserver, you might see a lot of files modified. This would occur if the permissions on these files on your local computer is different that what the post-receive hook sets them to (mine sets 755).
This isn’t really a problem, since you won’t be doing development on the server. But if it bothers you, here is how to fix it:
On your client:
chmod -R 755 [PATH/TO/DEVELOPMENT/SITE/REPO]
cd [PATH/TO/DEVELOPMENT/REPO]
git add .
git commit -m "fixed file permissions"
git push origin master
This is also a good first test to see if you are able to push to your server.
Now you should be able to run git status on your server and see that the working directory is clean. It may become dirty again over time, but that is okay, since the post-receive hook will always make sure you have the correct permissions.
Client-Side Development Site
Since we now have all the files that make WordPress run on our local system, we can setup a development copy of the site so that we can locally test changes. Feel free to skip this if you don’t want to have this, but it’s easy enough and can be quite useful, so I would recommend it.
Basically, we will be installing WordPress again on our local system. We won’t worry about security for this, since we will be the only one’s accessing it (make sure your firewall is set so this is actually the case).
install necessary packages
sudo apt-get install apache2 mysql-server php5 php5-mysql php-pear
set apache global configuration
sudo vim /etc/apache2/apache2.conf
add the following:
Options FollowSymLinks AllowOverride FileInfo Require all granted Require all denied RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] # Block the include-only files. RewriteBase / RewriteRule ^wp-admin/includes/ - [F,L] RewriteRule !^wp-includes/ - [S=3] RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] RewriteRule ^wp-includes/theme-compat/ - [F,L]
This configuration should be basically the same as your actually webserver, so feel free to change what I have to make it closer to your setup. However, not that we will not bother will https:// for this dev site.
add virtual host for the development site
sudo vim /etc/apache2/sites-available/[GIVE_IT_A_NAME].conf
# Admin email, Server Name (domain name), and any aliases ServerAdmin [NAME]@[HOSTNAME_OF_YOUR_SYSTEM] ServerName [HOSTNAME] # Index file and Document Root (where the public files are located) DirectoryIndex index.html index.php DocumentRoot [PATH_TO_REPOSITORY] # Log file locations LogLevel warn #ErrorLog /home/webuser/public/aidanmontare.net/log/error.log #CustomLog /home/webuser/public/aidanmontare.net/log/access.log combined # Lets use system logs that aren't writable by webuser ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined RewriteEngine on RewriteOptions Inherit
enabled necessary modules
You need mod_rewrite for all of this, so go and enable it:
sudo a2enmod rewrite
enable the virtual host
sudo a2ensite [WHATEVER_YOU_NAMED_THE_CONF_FILE]
disable the default virtual host
We don’t need this, so you can disable it:
sudo a3dissite 000-default
restart apache
sudo service apache2 restart
test
Open a browser and go to localhost.
get a new wp-config-sample.php
On my site I deleted this after installation, and it’s set to be ignored in our git exclusion list. However, you need it to install WordPress locally, so go grab one from the WordPress.org site.
create a new mysql database
mysql -u root -p
enter the password of the mysql root user that you set when installing mysql
create database wp_dev;
grant all privileges on wp_dev.* to "wp_dev_user"@"localhost" identified by "[PASSWORD];
flush privileges;
exit
install WordPress
Browse to localhost in your favorite browser and go through the installation process. Input the database information you just set. When it gives you a wp-config.php file, copy the contents and paste it into the wherever your development files are.
test logging into the development site
When you are done the installation process, make sure it works by logging in and looking around.
configure the development site to match your real site
Your development site will have anything that was in the database of your real site. That includes all your page and post content, but also settings and plugin configuration, etc. So you might want to go and enable the theme you had chosen and turn on your plugins. The development site is for you, so set it up however you like.
If you want to be really fancy, you might come up with a way to copy parts of your live site’s database to the development site, but I don’t think that’s necessary.
Finishing Up
start apache
sudo service apache2 start
test you site
Go to you website and make sure everything works as expected.
Also do one last ls -a in your web document root to make sure there are no Git files sitting there.
opening up for business
sudo vim /etc/iptables.firewall.rules
remove "-s [YOUR_IP]" to the allow statements for ports 80 and 443
sudo iptables-restore < /etc/iptables.firewall.rules
If you also changed any IPv6 rules, then make sure to undo those changes.
Using This System to Manage Updates
At this point, we have a very simple project. Our client has a single master branch and two commits (the first one and the one to fix the file permissions). The server has the same.
to be continued…
Doing Development Projects on the Side
References and Inspirations
Tracking WordPress using Git by Ian McKellar
git by example – upgrade wordpress like a ninja by Martin Matusiak
Adding Git to your WordPress development workflow – an introduction by Chris Olbekson
Speed Up Your WordPress Development Cycle With Git by Clint Berry
Using Git to manage a web site by Abhijit Menon-Sen
Further Reading
WordPress local dev tips: DB & plugins by Mark Jaquinth
Pro Git Book, a great introduction to using Git in all kinds of situations
Git Hooks Section of the Pro Git Book
Git Hooks Docs