Using Git to Deploy Code

Like many other programmers, I use git on a daily basis to manage my code. In fact, I believe git is so useful that I list it as a required skill whenever I post job openings for programmers at work. Although it’s used by a lot of people, I often find that git is not utilized to its full potential. Mainly, I find that programmers will use git to version their code, but they still upload files through FTP or some other method. This article is going to show you how I use git to quickly and easily deploy code across multiple servers without using FTP.




Setting Up Your Remote Server

The following instructions apply to your remote (eg: Production) server(s).

  1. Create a new directory for your git repository (preferably outside of your www directory):
  2. Create a bare (empty) git repository inside of your new directory:
  3. Create a post-receive “hook”. This hook gets executed automatically each time you push code changes to the server. In our case, we’ll use the hook to checkout the latest code from our repository.

    Create /home/git/project_name.git/hooks/post-receive and add the following:

    Update GIT_WORK_TREE, replacing /var/www/project_name with the root directory of your project (where you would normally upload the files using FTP).

    ProTip™ – You can add other commands and shell code to the post-receive hook to do things like chmod files and directories, call other scripts, etc. each time you push updated code to the server.

  4. Make your post-receive hook executable:

Setting Up Your Local Server

The following instructions apply to your local (eg: Development) server.

  1. Navigate to wherever your git repository is located:
  2. Add a remote source that references the server and repository we setup in the previous steps:

    You can use whatever you want in place of production (such as staging or server1). Replace website.com with your server address, and be sure that home/git/project_name.git matches the directory you created in step 1.

    ProTip™ – Add your username to the url so you don’t have to type it each time: ssh://username@website.com...

  3. Do an initial push to the remote server, which will setup the master branch. If your branch is named something other than master then use its name in place of master in the following command:



Deploying Code

Each time that you make changes to your code commit them as usual (using $ git commit ... , etc.), and then run the following command to deploy your changes to the remote server:

If your branch isn’t named master then use your branch name instead. The main point here is the format of the command:

And that’s it! Now you can easily deply code across multiple servers using only a few commands.

Notes

Multiple Servers and/or Branches

If you have multiple servers and/or branches (such as development, staging, and production) then you’ll need to repeat all of the steps for each one. Here’s the same steps from above, only using staging for the server and the branch in place of production (server) and master (branch):

Remote Server

Local Server

Updating

Permissions

It’s important that file permissions are set so that the Apache (or whatever server you’re using) user can write to the directory that you defined as GIT_WORK_TREE. If not, then the files can’t be pulled from the repository and placed in the correct directory. This is especially true if you created your repository outside of your root www directory (as recommended).

Changing Remote Server Settings

If you ever need to change the information for one of your remote servers, the easiest way is to edit the git config file. On your local server, open the config file located inside of your git repository (.git/config). By default, the config file is pretty bare, so you won’t have any trouble finding what to edit:

