Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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 a location for your repository, the following commands will create the bare repository.

git init --bare --shared mywebsite.git && cd $_

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. You may optionally specify a branch name to checkout. If unspecified, the default master or main branch is used.

#! /bin/sh
GIT_WORK_TREE=/path/to/webroot/of/mywebsite git checkout -qf [--detach] [<branch>]

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 one 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. If your remote user name is different from that of your local machine, you'll need to modify the above command to include the remote user name.

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

Finally, it's time to push your current website to the live server for the first time.

git push live +master:refs/heads/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.

If you ever need to force a reload on the server side without making any local changes, simply create an empty commit and push it!

git commit --allow-empty -m "trigger update"
git push live

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

@TheGrimSilence
Copy link

TheGrimSilence commented Jun 24, 2020

So, I like this. But I also think it could use some changes. Git is always a saving grace, but I don't think your server should be the host. Should anything happen to the server you're losing too much. I think a service like GitHub or its competitors should be the host. You push your changes to GitHub, and you have webhooks to listen for events. Ideally, you have releases, and when you create a new release then your server should pull that release and make it the new live server.

I'm self-taught and definitely not an expert, this is just my point of view if anyone experienced could shed some pros and cons, or their opinions, I'll listen. I got here because I myself am looking for an ideal setup.

@hayden-t
Copy link

hayden-t commented Jun 24, 2020

fair point, but i have aws backups of my home dirs so that grabs my repos too

@hawk-weed
Copy link

hawk-weed commented Aug 21, 2020

Hi, new to github. I am working a dev server and set my repository to it's location. Is there a way to delete a git repository while retaining its content? Or, should I copy the contents, delete the repository, move the content back, then setup a repository in new location locally and point it to my dev server?

@Nilpo
Copy link
Author

Nilpo commented Aug 21, 2020

Hi, new to github. I am working a dev server and set my repository to it's location. Is there a way to delete a git repository while retaining its content? Or, should I copy the contents, delete the repository, move the content back, then setup a repository in new location locally and point it to my dev server?

Just delete the hidden .git directory. It contains all of the Git tracking information. Your files will be left alone.

@hawk-weed
Copy link

hawk-weed commented Aug 21, 2020

Hi, new to github. I am working a dev server and set my repository to it's location. Is there a way to delete a git repository while retaining its content? Or, should I copy the contents, delete the repository, move the content back, then setup a repository in new location locally and point it to my dev server?

Just delete the hidden .git directory. It contains all of the Git tracking information. Your files will be left alone.

Awesome!!! Thanks!

@sundersj
Copy link

sundersj commented Aug 31, 2020

Thank you for sharing this, but I am having a little, every time pushing codes to the remote repo, post-receive hook updating the codes fine but changing the file permission too.

Copy link

ghost commented Sep 12, 2020

fatal: This operation must be run in a work tree

@Nilpo
Copy link
Author

Nilpo commented Sep 12, 2020

Thank you for sharing this, but I am having a little, every time pushing codes to the remote repo, post-receive hook updating the codes fine but changing the file permission too.

Git doesn't track file permissions. It only tracks whether a file is executable. Non-executable files get 644 when checked out. Executable gets 755. You might try this:

git config --local core.fileMode false

@Nilpo
Copy link
Author

Nilpo commented Sep 12, 2020

fatal: This operation must be run in a work tree

The work tree variable is set incorrectly in your post-receive hook.

@zarnab87
Copy link

zarnab87 commented Oct 6, 2020

I have a project with collaboration on git and want to upload that project live for future modification. Can one of my collaborators push code to live server by setting up these things on his local machines? I'm the one who created the repo and has collaborators on my project but wants to push the code by another machine (my collaborator's machine).

@stiber
Copy link

stiber commented Jan 18, 2021

Note that "master" branch above needs to be changed to "main", for GitHub. I believe GitHub made that change in late 2020.

@TheGrimSilence
Copy link

TheGrimSilence commented Feb 7, 2021

"main" is optional and not required. As there's no real reason for it.

@stiber
Copy link

stiber commented Feb 7, 2021

