Using Github to Manage Git Repositories (Part 1)

For a long time, I was content to manage git respoitories myself (you can see earlier articles in this blog to see how I accomplished that). More and more, however, I liked the idea of my remote repositories using a more client-server model. To that end, I created an account for myself on Github. If you want to have private repositories, there is a monthly cost.

New Repository

Creating a new repository is simple.

From your github dashboard, you create a new repository (click the "New Repository" button on the righthand panel). You are asked to complete a form:

  • Project Name: bnr
  • Description (optional): Big Nerd Ranch
  • Homepage URL: www.bignerdranch.com
  • Access: Only the people I specify (I want this to be a private repository)

After you complete this form and press the "Create Repository" button, you are presented with some helpful setup information, including the "identity" of your repository (e.g., git@github.com:fenoglma/bnr.git).

cd ~/Sites
mkdir bnr && cd bnr
git init
touch README
git add README
git commit -m "[create] initial repository contents"
git remote add origin git@github.com:fenoglma/bnr.git
git push -u origin master

And there you are. You have a brand new repository ready for more code!

Existing Repository

What if I already have a repository, though? (That is certainly true for Big Nerd Ranch, which has been an active project of mine for a couple years.) In that case, you change the current configuration of your git repository to point to the one on github.

cd ~/Sites/bnr
git remote add origin git@github.com:fenoglma/bnr.git
git push -u origin master

This is only the first step, of course. Before using github, I managed the repositories by adding remote entries to my configuration. I no longer need those. I want my local configuration (~/Sites/bnr/.git/config) to look like this now:

[core]
   repositoryformatversion = 0
   filemode = true
   bare = false
   logallrefupdates = true
   ignorecase = true
[remote "origin"]
   url = git@github.com:fenoglma/bnr.git
   fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
   remote = origin
   merge = refs/heads/master

The original config file would have had remote entries for "bignerdranch" and "bnrstaging". What I want to do next is update my git setup on those servers to use the github repository instead.

Remote Repositories

Previously, I set up git on both the staging and production servers for Big Nerd Ranch. What I can do now is just change the configuration file to use the github repository instead.

ssh fenoglma@www.bignerdranch.com
sudo su - www-data
cd bignerdranch.com
git remote add origin git@github.com:fenoglma/bnr.git
git pull

The last statement (git pull) will result in an error! Why? I have not yet let github know that this server can use the repository.

Deploy Key vs. SSH Public Key

Here's where things get a bit dicey. A deploy key is a way to have read-only access to exactly one repository. That would be the perfect solution, except for one detail: The framework that powers the Big Nerd Ranch web application is a separate repository. With an individual github account, the only solution is to add SSH Public Keys. First, I need to create that public key for the server where the repositories will be deployed.

ssh www.bignerdranch.com
sudo su - www-data
mkdir .ssh && cd .ssh
ssh-keygen -t dsa

Two files result: id_dsa (private key) and id_dsa.pub (public key). Back in github, go to Account Settings > SSH Public Keys and add the public key. Now, "git pull" works just fine.

Deploy an Application to the Remote Server

With Git prepared on both the local workstation and on the remote server, I can now deploy my application.

Defining a Remote Repository

In order to deploy an application from my development workstation to istarelworkshop.com, I need to configure the application repository on my workstation such that is can communicate with the repository on the production server.

Here is the current configuration for the Istarel Workshop application:

markf$ cd ~/Sites/iw
markf$ cat .git/config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true

Now I configure this repository with user and path information on the remote Git repository. (Note that the 1158 in the remote declaration is the port, for cases where ssh is not listening to its standard port 22.)

markf$ git remote add istarelworkshop 
       ssh://git@www.istarelworkshop.com:1158/var/git/iw
markf$ cat .git/config

If I look at the configuration for the local repository now, I see the definition for "istarelworkshop".