19 responses to “Using Git to Deploy Code

  1. Hi Mike, thanks for the great article!

    I have set up the following on bluehost server.

    /public_html/ (base code of drupal multisite reached with http://markyoung.us) and 2 associated subsites
    /public_html/sites/client1.markyoung.us
    /public_html/sites/client2.markyoung.us

    /public_html/bizness (another base code of a second multisite reached with http://bizness.us) and 2 associated subsites
    /public_html/bizness/sites/client1.bizness.us
    /public_html/bizness/sites/client2.bizness.us

    I would like to use git, but not sure if I set up repository for markyoung.us which will encompass additional subsites and another for bizness.us which will also encompass those subsites, or if each subsite needs a repository.

    What is, in your opinion, the most efficient git workflow to manage these sites?

    Thanks!

    1. Hey Mark,

      Thank you for the comment. Based on what you described, I recommend having a separate git repo for each project/client/sub site. So, in your case you would have four repos:

      1. client1.markyoung
      2. client2.markyoung
      3. client1.bizness
      4. client2.bizness

      On your server create a directory for each one like /home/git/client1.markyoung.git, /home/git/client2.markyoung.git, and so on. You should then be able to follow the rest of the steps in this article as normal to setup each repo.

      1. Thanks Mike,
        I will get working on this. A git workflow seems like the best workflow to use.
        If the configuration works out, I will let you know.
        Take care

  2. Ok, after a test I can confirm that yes, I can change GIT_WORK_TREE.

    Now I ask: Is there any way to do this push on GitHub?

    I tried to switch to production/master in my GitHub and hit “Update from master” button. Theoretically it synchronizes but did not send the content to GIT_WORK_TREE.

  3. I just setup my first “real” git workflow using my MacBook and AWS with EC2 and CodeCommit. I was pretty lost and made several failed time consuming attempts until I found your post. Thanks for the concise easy to follow guide.

  4. Thank you for this! Helped me a bunch, I’ve been using third party software to deploy, but due to firewall reasons on new server I cannot do this anymore without compromising the security – this is perfect!

    Cheers

    Dan

  5. This setup is amazing!

    Just one fix on “Notes”, “Local server”, 2nd line:
    $ git remote add production ssh://staging.website.com/home/git/project_name.staging.git

    Correct:
    $ git remote add staging ssh://staging.website.com/home/git/project_name.staging.git

  6. Very nice and helpful explanation!

    What if I would have my repo hosted on github.com?
    Should I clone this repo instead of doing a git init? Or do a git init and add a new remote locally?

    Thanks.

  7. Hi Mike i try to make this working with gitlab can you please help my i want when i push a change to gitlab automaticly change and my server files.

  8. @ point 2 of “Local Server” paragraph: shouldn’t command

    git remote add production ssh://staging.website.com/home/git/project_name.staging.git

    be read as

    git remote add staging ssh://staging.website.com/home/git/project_name.staging.git

    ? Typing error?

  9. This is fantastic! Very helpful. I have a couple of questions:

    * Why do you initialise the git repository outside of the www folder? Is this a security concern or something else? If it is security, could you please explain what the concern is?

    * I currently use bitbucket to store my code, track issues/bugs and i put documentation in the wiki, can I continue to do this by simply adding a remote to the .git/config? For example, I currently have

    git
    [remote "origin"]
    url = https://<>@bitbucket.org/<>
    fetch = +refs/heads/*:refs/remotes/origin/*

    Is it as simple as adding
    git
    ...
    [remote "production"]
    url = ssh://username@website.com/home/git/project_name.git
    fetch = +refs/heads/*:refs/remotes/production/*
    [remote "staging"]
    url = ssh://username@website.com/home/git/project_name_staging.git
    fetch = +refs/heads/*:refs/remotes/staging/*
    ...

    * Lastly, I use submodules in my projects quite a lot. Is it as easy as writing git submodule update --init into the post-receive file?

  10. I am trying to implement this and it is works for the most part. My problem is that some of my projects contain sub-modules. I have tried adding
    GIT_WORK_TREE=~/myDirectory git submodule update –init

    but I get the following error:
    fatal: /usr/local/git/libexec/git-core/git-submodule cannot be used without a working tree.

    DO you have any ideas on how to rectify this?

  11. Hi Mike,

    I’m new to Git, tried to use your workflow in my server but doesn’t work for me. I setup my server and my local git as your guide but something I do wrong or don’t understand.

    The checkout -f of post-receive file doesn’t do nothing on my www folder.
    When I execute “git push brain master” (brain is my git remote definition) I get an
    “Everything up-to-date”.. but nothing more
    No files copied over my empty /var/www/vhosts/domain.tld/httpdocs

    This folder must be initialized with git too? Must the webfiles uploaded previously over FTP?
    Trying to understand how Git works 🙂

    Thanks

    1. I answer myself… was a permission issue.
      Git user account doesn’t have permission to write to web root folder.

      I suggest after do manual push, login into server using git user account and try to execute sh hooks/post-receive to check if this account have enough permissions to update folder.
      When I try to execute manually post-receive script I got a permission denied trying to write files to folder. What I do is add git user account to apache group and finally works as expected.

Leave a Reply

Your email address will not be published. Required fields are marked *

×Mike Everhart

Need Some More Help? Let's Talk!

I'd love to work with you! Fill out the form below to schedule a free consultation to discuss your needs and how I can help.

Need More Help?