Installing a Git Repository and Gitosis

Posted Thursday, 04 March 2010 at 03:18 by Andrew Liu
Tagged: python | Git | linux | fedora | web development
Read more blogs...

 

OS: Fedora 10
 

Having installed Git once as a test on an older version of Fedora, I decided to try a fully fledged installation of Git and Gitosis on a newly built server with a fresh installation of Fedora 10.  After all, I had to store all my code somewhere, and so do you!

I had read a lot of forums, and most tend to say the same thing, just in different ways, but I still needed to piece together information to make it happen.  Hopefully you will find this article useful and a good walkthrough.  The most useful link I found was this from Garry Dolley, so mine is a slight twist towards the Fedora community.

 

1. Get the Source

 

You know, its weird to mention it, but I never noticed, after using Git for a few months, that its homepage is http://www.git-scm.com.  Well, now you know!  I'm an 'install from source' kinda person, rather than relying on the magic of RPMs.  RPMs are great for your system files (think your Windows XP), but I'd rather explicity download and install a program (like choosing the advanced options when installing Java JRE on Windows), thus I'd prefer building from source and putting into directories that I know.

 

First thing to note is that everything is done here as the root user.  And my typical habit is to download all packages into the root home directory.

 

So the first item I needed was wget to download the files.  Thats easy enough.

 

$ yum install wget

 

The proceeded to download the file I needed.  "cd" to root's home directory and grab it.

 

$ cd  
$ wget http://www.kernel.org/pub/software/scn/git/git-1.6.1.tar.bz2
 

 

Decompress the file yields the directory git-1.6.1 (or whichever version you downloaded).

 

$ tar xvfj git-1.6.1.tar.bz2 $ ls git-1.6.1 

 

As this is the source of git, I then move it into /usr/local/src.  This is where I always store all my source files that I compile from source.

 

$ mv git-1.6.1 /usr/local/src  $ cd /usr/local/src/git-1.6.1 

 

 

2. Do the "configure / make / install" routine

 

Ok.  Now comes the standard installation procedure.  Or so you hope.  Trying to compile this yields some errors.  Those of you who have done this many times will know that a fresh installation of anything rarely has all the libraries in place.  Thank god for yum for making this easy!

 

$ yum install gzip
$ yum install zlib
$ yum install libcurl
$ yum install openssl
$ yum install openmake
$ yum install zlib-devel
$ yum install perl
$ yum install perl-ExtUtils-MakeMaker
$ yum install tcl

 

Some of these may already be installed from the default Fedora 10 installation.  I just went through them again just in case.  Why these packages?  I have no idea to be honest.  But you dont need to know either.  This was done from trial and error from the results of attempting to compile, and then figuring it out from the error messages that returned.  Again, doing this for the umpteenth time helps.  When you do the "configure/make/install" routine, and see errors, you will understand the error messages over time - work off the presumption that the software you are installing is not buggy - it is your installation that is buggy.

 

Now, you should be able to compile.  I always compile with a prefix of /usr/local/foo where foo is normally the same name as the original directory in /usr/local/src/foo.  So here I'll go into /usr/local/git-1.6.1.  Why?  Because keeping the name the same is probably what the authors intended.  Renaming it is very tempting, but after years of doing this I suggest against this because it simply leads to confusion when you revisit your server in 6 months time.  Keep the names the same, and you wont ever have to think what version you are using.  Instead, I use the symbolic link trick (I do this a lot).  At least symbolic links give you a historical trace of what links to where, and which is your default installation.

 

$ ./configure --prefix=/usr/local/git-1.6.1 
$ make
$ make install
$ ln -s /usr/local/git-1.6.1 /usr/local/git

 

 

3. Make it accessible

 

Once you've built any binary, it helps to put this into your path.  I tend to give the path to everyone, as the default, so I will edit /etc/profile to do this.  Insert the red text below.

 

$ vi /etc/profile  
...
# Git export
GIT_HOME=/usr/local/git
pathmunge $GIT_HOME/bin

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC
$ . /etc/profile

 

The red text needs to appear before the "export PATH..." statement so that it is set before the environment is set.  One of the good things of Fedora 10 (from the previous Fedora installations) is that you don't need to mess with the pathmunging aspect when a root user and when not a root user.  Now, all users get access to the /usr/local/sbin, /usr/sbin and /sbin directories, just in a different path order.  It was messy everytime for me to go and change those few lines!  Now you don't have to!

 