markf$ cat .git/config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
[remote "istarelworkshop"]
    url = ssh://git@www.istarelworkshop.com:1158/var/git/iw
    fetch = +refs/heads/*:refs/remotes/istarelworkshop/*

Cloning a Bare Repository

With the description of the remote repository done, I can now deploy the application.

markf$ git push istarelworkshop master

I am not done yet, though. The repositorities I set up on istarelworkshop.com are bare repositories; that is, they contain all the branches and history but are not a working repository. I need to clone that bare repository to the appropriate location on istarelworkshop.com. In an earlier article, I showed how to set up Apache2 virtual hosts. The first such host has its base directory at /var/www/istarelworkshop.com. I need to take the bare repository and clone it to that location.

markf$ ssh markf@www.istarelworkshop.com
iwuser$ cd /var/www
iwuser$ sudo git clone /var/git/iw istarelworkshop.com
iwuser$ sudo chown -R www-data:www-data istarelworkshop.com

Configure the Remote Application

Back when I set up the local repository, I made sure that certain key configuration files were not part of the repository. This is important because every server is different, and the key paths and identities used on my local workstation are quite different from the Ubuntu VPS I deployed. So, I need to create those configuration files on istarelworkshop.com.

iwuser$ sudo su - www-data
$ bash
www-data$ cd /var/www/istarelworkshop.com
www-data$ mkdir conf && cd conf
www-data$ vi ApplicationConstants.php

Listing: /var/www/istarelworkshop.com/conf/ApplicationConstants.php

<?php

# Fundamental Application Mode
define('IN_PRODUCTION', 1);             # 0 = Development, 1 = Production

# Application Directories
define('APPL_ROOT_DIR', '/');
define('APPL_RSRC_DIR', '/var/www/istarelworkshop.com/rsrc/');

# Istarel Workshop Frameworks Directory
if (! defined('FRAMEWORK_DIR'))
   define('FRAMEWORK_DIR', '/var/www/istarelworkshop.com/fw/');

# Supplemental Directories Required by Application Frameworks Classes
define('APPL_IMG_DIR', APPL_ROOT_DIR . 'img/');
define('APPL_LIB_DIR', APPL_ROOT_DIR . 'lib/');

# Define the Application Database attributes
define('DB_TYPE',       'pgsql');
define('DB_HOST',       'localhost');
define('DB_NAME',       'iw');
define('DB_USERNAME',   'iwdb');
define('DB_PASSWORD',   'secret');

?>

The application framework uses Apache's mod_rewrite system to power the Front Controller. I need to create an .htaccess file at the root of the repository to ensure that proper routing happens.

Listing: /var/www/istarelworkshop.com/.htaccess

RewriteEngine On
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

Install and Use Git Locally

Git is a powerful, flexible version control simple, one I find more intuitive than Subversion. Once again, I use MacPorts to install Git, and then I supply some global configuration settings.

markf$ sudo port install git-core
markf$ git config --global user.name "Mark Fenoglio"
markf$ git config --global user.email markf@istarelworkshop.com
markf$ git config --global color.diff auto
markf$ git config --global color.status auto
markf$ git config --global color.branch auto

The first two Git configuration commands set important defaults for identifying the author of changes to the repository. The last three commands define what colors to use when Git presents information in the console.

Create the Initial Git Repository

markf$ cd ~/Sites/iw
markf$ git init
Initialized empty Git repository in /Users/markf/Sites/iw/.git/

Ignoring Mac OS X Finder Detritus

One nuisance in Mac OS X when working with version control systems is (normally hidden) .DS_Store files created by the Finder. Git allows you to globally ignore files.

markf$ git config --global core.excludesfile ~/.gitignore
markf$ echo .DS_Store >> ~/.gitignore

Exclude Files from Version Control

In addition to being able to ignore certain files regardless of context, you can also ignore files for a specific Git repository. One common example of this is configuration files where the values of certain parameters will be different between development (local) and production (remote) environments.

markf$ cd ~/Sites/iw
markf$ vi .git/info/exclude
conf/ApplicationConstants.php
httpd.conf
install/conf/sh.conf
install/php/config.php

Initial Repository

Now that the appropriate files will not be part of the repository, I can create its initial version.

markf$ git add .
markf$ git commit -m "[install] initial repository"

The . in the first command is a wildcard that means add all non-ignored files to the current batch of changes. The -m switch on the commit command is the message to be included as the changes are committed to the repository.