Well, every repo needs a base branch; "main" is the default on GitHub since October 2020. Anyone creating a new repo will get that as their base branch. Since these instructions are hosted on GitHub, they would be more complete if they indicated that and told the user to either change their base branch to "master" in their repo settings (and, of course, either rename or create a "master" branch) or alter the setup described here to use "main".

@TheGrimSilence
Copy link

TheGrimSilence commented Feb 9, 2021

Fair point 👍

@dzianis-bocharov
Copy link

dzianis-bocharov commented May 15, 2021

I have used "#! /bin/sh" with space inside instead of "#!/bin/sh" , because in my case it first showed mistake for hook : "no such file or directory", spent alot hours to understand this, thanks for your post :-)

@Nilpo
Copy link
Author

Nilpo commented May 16, 2021

If some files are deleted directly in the web server, git push live will not replace/update it.

Is there a way to do a forced git push live which will re-upload files? (even if there is no local changes on the local computer)

Just create an empty commit and push!

git commit --allow-empty -m "trigger update"     <--- creates a commit without adding any changed files
git push live

@Nilpo
Copy link
Author

Nilpo commented May 16, 2021

I have used "#! /bin/sh" with space inside instead of "#!/bin/sh" , because in my case it first showed mistake for hook : "no such file or directory", spent alot hours to understand this, thanks for your post :-)

Spaces in the shebang line are strictly optional. This really shouldn't cause a problem. I'll add the space, but I'm curious what system you are working with.

@Nilpo
Copy link
Author

Nilpo commented May 16, 2021

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

I originally used ssh-copy-id but I removed it for compatibility reasons. It doesn't exist on every system. It's definitely good to use it on systems that support it!

@Nilpo
Copy link
Author

Nilpo commented May 16, 2021

Well, every repo needs a base branch; "main" is the default on GitHub since October 2020. Anyone creating a new repo will get that as their base branch. Since these instructions are hosted on GitHub, they would be more complete if they indicated that and told the user to either change their base branch to "master" in their repo settings (and, of course, either rename or create a "master" branch) or alter the setup described here to use "main".

I may update this on the next major edit. Frankly, it's not strictly necessary and the Git binary still uses master. Github still works with master as well. GH has actually made it more confusing that necessary.

We're not creating the repo on Github, it's created locally if you follow the instructions so GH won't care either.

I have added this to my list of things to address though. Thanks for the suggestion.

@rodneytamblyn
Copy link

rodneytamblyn commented Sep 25, 2021

Can someone extend this GIST to explain how to set file ownership and permissions on files in the post-receive hook? Assume for example that ssh session is root, but files need to be deployed in web user.

@rodneytamblyn
Copy link

rodneytamblyn commented Sep 25, 2021