Ok once you have gotten that far, you can source your new /etc/profile file (". /etc/profile"), and viola! Git is available for everyone!

 

$ . /etc/profile 
$ which git /usr/local/git/bin/git

 

Hopefully you are starting to see why I install into these particular directories.  There is no reference to git-1.6.1 - this has been masked by the use of symbolic links.  Thus, I could potentially have 2 or 3 installations of git on the same machine, and no one (except those who should know) knows.  You may have a /usr/local/git-1.6.0, /usr/local/git-1.6.1 and /usr/local/git-1.5.0 to test against all versions, and all you need to do to change from one version to the next is to do is pathmunge the version you want into your path (in ~/.bashrc, your personal environment settings file).  And the rest of your users dont need to know.

 

4. Install Python

 

Ok.  Now for the fun part!  Gitosis is hosted at http://eagain.net/gitweb/?p=gitosis.git;a=summary, and the beautiful thing is that it uses Python, my personal favourite programming language.  So that means you will need Python on your system to run Gitosis.  Python can be found at http://www.python.org.

 

$ cd 
$ wget http://www.python.org/ftp/python/2.6.1/Python-2.6.1.tar.bz2
$ tar xvfj Python-2.6.1.tar.bz2
$ mv Python-2.6.1 /usr/local/src
$ cd /usr/local/src $ ./configure --prefix=/usr/local/Python-2.6.1
$ make
$ make install
$ cd /usr/local
$ ln -s
Python-2.6.1 Python

 

Again, all this for the same reasons as when installing Git.  This simply puts the source files into /usr/local/src/foo-x.y.z, the binaries into /usr/local/foo-x.y.z and symbolic linking it from /usr/local/foo to make it transparent.  Again, I edit /etc/profile to put this into my path explicitly.  Some people may wonder why all this configuration.  You could easily just install from source without using the "--prefix" option.  What this does is install the binaries into /usr/local/bin (rather than /usr/local/foo-x.y.z/bin).  However, what this also does is prevent you from running multiple versions of the same software.  You wouldn't be able to have git-1.5.0, git-1.6.0 and git-1.6.1 on the same system, as all their binaries would be sitting in /usr/local/bin!  Of course, no one would have multiple versions of git, but I would (and I do) have multiple versions of Python running to test my software against..... and thats good enough reason for me to install in the above directory structure.  Dont forget to source the new /etc/profile file to load your new environment, otherwise your current session / terminal will not have these new settings.  Only new sessions / terminals will inherit these new settings unless you explicity source them.

 

$ vi /etc/profile 
...
# Python
export PYTHON_HOME=/usr/local/Python
pathmunge $PYTHON_HOME/bin

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC
... $ . /etc/profile

 

Now we need to install a python module.  Python modules tend to be a bit more involved than the typical "compile/make/install" routine, but these days they tend to be streamlined into "eggs", or python packages that magically handle themselves.  They are distributed by a master module called setuptools, and can be found at http://pypi.python.org/pypi/setuptools.  Make sure that you download the correct version to suit your version of Python you have installed.  I used Python 2.6 here.

  

$ wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c9-py2.6.egg#md5=ca37b1ff16fa2ede6e19383e7b59245a

 

You can pretty much run the egg file like a shell script, and it will just install itself into the Python installation.

 

$ sh setuptools-0.6c9-py2.6.egg --prefix=/usr/local/Python-2.6.1 

 

 

5. Gitosis Time!

 

Now we have the necessary tools to install Gitosis.   Firstly, grab the source from the git web repository.

 

$ cd /usr/local/src 
$ git clone git://eagain.net/gitosis.git
Initialized empty Git repository in /usr/local/src/gitosis/.git/
remote: Counting objects: 603, done.
remote: Compressing objects: 100% (172/172), done.
remote: Total 603 (delta 425), reused 597 (delta 422)
Receiving objects: 100% (603/603), 92.87 KiB | 23 KiB/s, done.
Resolving deltas: 100% (425/425), done.

 

