A Git workflow for single developers

Published on and tagged with git  workflow

So far I used Subversion as version control system and my usual workflow is quite simple:

IDE => Subversion repository => live web application

I develop on my local machine, commit the code to the Subversion repository on the server, and finally export the code from the repository to the live application. That works fine.

However, as I wasn’t able to connect to Subversion repositories from Netbeans (the IDE I currently use for Ruby-based stuff) for whatever reasons, I had to look for a different solution. I could learn to use Subversion from the command line, or I could use this opportunity to dive a bit more into Git. And as you can guess from the subject of this article, I have chosen the second option: Git.

After learning the basics, I looked around to see how others use Git and I found a web-focused Git workflow by Joe Maller, which I used as a basis for my own workflow. The workflow is:

IDE => local Git repository => remote bare Git repository => Git repository with the live web application

As you can see, it’s a bit more complex than the previous workflow, as it involves three repositories instead of one.

I still develop on the local machine, however, now I commit to a local Git repository. From this local repository I push my changes to the remote bare Git repository (“bare” meaning the repository doesn’t have a working directory). This repository then “notifies” the Git repository with the live application about the changes, so that this repository can pull the changes from the bare Git repository (technically, this is incorrect, as you will see later).

Ok, now let’s have a look at how such a workflow is set up.

The first step is to set up the local repository:

$ mkdir -p projects/example
$ cd projects/example
$ git init
// create some files
$ git add .
$ git commit -m "Initial commit"

For the next step we have to switch to the server, and set up the bare repository:

$ mkdir -p git/example.git
$ cd git/example.git
$ git --bare init

To be able to push data to this bare repository, we have to tell our local repository about this remote repository. We do this with the following command (on the local machine):

$ git remote add exampleserver ssh://exampleuser@example.com/~/git/example.git

If everything went well, we can now push the data to the server with the following command:

$ git push exampleserver master

Tip: if you want to avoid typing the SSH password every time you push data, create a public/private key set as described by Casper Fabricius.

The next step is to create the repository which will contain the live application. For this purpose we switch again to the server. This time, we create a “normal” Git repository, because the working directory will contain the application. As this repository will pull the data from the bare repository, we have to add that bare repository as a “remote” repository.

$ cd ~
$ mkdir example.org
$ cd example.org
$ git init
$ git remote add bare ~/git/example.git

Note: make sure your document root doesn’t point to “example.org” but to a subdirectory!

The final step is to define the “post-update” hook script on the bare repository, which is called when the bare repository receives data. To enable the “post-update” hook, we have to either make hooks/post-update executable or rename hooks/post-update.sample to hooks/post-update, depending on the Git version. In this script, we simply change to the folder of the live application, and start the pull operation:

#!/bin/sh
cd $HOME/example.org
unset GIT_DIR
git pull bare master

exec git-update-server-info

Now, with everything set up, we can deploy the application from the local machine with a single command:

$ git push exampleserver master

I hope I could give you an overview of how a Git workflow for a single developer could look like. Feedback is welcome!