Further to my question, my solution (I don't know if this is the best, but it seems to work)
su -c "GIT_WORK_TREE=/PATH/WEBSITE git checkout -f" web
... where 'web' in this case is the user I want the files to be owned by.

To get this solution working you may need to change existing ownership and file permissions of the bare repository to allow the web user to read, as otherwise it will generate permissions errors, as you are "su" to the context of "web" user.
so for example:
chown root:web /path/to/barerepo.git
chown root:web /path/to/website
... and chmod the file permissions.

Works for me, but there may be better ways - please advise if you know them.

@barnamah
Copy link

barnamah commented Oct 6, 2021

After the first step which I created bare repository /home/xyz/example.com.git
git init --bare --shared

now what should I do ? I can't believe there is no tutorial anywhere on the web to make it work.
my web directory is located /home/www/example.com/
how can I use git? Actually what is the next step?

  1. Should copy files from /home/www/example.com/ to /home/xyz/example.com.git
  2. or there is a command get files from /home/www/example.com/

Year after year (may be 10 years now) I realize git is important but always find problem and give up.

@Nilpo
Copy link
Author

Nilpo commented Oct 7, 2021

After the first step which I created bare repository /home/xyz/example.com.git git init --bare --shared

now what should I do ? I can't believe there is no tutorial anywhere on the web to make it work. my web directory is located /home/www/example.com/ how can I use git? Actually what is the next step?

  1. Should copy files from /home/www/example.com/ to /home/xyz/example.com.git
  2. or there is a command get files from /home/www/example.com/

Year after year (may be 10 years now) I realize git is important but always find problem and give up.

If you have an existing web site, these instructions won't work for you easily. You should make a local copy of all of the web site files on your local PC. Then you should empty the web site directory on the server and follow these instructions. With a post-hook in place, you can return to your local PC and commit the web site files to your repository. This will push them back up to your server where they belong.

However, if you do not use Git, I strongly recommend that exercise caution before attempting this. You may want to consider not trying it at all. If you do this incorrectly there is a risk of losing data since your server directory is not empty to begin with.

Git can be complicated, but it's basic functions can be quite simple. I suggest checking out a good tutorial or YouTube video. 20 minutes of practice on a clean project and you will be up and running. You can set up a local web server and test all of this from your local machine before attempting this on a production server.

@Nilpo
Copy link
Author

Nilpo commented Oct 7, 2021

Maybe I should do a YouTube video demoing all of this.

@barnamah
Copy link

barnamah commented Oct 7, 2021

@Nilpo thank you for your reply. you still used terms that is not clear "post-hook". use simple words. if you are making a video then title should be "How to use Gig with existing website contents" and do not assume anything that the user know. I am creating tutorials on YouTube but when see tutorial on Git, they none can work in my case. They are all vague or they assume user knows stuff.
May be my understanding is wrong about git.
I assume that Git is a tool that I modify a file, it keeps the older file for me and I can check it any time to see what changes I have done. But no one explains how to use if efficiently. If Git does not or cannot upload the modified file to my live folder then why should I use Git? I can make copy of the file, edit it and use it. if I want to compare them later, I can use Notepad++ to compare two files or texts.

I gave up again.

@Nilpo
Copy link
Author

Nilpo commented Oct 8, 2021

@Nilpo thank you for your reply. you still used terms that is not clear "post-hook". use simple words. if you are making a video then title should be "How to use Gig with existing website contents" and do not assume anything that the user know. I am creating tutorials on YouTube but when see tutorial on Git, they none can work in my case. They are all vague or they assume user knows stuff. May be my understanding is wrong about git. I assume that Git is a tool that I modify a file, it keeps the older file for me and I can check it any time to see what changes I have done. But no one explains how to use if efficiently. If Git does not or cannot upload the modified file to my live folder then why should I use Git? I can make copy of the file, edit it and use it. if I want to compare them later, I can use Notepad++ to compare two files or texts.

I gave up again.

Git can do everything you've said.

Everything you need is step-by-step in the instructions. You should be able to follow it even if you've never used Git before. The instruction show how to set up the Post-Receive hook. This tells Git what to do wherever you commit new changes to your project folder.

I'm sorry that you are giving up, but all of the information you need to make this work is here. It's written for beginners.

@YungCrunchy
Copy link

YungCrunchy commented Mar 2, 2022

@Nilpo Hi Niplo, hope you are well and thank you for your tutorial. I have been getting an error on the "git push live +main:refs/heads/main" step. how can I approach this? it is initialized as shared

[local ~] git push live +main:refs/heads/main

fatal: '/example.git' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

@Nilpo
Copy link
Author

Nilpo commented Mar 25, 2022

Git is always a saving grace, but I don't think your server should be the host. Should anything happen to the server you're losing too much. I think a service like GitHub or its competitors should be the host.

You're absolutely right. The server isn't the central repository (although it acts like one). Just add another remote to your project and push changes to GitHub or whatever central repository you choose to use. You can even push them all simultaneously as I show later in the post.

@Nilpo
Copy link
Author

Nilpo commented Mar 25, 2022

@Nilpo Hi Niplo, hope you are well and thank you for your tutorial. I have been getting an error on the "git push live +main:refs/heads/main" step. how can I approach this? it is initialized as shared

[local ~] git push live +main:refs/heads/main

fatal: '/example.git' does not appear to be a git repository fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

It sounds like you aren't using the correct repository name in your project. The origin setting in your project must match the name of the *.git directory on the server. Have a look at the output from git remote -v show. In your case, it's probably showing example.git (based on your error) but the repository on your server was actually named something different. Perhaps the name of your site or domain.

@vigilancetech-com
Copy link

vigilancetech-com commented Jun 29, 2022

I've tried to follow these instructions as closely as possible but when I get to the git push to populate the web server's git repo it says everything is okay but the actual index.html file doesn't copy across.

Any idea how to debug this?

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