Home ⋅ Peter Nelson

Using Git to auto-publish a website to Windows Server 2008

by peterdn 4. November 2011 10:07

(Updated July 15 2012.  much of the information presented here is now obsolete -- check out my updated guide.)

This is a quick and dirty guide.  YMMV.

I was motivated by the following:

  1. I want to work on and publish my website from several different machines, running different operating systems, with a minimum of fuss.
  2. After saving any changes to my central repository, I ideally want to see those changes with only a refresh of my browser.
  3. FTP publishing is generally clunky as hell.
  4. I’d done a similar thing before for a client, using SVN, and it worked well.
  5. Git is great.
  6. Whilst GitHub is fantastic, it costs money for private repositories and I would rather not pay for something when I can use my own VPS.
  7. Auto-publishing to my web server would be more difficult if my code was hosted on GitHub, anyway.

Setting up a Git server on Windows

This part is heavily inspired by the excellent and detailed guide by Tim Davis.  Since writing, some of his steps have changed and/or are unnecessary and/or I chose to do them in a slightly different way.  If you run into any problems with any of the steps listed here, please check out his guide for some solutions to common issues.  Here follows a brief summary of what I did:

  1. Download and install COPSSH to C:\ssh.

  2. Create a new Windows user (optional; you can enable SSH access for an existing user if you prefer).

  3. Open COPSSH Control Panel –> Users –> Add.  Enter details for the user account <user> you want to enable SSH access for.  Choose Linux shell + Sftp.  I chose to allow both password and public key authentication.

  4. Add a new inbound rule to the Windows Firewall to allow SSH traffic in (port 22, unless you changed it).

  5. Add your public key to C:\ssh\home\<user>\.ssh\authorized_keys (create if needed).  If you need to generate a public/private key pair, use PuTTYgen.

  6. Test that everything is working so far by attempting to connect from another machine.

  7. Download and install msysgit (at time of writing, Git- is the file you want) to C:\git.

  8. Open C:\ssh\home\<user>\.bashrc and add the following to the end of the file:

    export PATH=$PATH:/cygdrive/c/git/bin
  9. Copy git-receive-pack.exe and git-upload-pack.exe from C:\git\libexec\git-core to C:\git\bin.

  10. Verify that everything works by initialising a bare repository, cloning it, and pushing some changes.  If you have a repository in C:\ssh\home\<user>\test.git, it can be accessed via ssh://<user>@<hostname>/ssh/home/<user>/test.git (TODO: find out why it’s rooted at /ssh).  The following command-line session is fairly representative of success:

    peterdn@ubuntu:~$ ssh
    Last login: Thu Nov  3 21:45:58 2011 from
    peterdn@WIN-53SSN6PLF6F ~
    $ mkdir test.git
    peterdn@WIN-53SSN6PLF6F ~
    $ git init --bare test.git/
    Initialized empty Git repository in C:/ssh/home/peterdn/test.git/
    peterdn@WIN-53SSN6PLF6F ~
    $ exit
    Connection to closed.
    peterdn@ubuntu:~$ git clone ssh://peterdn@ test
    Initialized empty Git repository in /home/peterdn/test/.git/
    warning: You appear to have cloned an empty repository.
    peterdn@ubuntu:~$ cd test
    peterdn@ubuntu:~/test$ echo hello > hello.txt
    peterdn@ubuntu:~/test$ git add hello.txt
    peterdn@ubuntu:~/test$ git commit -m "initial commit"
    [master (root-commit) 64f843d] initial commit
     1 files changed, 1 insertions(+), 0 deletions(-)
     create mode 100644 hello.txt
    peterdn@ubuntu:~/test$ git push origin master
    Counting objects: 3, done.
    Writing objects: 100% (3/3), 219 bytes, done.
    Total 3 (delta 0), reused 0 (delta 0)
    To ssh://peterdn@
     * [new branch]      master –> master

Setting up a repository for auto-publish

Now that we have our Git server up and running, we can begin to invoke the black magic required to implement auto-publishing.  This part of the guide will be purely by-example; your setup with undoubtedly differ from mine, but hopefully the principles remain the same.  My desired setup looks like so:

  • Git repository is located in C:\inetpub\git\mysite.git.
  • Website is located in C:\inetpub\wwwroot\mysite.peterdn.com.  This is a clone of the above repository.

Note: for some reason that I’m yet to fathom, my git*.exe binaries live in a different virtual directory environment to the shell, and of course, Windows.  For example, what my shell thinks is /home/peterdn, git.exe thinks is /c/ssh/home/peterdn.  This has turned out to bite me a couple of times, but if you’re aware of the problem, it might make things easier to diagnose.


  1. In C:\ssh\home\<user>, create a symbolic link to C:\inetpub\git, using the following command:

    mklink.exe /D git C:\inetpub\git
  2. Similarly, create a symbolic link to C:\inetpub\wwwroot.

  3. Make sure that <user> has appropriate permissions for C:\inetpub\git\mysite.git and C:\inetpub\wwwroot\mysite.peterdn.com.

  4. Due to the bizarre path problem mentioned above, and the fact that I’d cloned <website> from within cmd.exe, my remote config currently looks like:

    $ git remote -v
    origin  C:\inetpub\git\mysite.git (fetch)
    origin  C:\inetpub\git\mysite.git (push)

    This will make things unhappy if we attempt to pull from origin in the Cygwin environment.  Therefore, I added another remote:

    git remote add local /c/ssh/home/peterdn/git/mysite.git
  5. Now we add the hook that will automatically pull changes whenever the main repository receives changes.  Add the following to C:\inetpub\git\mysite.git\hooks\post-receive:

    unset GIT_DIR
    cd /c/ssh/home/peterdn/wwwroot/mysite.peterdn.com
    git pull local master
  6. Verify that it works by pushing some changes.

Future Improvements

The results of the above are sufficient for the moment.  However, there are a few things that I would consider changing or adding in the future:

  1. Remove the requirement for the /ssh/home/<user>/git component from the URL, as it looks messy and is just generally bad form.
  2. Sort out issues with different virtual directory structures.
  3. Have a separate website for testing out changes without having to mess with the live site.  I assume this is simple as having separate “stable” and “current” branches, and pulling from these to the appropriate places.  But I could be wrong.
  4. Remove the .git directory from my web root.  I believe this is possible using a detached work tree.
  5. GitHub-style web interface for managing my repositories.

Tags: , , ,