30 comments baked

  • Nate Todd

    Hmm, I fail to see the necessity of the bare remote repo if all you are doing is hooking it to update the live repo. Since all git repos contain full history, nothing is gained and the 2 will always be identical, so what’s the point?

    I would suggest not hooking the bare repo, but treating it as a remote copy of your repo. This would be the equivalent of a service like github. With your setup, you would have to make sure all your local commits are correct and you have reached a solid release before pushing up. This does 2 things wrong. First, you only have 1 copy of your data. Something goes wrong with your drive and you’re shot (you could back the repo up offsite i guess). Second, this prevents easy collaboration with others in the future, or (more importantly) the use of more than 1 development machine. I use 3. It’s possible to do without a common remote repo, but not much fun.

    My suggestion is to add both the bare repo and the live server (or several: testing, staging, production) as remotes and remove the hook. This way you can commit all day to your local repo, run “git push backupServer master” to merge your changes with the central repo whenever, and when you are ready, run “git push stagingServer master” to make those changes live.

  • cakebaker

    @Nate: Thanks for your comment!

    The reason there is a bare remote repo is because it is “discouraged” to do a push into a repo with a working directory: “A quick rule of thumb is to never push into a repository that has a work tree attached to it, until you know what you are doing.” (GitFaq). And as I’m a Git newbie I thought to follow this advice ;-)

    Yes, you are right, there is only one copy of the data. I think it depends on your project and/or your development style, whether this is ok. If you have, for example, a fast-paced project with more than one release per day, then you can ignore this risk in my opinion.

    Yes, this workflow is not meant for collaboration with others, for this purpose you would need a different setup. However, I think you can use it easily with different development machines: set up a local repository on each machine, and push to/pull from the bare remote repo.

    I will definitely follow your suggestion if I have a more complex setup :)

  • Nate Todd

    I have had no problems with pushing to a working copy repo, but that makes sense.

    Yes, you can use the bare repo as the central repo for your multiple dev machines, but whenever you push to the repo, it get’s pulled out to the live server. This is very likely not what you want to happen when you just need to sync an in-progress branch to your laptop.

    The nice thing about git is that you get the flexibility to handle your setup however you want, so I’m not sure either way is the “right way”.

  • Nate Todd

    I have had no problems with pushing to a working copy repo, but that makes sense.

    Yes, you can use the bare repo as the central repo for your multiple dev machines, but whenever you push to the repo, it get’s pulled out to the live server. This is very likely not what you want to happen when you just need to sync an in-progress branch to your laptop.

    The nice thing about git is that you get the flexibility to handle your setup however you want, so I’m not sure either way is the “right way”.

    PS. Having some openid issues with submitting comments.

  • cakebaker

    @Nate: Yes, you are right, this could lead to unwanted results ;-)

    I agree with you, thanks to the flexibility of git you can easily tailor the setup to the needs of the respective projects, and so the “right way” probably doesn’t exist.

    What OpenID issue did you encounter?

  • Frank

    Thanks for the tutorial! I tried installing gitosis but had a few problems with that but this solution works very well cheers.

  • Frank

    One question actually, if you make changes to the live files on the server do you need to commit and push these changes to the bare repository or how should that work?

  • cakebaker

    @Frank: Thanks for your comments!

    Yes, you commit the changes on the “live” repository and then you push them to the bare repository (you could automate the push with an additional hook script as shown in the article by Joe Maller). However, editing on the server should be the exception imho ;-)

  • Eoin Bailey

    Thanks for this. My plan was to setup exactly what you have here, you’ve cut down on some work for me, appreciate it!

  • cakebaker

    @Eoin: You are welcome!

  • othmane ouahbi

    that certainly works for one single dev.

    though, I won’t name the bare repo “bare” to avoid confusion ( maybe hub? central? or something meaningful ), but again this is only for a single dev so who cares.

    hmm, are you on dreamhost ?

    the reason I ask is that I have a domain I want to use to host repos for the internal team, and they don’t necessarly have ssh access.
    so webdav wasn’t a choice. ended up working but since repo folder is no longer owned by the shell user, I can’t chmod +x post-update and thus the automation isn’t working.
    I’ll see if I can install gitosis :/

  • cakebaker

    @othmane: Thanks for your comment!

    Yes, I’m using dreamhost. Though I don’t have any experience with webdav and multi-dev Git setups… Stupid question: couldn’t you just give everyone in your team ssh access, and the problem would disappear?

  • Baz L

    @Nate: Yep, I agree with you. I usually have a “live” branch on my local repo, where I merge changes and do other pre-launch processes. Then I push to the live repo.

  • omarish

    I love it. Thanks!

  • cakebaker

    @omarish: You are welcome!

  • Max

    Well… I have to say that without this tutorial, git would’ve taken me another 10 or 12 hours to figure out. Thanks for the input — it worked fine… eventually.

    After having gone through that ordeal I’m convinced that git just isn’t suited to individual development or small teams.

  • cakebaker

    @Max: Thanks for your comment!

    What I described here is only one possible workflow to give you an idea of how such a workflow could look like. You can find other workflows in the Pro Git book. In the end you have to find the workflow that fits to you/your team.

    Personally, I can only recommend to use Git, even if you are a single developer. And I can’t imagine to go back to Subversion.

    Anyway, give it a try, and don’t get discouraged by this article ;-)

  • primeminister

    When I loose my local repo I can always make a new one and pull from the bare repo, right? So I never loose code if the bare remains intact?

  • cakebaker

    @primeminister: Yep, you can always clone the bare repo. And yes, you won’t lose any code as long as the bare repo remains intact (though you will lose all changes not pushed to the server if your local machine crashes).

  • Very simple source control for personal projectsProgramming – It's a way of life | Programming - It's a way of life

    […] quick note… Daniel Hofstetter posted in his blog yesterday, an article about another single-developer solution if you wish to avoid Subversion for whatever reason (his being that he couldn’t get Subversion and NetBeans to interact, a problem I […]

  • Searching for a git Workflow « Mark Alan Evans
  • huynguyen

    Hi ,
    I have an issues, help me
    I follow instruction and get error

    Workflow: Client —> [ [local repo] , [ bare repo]] <— Web server
    user: git

    – Local and Bare repo on the same server
    1. mkdir -p /git/{store.git,bare_store}
    2. cd /git/store.git
    3. git init
    4. touch README
    5. git add .
    6. git commit -m "First commit"

    7. cd /git/bare_store.git
    8. git init –bare
    9. git remote add origin git@github.domain.com:/git/bare_store.git/
    10. git push origin master
    —- Show an errors —-
    git@github.domai.com's password:
    error: src refspec master does not match any.
    error: failed to push some refs to 'git@github.domain.com:/git/bare_store.git/'

    How can I fix it.
    Thanks

  • cakebaker

    @huynguyen: I think you get this error because there was no commit in your local bare_store.git repository yet. However, I don’t understand step 9. Is that a third repository on another server?

  • huynguyen

    Many thanks for your reply
    My workflow was:

    Coder (local repo) —> [Git server [Main repo] and [ bare repo for web project]] <—- Web server listen on bare repo.

    I have 02 server (one for Main repo and bare repo and another web server for testing)

  • cakebaker

    @huynguyen: Hm, I don’t think this setup will work because the bare repo doesn’t have a working directory, all it contains is the content that’s in the “.git” folder in a “normal” git repository. So there is nothing to serve for the web server.

  • huynguyen

    Hi Cakebaker,

    Could you help me an workflow for my case?
    I an newbie on Git.

  • cakebaker

    @huynguyen: I would start with a simple setup, like a local repo and one on the server. And once you got some experience with Git, adapt it to the specific needs of your projects, if necessary.

  • Ian

    I have been using this set up on a number of projects for a while now and I think it’s great.

    The only problem I have run into is if there’s a conflict on the hook pull from bare to live it doesn’t update and it doesn’t feedback to me, as far as what I see the push to bare went fine.

    I know in an ideal world there shouldn’t be conflicts on the live and it’s the first time it’s happened to me but I was left scratching my head wondering why my live site wasn’t updating!

  • Frank

    @ian I also enjoy using this workflow and have come across the same issue.

    You can get around this by forcing the live site to reset instead of pull but this may not be the desired behaviour if you are altering the git repo on the server directly.

    e.g. using something like:
    git fetch –all
    git reset –hard bare/master

    instead of:
    git pull bare master

  • Ian

    In the end I pimped up the post-update hook to email me the git log and commit message(s) if there’s a problem, as well as a few other useful tasks like clearing my websites and cdn caches

© daniel hofstetter. Licensed under a Creative Commons License