Create a gist now

Instantly share code, notes, and snippets.

Embed
Using Git to Manage a Live Web Site

Using Git to Manage a Live Web Site

Overview

As a freelancer, I build a lot of web sites. That's a lot of code changes to track. Thankfully, a Git-enabled workflow with proper branching makes short work of project tracking. I can easily see development features in branches as well as a snapshot of the sites' production code. A nice addition to that workflow is that ability to use Git to push updates to any of the various sites I work on while committing changes.

Contents

Prerequisites

  • Git, installed on both the development machine and the live server
  • a basic working knowledge of Git, beginners welcome!
  • passwordless SSH access to your server using pre-shared keys
  • a hobby (you'll have a lot of extra time on your hands with the quicker workflow)

back to top


Getting Started

Installing Git

Git is available for Windows, Linux, and Mac. Getting it installed and running is a pretty straightforward task. Check out the following link for updated instructions on getting Git up and running.

Getting Started Installing Git

You'll need to have Git installed on your development machines as well as on the server or servers where you wish to host your website. This process can even be adapted to work with multiple servers such as mirrors behind a load balancer.

back to top

Setting up Passwordless SSH Access

The process for updating a live web server relies on the use of post hooks within the Git environment. Since this is fully automated, there is no opportunity to enter login credentials while establishing the SSH connection to the remote server. To work around this, we are going to set up passwordless SSH access. To begin, you will need to SSH into your server.

ssh user@hostname

Next, you'll need to make sure you have a ~/.ssh in your user's home directory. If not, go ahead and create one now.

mkdir ~/.ssh

On Mac and Linux, you can harness the power of terminal to do both in one go.

if [ ! -d ~/.ssh ]; then mkdir ~/.ssh; fi

Next you'll need to generate a public SSH key if you don't already have one. List the files in your ~/.ssh directory to check.

ls -al ~/.ssh

The file you're looking for is usually named similarly to id_rsa.pub. If you're not sure, you can generate a new one. The command below will create an SSH key using the provided email as a label.

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

You'll probably want to keep all of the default settings. This will should create a file named id_rsa in the ~/.ssh directory created earlier.

When prompted, be sure to provide a secure SSH passphrase.

If you had to create an SSH key, you'll need to configure the ssh-agent program to use it.

ssh-add ~/.ssh/id_rsa

If you know what you are doing, you can use an existing SSH key in your ~/.ssh directory by providing the private key file to ssh-agent.

If you're still not sure what's going on, you should two files in your ~/.ssh directory that correspond to the private and public key files. Typically, the public key will be a file by the same name with a .pub extension added. An example would be a private key file named id_rsa and a public key file named id_rsa.pub.

Once you have generated an SSH key on your local machine, it's time to put the matching shared key file on the server.

ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname

This will add your public key to the authorized keys on the remote server. This process can be repeated from each development machine to add as many authorized keys as necessary to the server. You'll know you did it correctly when you close your connection and reconnect without being prompted for a password.

back to top


Configuring the Remote Server Repository

The machine you intend to use as a live production server needs to have a Git repository that can write to an appropriate web-accessible directory. The Git metadata (the .git directory) does not need to be in a web-accessible location. Instead, it can be anywhere that is user-writeable by your SSH user.

back to top

Setting up a Bare Repository

In order to push files to your web server, you'll need to have a copy of your repository on your web server. You'll want to start by creating a bare repository to house your web site. The repository should be set up somewhere outside of your web root. We'll instruct Git where to put the actual files later. Once you decide on location for your repository, the following commands will create the bare repository.

mkdir mywebsite.git
cd mywebsite.git
git init --bare

A bare repository contains all of the Git metadata without any HEAD. Essentially, this means that your repository has a .git directory, but does not have any working files checked out. The next step is to create a Git hook that will check out those files any time you instruct it to.

If you wish to run git commands from the detached work tree, you'll need to set the environmental variable GIT_DIR to the path of mywebsite.git before running any commands.

back to top

Add a Post-Receive Hook

Create a file named post-receive in the hooks directory of your repository with the following contents.

#!/bin/sh
GIT_WORK_TREE=/path/to/webroot/of/mywebsite git checkout -f

Once you create your hook, go ahead and mark it as executable.

chmod +x hooks/post-receive

GIT_WORK_TREE allows you to instruct Git where the working directory should be for a repository. This allows you to keep the repository outside of the web root with a detached work tree in a web accessible location. Make sure the path you specify exists, Git will not create it for you.

back to top


Configuring the Local Development Machine

The local development machine will house the web site repository. Relevant files will be copied to the live server whenever you choose to push those changes. This means you should keep a working copy of the repository on your development machine. You could also employ the use of any centralized repository including cloud-based ones such as GitHub or BitBucket. Your workflow is entirely up to you. Since all changes are pushed from the local repository, this process is not affected by how you choose to handle your project.

back to top

Setting up the Working Repository

On your development machine, you should have a working Git repository. If not, you can create on in an existing project directory with the following commands.

git init
git add -A
git commit -m "Initial Commit"

back to top

Add a Remote Repository Pointing to the Web Server

Once you have a working repository, you'll need to add a remote pointing to the one you set up on your server.

git remote add live ssh://server1.example.com/home/user/mywebsite.git

Make sure the hostname and path you provide point to the server and repository you set up previously. Finally, it's time to push your current website to the live server for the first time.

git push live +master:refs/head/master

This command instructs Git to push the current master branch to the live remote. (There's no need to send any other branches.) In the future, the server will only check out from the master branch so you won't need to specify that explicitly every time.

back to top


Build Something Beautiful

Everything is ready to go. It's time to let the creative juices flow! Your workflow doesn't need to change at all. Whenever you are ready, pushing changes to the live web server is as simple as running the following command.

git push live

Setting receive.denycurrentbranch to "ignore" on the server eliminates a warning issued by recent versions of Git when you push an update to a checked-out branch on the server.

back to top


Additional Tips

Here are a few more tips and tricks that you may find useful when employing this style of workflow.

back to top

Pushing Changes to Multiple Servers

You may find the need to push to multiple servers. Perhaps you have multiple testing servers or your live site is mirrored across multiple servers behind a load balancer. In any case, pushing to multiple servers is as easy as adding more urls to the [remote "live"] section in .git/config.

[remote "live"]
    url = ssh://server1.example.com/home/user/mywebsite.git
	url = ssh://server2.example.com/home/user/mywebsite.git

Now issuing the command git push live will update all of the urls you've added at one time. Simple!

back to top

Ignoring Local Changes to Tracked Files

From time to time you'll find there are files you want to track in your repository but don't wish to have changed every time you update your website. A good example would be configuration files in your web site that have settings specific to the server the site is on. Pushing updates to your site would ordinarily overwrite these files with whatever version of the file lives on your development machine. Preventing this is easy. SSH into the remote server and navigate into the Git repository. Enter the following command, listing each file you wish to ignore.

git update-index --assume-unchanged <file...>

This instruct Gits to ignore any changes to the specified files with any future checkouts. You can reverse this effect on one or more files any time you deem necessary.

git update-index --no-assume-unchanged <file...>

If you want to see a list of ignored files, that's easy too.

git ls-files -v | grep ^[a-z]

back to top


References

@jbialy

This comment has been minimized.

Show comment
Hide comment
@jbialy

jbialy Apr 25, 2017

Thanks for creating this; great tips!

jbialy commented Apr 25, 2017

Thanks for creating this; great tips!

@yanxun827

This comment has been minimized.

Show comment
Hide comment
@yanxun827

yanxun827 Aug 21, 2017

Hi, I assume that the '###' before each section names are meant to be headers.
If so, a spacebar is needed between the hashes and the text for markdown to display correctly, or else its just

###section

instead of

section

Hi, I assume that the '###' before each section names are meant to be headers.
If so, a spacebar is needed between the hashes and the text for markdown to display correctly, or else its just

###section

instead of

section

@glefait

This comment has been minimized.

Show comment
Hide comment
@glefait

glefait Aug 27, 2017

I would replace

ssh` user@hostname 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

with

ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname

glefait commented Aug 27, 2017

I would replace

ssh` user@hostname 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

with

ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname
@exenza

This comment has been minimized.

Show comment
Hide comment
@exenza

exenza Aug 30, 2017

If you work with ATOM as an editor be sure to name the remote branch "origin" and not "live" or ATOM won't be able to push.
Also if your local username differ from the remote one you should specify the username when adding the remote:

git remote add origin ssh://username@server1.example.com/home/user/mywebsite.git

If you change the remote name from "live" to "origin" obviously you should change the first push as well to:

git push origin +master:refs/head/master

exenza commented Aug 30, 2017

If you work with ATOM as an editor be sure to name the remote branch "origin" and not "live" or ATOM won't be able to push.
Also if your local username differ from the remote one you should specify the username when adding the remote:

git remote add origin ssh://username@server1.example.com/home/user/mywebsite.git

If you change the remote name from "live" to "origin" obviously you should change the first push as well to:

git push origin +master:refs/head/master

@tobypeschel

This comment has been minimized.

Show comment
Hide comment
@tobypeschel

tobypeschel Sep 19, 2017

Good tips. One issue: the headings need spaces inserted, e.g. ###Header should be ### Header, otherwise they aren't formatted as intended. Also, the first reference 404s for me, it seems it's moved to https://blog.sebduggan.com/2012/03/13/deploy-your-website-changes-using-git/

Good tips. One issue: the headings need spaces inserted, e.g. ###Header should be ### Header, otherwise they aren't formatted as intended. Also, the first reference 404s for me, it seems it's moved to https://blog.sebduggan.com/2012/03/13/deploy-your-website-changes-using-git/

@freshfruits

This comment has been minimized.

Show comment
Hide comment

Good stuff.

@tomhag

This comment has been minimized.

Show comment
Hide comment
@tomhag

tomhag Nov 30, 2017

Hi Thanks for a great tutorial. Sorry for the newbie question but after I have setup everything; everytime I push from the local website directory, the repo on the remote webserver is updated (home/myrepo.git). However how do I get this automatically cloned into the /www directory so I can see the changes live?

tomhag commented Nov 30, 2017

Hi Thanks for a great tutorial. Sorry for the newbie question but after I have setup everything; everytime I push from the local website directory, the repo on the remote webserver is updated (home/myrepo.git). However how do I get this automatically cloned into the /www directory so I can see the changes live?

@marty8zhang

This comment has been minimized.

Show comment
Hide comment
@marty8zhang

marty8zhang Dec 9, 2017

In 'Additional Tips' -> 'Ignoring Local Changes to Tracked Files':
git update-index --assume-unchanged <file...>
should be used on your local dev repo rather than on the remote server. If you used it on a bare repo on the remote server, an error will be thrown saying that the command needs a working tree.

In 'Additional Tips' -> 'Ignoring Local Changes to Tracked Files':
git update-index --assume-unchanged <file...>
should be used on your local dev repo rather than on the remote server. If you used it on a bare repo on the remote server, an error will be thrown saying that the command needs a working tree.

@domemvs

This comment has been minimized.

Show comment
Hide comment
@domemvs

domemvs Dec 27, 2017

Thank you! How could this look like in a folder with a build or src folder and a prod/dist folder?

|-- /projectfolder
|   |-- app.js
|   |-- index.jsx
|   |-- style.scss
|   |-- /dist
|   |   |-- index.html
|   |   |-- style.css
|   |   |-- bundle.js

I don't care about the build files being in my bare repo but I want only the files from the dist folder to be in my htdocs folder. How can I achieve this?

domemvs commented Dec 27, 2017

Thank you! How could this look like in a folder with a build or src folder and a prod/dist folder?

|-- /projectfolder
|   |-- app.js
|   |-- index.jsx
|   |-- style.scss
|   |-- /dist
|   |   |-- index.html
|   |   |-- style.css
|   |   |-- bundle.js

I don't care about the build files being in my bare repo but I want only the files from the dist folder to be in my htdocs folder. How can I achieve this?

@boyd0029

This comment has been minimized.

Show comment
Hide comment
@boyd0029

boyd0029 Jan 25, 2018

I think you are missing and 's'?

git push live +master:refs/head**s**/master

I think you are missing and 's'?

git push live +master:refs/head**s**/master
@dbengston

This comment has been minimized.

Show comment
Hide comment
@dbengston

dbengston Feb 28, 2018

Excellent instructions. Thank you so much for putting this together. As others have mentioned, depending on your host you might need to use

ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname
instead of
ssh user@hostname 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

This worked at Dreamhost.

Excellent instructions. Thank you so much for putting this together. As others have mentioned, depending on your host you might need to use

ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname
instead of
ssh user@hostname 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

This worked at Dreamhost.

@e-baker

This comment has been minimized.

Show comment
Hide comment
@e-baker

e-baker Mar 1, 2018

@domemvs You could use a symbolic link to the dist folder so that the server only saw that one.

e-baker commented Mar 1, 2018

@domemvs You could use a symbolic link to the dist folder so that the server only saw that one.

@Nilpo

This comment has been minimized.

Show comment
Hide comment
@Nilpo

Nilpo Apr 11, 2018

Thanks for the feedback. ssh-copy-id is much more widely supported now. I definitely recommend that as well. I will update the instructions with that.

Owner

Nilpo commented Apr 11, 2018

Thanks for the feedback. ssh-copy-id is much more widely supported now. I definitely recommend that as well. I will update the instructions with that.

@mjc4

This comment has been minimized.

Show comment
Hide comment
@mjc4

mjc4 Apr 18, 2018

@Nilpo thanks for the tutorial. I did everything as you explained but when I try to push to the remote server (git push live +master:refs/heads/master) I get a prompt to enter a password. Let me explain:

Let's say my user on my local machine is: localuser@local and the remote user is : remoteuser@remoteserver
when I push my changes to the remote server I get this prompt:

enter the localuser@remoteserver's password:

I entered both my local user and remote user password, nothing works and I got denied access. Has anyone faced a similar situation??

mjc4 commented Apr 18, 2018

@Nilpo thanks for the tutorial. I did everything as you explained but when I try to push to the remote server (git push live +master:refs/heads/master) I get a prompt to enter a password. Let me explain:

Let's say my user on my local machine is: localuser@local and the remote user is : remoteuser@remoteserver
when I push my changes to the remote server I get this prompt:

enter the localuser@remoteserver's password:

I entered both my local user and remote user password, nothing works and I got denied access. Has anyone faced a similar situation??

@mindpixel-labs

This comment has been minimized.

Show comment
Hide comment
@mindpixel-labs

mindpixel-labs Apr 19, 2018

Thanks for taking your time to create this! This workflow has been great and has made me 10x more productive.

Thanks for taking your time to create this! This workflow has been great and has made me 10x more productive.

@mindpixel-labs

This comment has been minimized.

Show comment
Hide comment
@mindpixel-labs

mindpixel-labs Apr 23, 2018

@Nilpo The only thing I've noticed with this is that if your server has a limit or cap on your I/O (throughput) you run the risk of maxing out your system resources and possibly having your server throw a 503 Error. Is there anyway to have Git throttle the read/write speed so it doesn't use too much system resources?

@Nilpo The only thing I've noticed with this is that if your server has a limit or cap on your I/O (throughput) you run the risk of maxing out your system resources and possibly having your server throw a 503 Error. Is there anyway to have Git throttle the read/write speed so it doesn't use too much system resources?

@navkarjain

This comment has been minimized.

Show comment
Hide comment
@navkarjain

navkarjain May 7, 2018

Yeah it should be "heads" instead of "head" in the command git push live +master:refs/head/master. I spent hours
trying to figure out why this was not working when I read a comment made by boyd0029 above.

Yeah it should be "heads" instead of "head" in the command git push live +master:refs/head/master. I spent hours
trying to figure out why this was not working when I read a comment made by boyd0029 above.

@prestodigital

This comment has been minimized.

Show comment
Hide comment
@prestodigital

prestodigital May 18, 2018

using OSX10.11.6 My server login userid is not the same as the as my OSX user - my keys are setup so I can login to the server without a password. How to I override the default userid in the git remote add live ssh://www.mypath.com/mydirectory ???

prestodigital commented May 18, 2018

using OSX10.11.6 My server login userid is not the same as the as my OSX user - my keys are setup so I can login to the server without a password. How to I override the default userid in the git remote add live ssh://www.mypath.com/mydirectory ???

@davik4life

This comment has been minimized.

Show comment
Hide comment
@davik4life

davik4life Jun 11, 2018

Please, I am stuck on the process of adding a Post-Receive Hook. I am not getting the path right. Any help would be well appreciated.

Please, I am stuck on the process of adding a Post-Receive Hook. I am not getting the path right. Any help would be well appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment