How to maintain a website with git

Applicable To: All servers/hostings
Updated: Wed 15 Jun, 2016 GMT

Git is a version control system. It can be used to manage and maintain websites which gives much more control and efficiency over other manual methods (like using Filezilla and other tools). It's feature rich and the time consumption for upload is super low compared to other non-git equivalent methods.

Preview

Managing website with Git is fairly easy, efficient and less time and data consuming once you setup everything appropriately. setting up Git for website management involves getting ssh access to remote server to create a Git repository there, then creating a Git repository in local or making the DocumentRoot (or the portion of website to be uploaded) in local a Git repository, adding the remote URL, git add, git commit and finally git push to upload the site to remote.

Why use git?

image for Git

Git is a version control system, not something like Filezilla, so why would I want to use git for managing my website? Well, the answer is simple: Git is an effective solution for website management which has some major advantages:

  1. Low time consumption: Git only sends the changes not the files. For example, let's say you have 2000 php files. You made 1byte of change on each file so there's 2K byte of changes total. Git will upload only those 2K bytes not the complete files. If you try to upload all these php files with Filezilla then it can take hours (depending on your connection speed) while Git will do it in just a few seconds!!!. Without using something like Filezilla you can compress the files yourself and then upload it too, but that will be much more tedious than just running a few line of Git commands.
  2. Low initial upload time: When you upload your site for the first time with Git, surely it will upload all the files, but as it compresses before sending to remote, the time taken will be much lesser than any other method (not using Git or other equivalents).
  3. Low data consumption: Git only sends the changes not the whole files. That minimizes the upload data size a lot.
  4. Super fast: Git compresses the changes before sending to remote. It is pretty well-know that uploading a single compressed file is much faster than uploading its' numerous small-sized contents.
  5. Multiple versions of site: You can have multiple versions of your site pretty easily and activate whichever version you like and whenever you like.
  6. Super easy to maintain: Git is very easy to maintain. It will surely make your life easier.

Let's start:

First we need to get our goals clarified. There are two types of Git repository that you might want to create on the remote host (server):

  1. Bare repository: If all you need is to push/upload your local changes to the remote without caring so much about the changes made on the server itself, then this is what you need. If you will only be pushing from local and there's no chance that the content won't be changed on the server side then choose this type of repository. It is much simpler and easier than maintaining a non-bare repository.
  2. Non-bare repository: When your content is changed on the server side and from local machine/s too, you need a non-bare repository to sync between both (or all) sides.

1. Prepare ssh access on remote:

We need ssh access to the remote host to open a terminal to run git commands. If ssh is not enabled in your remote host, then enable it from your hosting account control panel or contact them to enable ssh. For example, I had to contact through Live Chat with namecheap-support to enable ssh for my hosting account. Most of the time you have some options to enable it yourself from hosting control panel, though it may require some time (72 hrs max depending on your hosting provider) to fully enable ssh on your account.

Each hosting provider may have preferences of their own on which port they intend to allow you to use for ssh. For example, Godaddy allows port: 22, Namecheap allows port: 21098 etc...

You should generally search for the way your hosting provider recommends you to use ssh. Most renowned hosting providers have tutorials/documents available on how to use ssh on their servers. A simple Google search should bring it out.

2. Prepare Git on remote:

Git is generally installed on the remote server. If not, you can install it yourself if you are a sys-admin or have required permissions. If you are on a shared server then your best bet is to contact the hosting provider. Don't worry though, Git is installed on almost all Linux hosting services, though if you do happen to find some hosting provider that do not have Git installed, well, dump them altogether to a nearby dustbin, wash your hands with antiseptic and move on to a decent hosting provider.

Some hosting provider may have Git installed in a different directory than the standard install directory. In this case running git in a terminal for those servers will show error:

ssh -p port user@domain.com #Login to remote
#change port to actual port number
#change user to cpanel username or whatever
#change domain.com to your domain name or the actual server name.
git
git: command not found

This actually means Git is not installed in general case. But some hosting provider installs Git into a different directory which is not in the PATH environment variable. For example, Namecheap shared hosting has Git in /usr/local/cpanel/3rdparty/bin/ folder. So, you should first check how your hosting provider provides git to you. A simple Google search shall bring it out in most cases.