Gitosis is a very small project, and is very quick to pull from the internet, regardless of your internet connection.  This will typically take a few seconds to complete.  Once downloaded, we run the installation. 

 

$ cd gitosis 
$ python setup.py install

 

Gitosis uses an operating system user to log in to the Git repositories, and does this through SSL certificates.  The user is typically called 'git', and does not need a password (because the user logs in through certificate authentication), and requires shell access. A standard useradd command should do fine.

 

$ /usr/sbin/useradd git 

 

Now you will require an SSH certificate, so that you can actually log in and use this 'git' user.  I recommend not putting a password on this user, so that the only way of accessing the 'git' user is to use SSH certificates - a far more secure method of access.  Lets do this as the 'git' user, as the certificate is for him. 

 

When prompted for a file to save the key, just press enter to use the defaults (this is best).  You can set any passphrase you wish when prompted (remember this as you will need to know it later!)

 

$ su - git git
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/git/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/git/.ssh/id_rsa.
Your public key has been saved in /home/git/.ssh/id_rsa.pub.
The key fingerprint is: 59:a8:a8:0a:f7:b5:0e:eb:74:c4:66:cb:ac:03:84:da git@localhost
The key's randomart image is:
+--[ RSA 2048]----+
| |
| . |
| . . . |
|. . o . o |
|.o . * S |
|. E. * . |
|. o.o * |
|.o o.* . |
|. .=oo |
+-----------------+
git$ ls -al .ssh total 16
drwx------ 2 git git 4096 2009-02-11 14:52 .
drwx------ 5 git git 4096 2009-02-11 14:52 ..
-rw------- 1 git git 1743 2009-02-11 14:52 id_rsa
-rw-r--r-- 1 git git 393 2009-02-11 14:52 id_rsa.pub

 

Still as the 'git' user, we simply initialise the git repositories.

 

git$ gitosis-init < .ssh/id_rsa.pub
Initialized empty Git repository in /home/git/repositories/gitosis-admin.git/ 
Reinitialized existing Git repository in /home/git/repositories/gitosis-admin.git/

git$ ls -al
total 44
drwx------ 7 git git 4096 2009-02-11 14:57 .
drwxr-xr-x 4 root root 4096 2009-02-11 00:51 ..
-rw------- 1 git git 87 2009-02-11 14:56 .bash_history
-rw-r--r-- 1 git git 18 2008-12-15 22:04 .bash_logout
-rw-r--r-- 1 git git 176 2008-12-15 22:04 .bash_profile
-rw-r--r-- 1 git git 124 2008-12-15 22:04 .bashrc
drwxr-xr-x 2 git git 4096 2009-02-11 14:57 gitosis
lrwxrwxrwx 1 git git 53 2009-02-11 14:57 .gitosis.conf -> /home/git/repositories/gitosis-admin.git/gitosis.conf
drwxr-xr-x 2 git git 4096 2008-10-29 05:49 .gnome2
drwxr-xr-x 4 git git 4096 2008-11-20 06:11 .mozilla
drwxr-xr-x 3 git git 4096 2009-02-11 14:57 repositories
drwx------ 2 git git 4096 2009-02-11 14:57 .ssh

 

In particular, lets take a look at what repositories we start out with.

 

git$ ls -al repositories/ 
total 12
drwxr-xr-x 3 git git 4096 2009-02-11 14:57 .
drwx------ 7 git git 4096 2009-02-11 14:57 ..
drwxr-x--- 8 git git 4096 2009-02-11 14:57 gitosis-admin.git

 

What we have done is create a repository for our own gitosis!  This allows us to control who can access git, and from where they can access (if you so desire). 

 

To make sure git is working, lets actually take a working snapshot of this repository.  This is known in the git world as 'pulling' the repository, and is the very similar to 'checking out' an SVN repository (if you are familiar with SVN).

 

When prompted to confirm authenticity of the certificate and continue connecting, you need to type 'yes'.  This happens on when using a key on a server for the first time.  Answering 'yes' will hereon trust that key on that server, so you will never be asked again (given that key/server pair).

 

Of course, enter your passphrase when prompted as well.

 