If the previous git command succeeded (it shows help message) you are good to go, or if the git resides in another directory than the standard install directories then all you have to do is add the path to the git binary to PATH environment variable. To do that you need to login to remote host and run these commands:

ssh -p port user@domain.com #Login to remote
#change port to actual port number
#change user to cpanel username or whatever
#change domain.com to your domain name or the actual server name.
var=/usr/local/cpanel/3rdparty/bin/
#change the path above according to your hosting provider
#[[ -d $var ]] && echo "valid" || echo "Not a directory"
#comment out the above line and run if you want to check if the path is valid.
if ! echo $PATH | grep -q  ":$var\([/:]\|$\)"; then echo "export PATH=\$PATH:$var" >> ~/.bashrc && . ~/.bashrc ;fi

That's it, path is added to the PATH environment variable. You should be able to access git from anywhere in your remote terminal. This is an example output of git command in remote:

server105 ~]$ git
usage: git [--version] [--help] [-C <path>] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]
           ...

3. Preparing Git and ssh on local machine:

As both ssh and Git are on-line on the remote side, we just need to have these two tools on local too. A basic Git and ssh install comes by default in most Unix/Linux based systems, if not then just install them.

On windows you can use git-for-windows (previously known as git-bash) or cygwin or other Git clients. I recommend git-for-windows or cygwin. If you install cygwin you should check openssh and git package for install.

4. Create a Git repository on remote:

On remote host we need to create a git repository to store our website. As mentioned earlier, you can have two types of repository there; hope you decided which one to use by now. Prepare two terminals and login to remote using ssh in one of them. We will call them local terminal and remote terminal respectively.

4.1 Creating a bare repository:

Maintaining a bare repository is simpler than a non-bare repository but for creating we can not say the same. This method has the advantage of keeping the DocumentRoot clean, i.e there will be no .git directory inside DocumentRoot.

mkdir ~/Git && cd ~/Git
#Git is the directory where we will keep our bare Git repositories.
#It's not the DocumentRoot, neither it is a subdir of DocumentRoot.
git init --bare myweb.git #creates a bare repo named myweb.git

Now we need a post-receive hook which will checkout the commits after a push. Run these commands on remote-terminal:

cd ~/Git/myweb.git
gwt=~/public_html #or whatever your DocumentRoot is
echo '#!/bin/sh' > hooks/post-receive
echo "GIT_WORK_TREE=$gwt git checkout -f" >> hooks/post-receive
chmod +x hooks/post-receive   #Give execute permission to the script

This will create a hooks/post-receive script with execution permission. You can see the content by running cat hooks/post-receive:

]$ cat hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/home/user/public_html git checkout -f

Now all is set for the remote repository. You won't have to deal with remote-terminal any more unless you are going to use a non-bare repository too.

Note: This does not work for git <1.5.4.3. Either update the git version to >1.6 on remote or use the non-bare repository method.

4.2 Creating a non-bare repository:

It's pretty simple. Just go to your DocumentRoot and run git init in the remote-terminal:

cd ~/public_html #or whatever the DocumentRoot is
git init

That's it. Now you have successfully created the non-bare repository which contains the website. All Git related stuffs will be put into a directory named .git; don't delete it. Keep the remote-terminal open, you will need it.

5. Creating Git repository on local:

Local git repository is non-bare. So it's simple, just the git init command inside the folder where you keep your website.

cd ~/mysite #or whatever path like /var/www/htdocs etc...
git init

Also add the ssh address of the remote repository:

git remote add origin ssh://user@domain.com:port/home/user/Git/myweb.git   # for bare repo
#git remote add origin ssh://user@domain.com:port/home/user/public_html     # for non-bare repo
#change port to actual port number
#change user to cpanel username or whatever
#change domain.com to your domain name or the actual server name.
#change /home/user/Git/myweb.git to the actual path of the repository in remote
#For bare repository, it's the git repo inside ~/Git in remote
#For non-bare repo, it's the DocumentRoot of remote

6. Upload the website to remote:

As the website folder itself is a non-bare Git repository we will just add and commit any changes and push the changes to remote.

6.1 Pushing to bare remote repo:

Run these commands on local-terminal:

#assuming we are inside the DocumentRoot
#otherwise run the cd command to get to the appropriate dir
git add --all :/ && git commit -m your-commit-message
git push --all

This will upload your site (or changes) to the remote. Changes will be immediately available.

6.2 Pushing to non-bare remote repo:

This is a bit tedious than the bare-repo method. We first need to detach the remote checked out branch. This can be avoided by setting receive.denycurrentbranch to "ignore" but we will not do that because it's highly unsafe.

Run these commands on remote terminal:

cd ~/mysite #or whatever path like /var/www/htdocs etc... (DocumentRoot)
git add --all :/ && git commit -m "site updated on server"
git checkout --detach

If the checkout command fails, then your git version is too old, you seriously need to consider updating the git version on remote. Though, it can be worked around:

cd ~/mysite #or whatever path like /var/www/htdocs etc... (DocumentRoot)
if ! git checkout --detach;then
    git add --all :/ &&
    git commit -m "site updated on server"
    git checkout old-master ||
    git checkout -b old-master
    git merge master
fi
#Don't copy the if block line by line. Copy altogether and paste to the remote-terminal and hit enter.

Above, we are checking if git checkout --detach is available, if it is, then we will use that otherwise we will use an extra branch named old-master. Thus your remote repository will contain two versions of your website: one is the latest and the other is previous latest. So, of course, you can activate the previous version of your website anytime you want and let users have that version instead of the latest. You might prefer this workaround (deleting the two lines containing if and fi) even if you have latest Git on remote in case having two versions is not a problem (negotiations on regards of size).

Run these commands on local-terminal:

#assuming we are inside the DocumentRoot
#otherwise run the cd command to get to the appropriate dir
git add --all :/ && git commit -m your-commit-message
#git pull origin master
#git add --all :/ && git commit -m your-commit-message
#uncomment and run the above two commands if your site was changed
#on the server.
git push --all 
#or more precisely 
#git push origin master

This will upload your site (or changes) to the remote.

We need an additional step to make the changes available i.e we need to re-attach the detached branch.

Run this/these command/s on remote terminal:

#git add --all :/ && git commit -m update
#The above command is not generally needed.
#comment it out and run it if needed (when you change something on detached state)
git checkout master

The above command is same even when we had to use the workaround for old git version.

Now the changes will be available immediately.

Uploading portion of the website:

We talked about DocumentRoot all the way as our main goal. But it can be done with any portion of the website too. The procedure is same i.e instead of DocumentRoot you will use the directory pointing to that portion both in remote and local.

Managing multiple versions of the site:

We can manage multiple versions of the site with Git. Version management is Gits' core and basic forte. To create a new version from the current branch you just run the command:

git checkout -b version_number

A new branch named version_number will be created with the content from the branch on which the above command was run. To activate a version of the site, just checkout that branch in remote (git checkout branch_name).

Managing backup:

You may wish to backup your website in one of the Git flavored code hosting sites like Github or Bitbucket. To do that, create a repository on those sites and copy the URL and add that URL to your local repository. For example

git remote add bit https://user@bitbucket.org/user/myweb.git

will add the remote URL for myweb.git in Bitbucket. To push to this repository, run this code:

git push bit master

Thus you can keep as many backups as you want in your preferred code hosting sites.

Important notes:

Permission management:

Git sends files as it is in its default setting. So if the file and directory permissions are messed up in local, they will be messed up in remote too. For example, if you have 655 permission for index.php files, that will give you a 500 internal error saying that index.php is writable by group. You should generally give the php files 644 permission or better 600 permission. You can run the following code on remote-terminal to fix php file permission on current directory and its' subdirectories:

find . -type f -name '*.php' -exec chmod 600 {} \;

This will, however, mess up your file modification time. So don't do this; instead use this long command which will preserve the timestamp:

find . -type f -name '*.php' -exec touch -r {} .timestamp.nb \; -exec chmod 600 {} \; -exec touch -r .timestamp.nb {} \;

For bare repository you can automate the above command by putting it into hooks/post-receive in ~/Git/myweb.git directory. Run the following command in remote terminal to do this:

gwt=~/public_html #or whatever your DocumentRoot is
echo "find $gwt -type f -name '*.php' -exec touch -r {} .timestamp.nb \; -exec chmod 600 {} \; -exec touch -r .timestamp.nb {} \;" >> ~/Git/myweb.git/hooks/post-receive