git$ git clone 
git@localhost:gitosis-admin.git
Initialized empty Git repository in /home/git/gitosis-admin/.git/
The authenticity of host 'localhost (127.0.0.1)' can't be established.
RSA key fingerprint is f4:bf:85:05:81:6d:56:49:40:66:1e:5d:fa:90:d9:f7.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
Enter passphrase for key '/home/git/.ssh/id_rsa':
bash: gitosis-serve: command not found fatal:
The remote end hung up unexpectedly

 

Ouch! One final quirk to this is that, for some unknown reason, when using gitosis, the git user does not load /etc/profile.  That means that it will not be able to find our gitosis binaries (as shown above), which are installed in a 'non-standard' location.  Non-standard here means that it is not in /usr/local/bin (which we dont want).  So we explicity need to tell the 'git' user where to find it.  We already have it defined in our /etc/profile, so we just need to add the following to the 'git' user's .bashrc file.  This just means that gitosis will now load up a normal environment (as one would expect).

 

git$ vi .bashrc   
1 # .bashrc
2
3 . /etc/profile
4 # Source global definitions
5 if [ -f /etc/bashrc ]; then
6 . /etc/bashrc
7 fi
8
9 # User specific aliases and functions

 

Now if we clone a git repositoy, it will work!

 

git$ git clone git@localhost:gitosis-admin.git
Initialized empty Git repository in /home/git/gitosis-admin/.git/ 
Enter passphrase for key '/home/git/.ssh/id_rsa':
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 5 (delta 0)
Receiving objects: 100% (5/5), done.

 

 

6. Creating New Repositories

 

Before we go any further, we need to run the following line.  I don't know why this is the case (and we may have to do this for new repositories) but this post notes that the post-update hook isn't executable.  Sure enough, when we look at it, it isn't.  So lets fix that up pronto.  If we dont, then any 'git push' that you do never seems to show in the repository.  This made me scratch my head one long night....
 

git$ ls -al repositories/gitosis-admin.git/hooks/post-update
-rw-r--r-- 1 git git 69 2009-02-11 14:57 repositories/gitosis-admin.git/hooks/post-update
git$ chmod 755 repositories/gitosis-admin.git/hooks/post-update git$ ls -al repositories/gitosis-admin.git/hooks/post-update -rwxr-xr-x 1 git git 69 2009-02-11 14:57 repositories/gitosis-admin.git/hooks/post-update

 

Now, lets take a look at the gitosis-admin source code we checked out (or, in more gitty terms, we pulled out).

 

git$ ls -al gitosis-admin
total 20
drwxr-xr-x 4 git git 4096 2009-02-11 15:38 .
drwx------ 8 git git 4096 2009-02-11 15:49 ..
drwxrwxr-x 8 git git 4096 2009-02-11 15:38 .git
-rw-rw-r-- 1 git git 81 2009-02-11 15:38 gitosis.conf
drwxrwxr-x 2 git git 4096 2009-02-11 15:38 keydir

 

In particular, lets dissect the gitosis.conf, for the purposes of creating a new repository.  We only have one repository (gitosis-admin), and presumably you want to add more.  Our new repository will be called 'my-first-repo'.  Lets edit the gitosis.conf file, and then check that in (oops, I mean push it in).

 

git$ cd gitosis-admin
git$ vi gitosis.conf
1 [gitosis]
2
3 [group gitosis-admin]
4 writable = gitosis-admin
5 members = git@localhost
6
7 [group my-staff]
8 writable = my-first-repo
9 members = git@localhost git$ git commit -a -m "Allow my staff access to my-first-repo" [master]: created 0c6a685: "Allow my staff access to my-first-repo"
1 files changed, 3 insertions(+), 0 deletions(-)
git$ git push Enter passphrase for key '/home/git/.ssh/id_rsa':
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 381 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@localhost:gitosis-admin.git
7b3ddbe..0c6a685 master -> master
git$ cd

 

'git@localhost' is the name of public key that we used to initialise gitosis.  For now, seeing thats our only user with gitosis access, we'll keep going aloing with using 'git@localhost'. Remember, 'git@localhost' doesn't necessarily refer to the user 'git' - that just happens to be the case here.  The name of the public key could equally be called 'foo@bar' and you could still be using the 'git' user, so long as the 'git' user had the equivalent private key in his .ssh setup.  But lets not worry about the SSH setup - thats another story!

 

 

The 'git push' command commits the changes into gitosis.  Now gitosis knows about a pending repository called 'my-first-repo'.  Note that we have only added access to a repository that doesn't exist yet.  Strangely (sort of), access needs to be granted to a repository, before it can be created.

 

 

The last command 'cd' just takes us back to the home directory of 'git'.

 

 

Lets create that repository we've been talking about now.  We cant create an empty repository, so to get around that, we'll just create a single text file, add the new file, commit the revision, then push the changes.

 

git$ mkdir my-first-repo
git$ cd my-first-repo
git$ git init
Initialized empty Git repository in /home/git/my-first-repo/.git/
git$ git remote add origin git@localhost:my-first-repo.git
git$ echo "Hello" > README.txt
git$ git add README.txt 
git$ git commit -a -m "Initial Revision"
[master (root-commit)]: created 6f567f9: "Initial Revision"
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 README.txt
git$ git push origin master:refs/heads/master
Enter passphrase for key '/home/git/.ssh/id_rsa':
Counting objects: 3, done.
Writing objects: 100% (3/3), 220 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@localhost:my-first-repo.git
* [new branch] master -> master
git$ cd ..

 

Done! The new repository now exists!

 

7 Adding Users

 

Now that we have repositories under way, you will want to give access to your staff (you have a big company of course).  Lets say 'alice' and 'bob' are your staff members, and you want them to take care of 'my-first-repo' now.

 

First thing we need is to get their public key.  Lets assume that their public keys are files called 'alice.pub' and 'bob.pub' respectively.  Lets add these keys to gitosis.  Note the .pub extension is required for gitosis to work.

 

git$ cd gitosis-admin
git$ cp ~/alice.pub keydir
git$ cp ~/bob.pub keydir
git$ git add keydir
git$ vi gitosis.conf
1 [gitosis]
2
3 [group gitosis-admin]
4 writable = gitosis-admin
5 members = git@localhost
6
7 [group my-staff]
8 writable = my-first-repo
9 members = alice bob
git$ git commit -a -m "alice/bob writable to my-first-repo"
[master]: created 17b3295: "alice/bob writable to my-first-repo"
1 files changed, 1 insertions(+), 1 deletions(-)
create mode 100644 keydir/alice.pub
create mode 100644 keydir/bob.pub
git$ git push Enter passphrase for key '/home/git/.ssh/id_rsa':
Counting objects: 8, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 430 bytes, done.
Total 5 (delta 1), reused 0 (delta 0)
To git@magneto:gitosis-admin.git
305600f..17b3295 master -> master

 

Doneski.  Alice and Bob can now clone the repository from wherever they are.
 

alice$ git clone git@SERVER_HOSTNAME:my-first-repo.git

 

or, even trickier (if access to the server via SSH is not on port 22)

 

alice> git clone ssh://git@SERVER_HOSTNAME:24/my-first-repo.git

 

So long as they have their private key with them, and the server is accessible over the network, this should give you enough flexibility to control who can access what, from wherever they are!

 

Except: (1, 2)
Traceback (most recent call last): File "/apps/case-users/webtop/case/case/case/model/content.py", line 577, in print_text text = template.render_unicode(**globs) File "/apps/case-users/webtop/lib/python2.6/site-packages/Mako-0.3.2-py2.6.egg/mako/template.py", line 198, in render_unicode as_unicode=True) File "/apps/case-users/webtop/lib/python2.6/site-packages/Mako-0.3.2-py2.6.egg/mako/runtime.py", line 403, in _render _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data)) File "/apps/case-users/webtop/lib/python2.6/site-packages/Mako-0.3.2-py2.6.egg/mako/runtime.py", line 434, in _render_context _exec_template(inherit, lclcontext, args=args, kwargs=kwargs) File "/apps/case-users/webtop/lib/python2.6/site-packages/Mako-0.3.2-py2.6.egg/mako/runtime.py", line 457, in _exec_template callable_(context, *args, **kwargs) File "memory:0x2ab30a60d250", line 67, in render_body File "/apps/case-users/webtop/case/case/xbits/helpers.py", line 421, in quote return urllib.quote(s) File "/usr/local/Python-2.6.5/lib/python2.6/urllib.py", line 1222, in quote res = map(safe_map.__getitem__, s) KeyError: u'\u2014'