Configuring Gitolite with NAS and NTFS

Ok so the task was simple. Add Gitolite to my RaspberryPi, to enable me to admin a growing number of git repos. To begin with I had a git user and used absolute paths to the repo’s

git clone git@home:/opt/data/git/repo

One of the many problems with this was that when I wanted to share my repos with folk I would have to: cat newkey.pub >> /home/git/.ssh/authorized_keys - not an ideal solution. (The other reasons include, exposing my repo storage dir and absolutely no control of R / W access into my repos, let alone http or git protocol access).

Installing giotlite itself is a fairly easy thing to do. There’re a couple of commands (detailed on the gitolite website) that I needed to run and hey presto I was up and running! There was one small problem however, I had created my “git" user in the usual /home/ and I only had a 4GB card running the Pi - not really enough room for my repos.

Looking at the gitolite website it seemed possible to move the “repositories” dir somewhere else and just symlink to the new path - great! Well, not quite. Moving the repos and the symlink back was all good but during the move I hit a warning:

mv: cannot create symbolic link `/opt/data/repositories/gitolite-admin.git/hooks/update': Operation not supported

Trouble ahead. Apparently gitolite uses some custom git hooks to admin the repos and normally they’re symlinked from the main gitolite src.

After a quick look around the internet I realised my error. Because I was trying to move the repos on to a NAS drive which was using NTFS, symlinks wouldn’t be possible in Linux. (Apparently there’s a way to do this in Windows - but that doesn’t help me really).

Not only that but when I tried to create a new repo via the gitolite-admin conf I was shown a similar message when I pushed:

remote: Initialized empty Git repository in /opt/data/repositories/Tester.git/
remote: FATAL: could not symlink /home/git/.gitolite/hooks/common/update to Tester.git/hooks
remote:  at /home/git/gitolite/src/lib/Gitolite/Conf/Store.pm line 372

What to do?

I should have re-formatted my drive in something a little more symlink friendly. I still will in the future. But for the moment I wanted to see what I could put in place. I began by manually copying the symlinked hook(s). The regular repos only need the Gitolite update hook. But that wasn’t going to be very sustainable.

To get this to work I needed to adjust Gitolte slightly to work for me. Shouldn’t be too much work, I knew a littler perl and I’ve had a spare hour. Lets go!

I knew that somewhere at the heart of the gitolite source there’ll be a symlink that would add the hooks to the newly created repos. In theory all I needed to do was to replace the symlink with a copy and it’d be done.

Time to re-write gitolite (kinda)

From a quick look at my setup the git user had a single file, another symlink, in the bin dir. This symlinked to gitolite/src/gitolite, which is a link back to the originally cloned gitolite repo. That’s encouraging, if it’s just linking back to the repo then there’s nothing to stop me just changing some of the source to suit my needs.

I took a look at the source on Github (much easier than vim on a RaspberryPi) and located the lines I was looking for (the error above gave me a bit of a hint) gitolite/src/lib/Gitolite/Conf/Store.pm 178 - 200, which defines the new_repo function. In here another function ln_sf is being used on the new repo with the hooks - I bet that’s where the symlinks are done!

It took me a bit more digging but I found the function in gitolite/src/lib/Gitolite/Common.pm. Here a number of non-gitolite functions were defined and it was here that I could add my new copy function. After a quick google I stumbled upon File::Copy which does the job, here’s my implementation:

    sub cp {
        trace( 3, @_ );
        my ( $srcdir, $glob, $dstdir ) = @_;
        for my $hook ( glob("$srcdir/$glob") ) {
                $hook =~ s/$srcdir\///;
                unlink "$dstdir/$hook";
                copy "$srcdir/$hook", "$dstdir/$hook" or croak "could not copy $srcdir/$hook to $dstdir\n";
        }
    }

I just took the original ln_sf function and simply replaced the symlink function with my copy function and updated the croak message - can’t have unhelpful logging.

After making the changes I needed to test it (really I should have written some tests for this, so...) I created a test repo in the freshly pulled gitolite-admin and pushed.. fingerscrossed … success! Whereas before I was unable to push because of a FATAL inability to create the symlink, I could now push fine, using the new copy implementation.

All in all

It wasn’t too bad adjusting gitolite to suit my setup. Ideally this could be automatically done with some checks on the filesystem type to determine whether or not to use a symlink. If I feel ambitious in the future, and sitaramc willing, I might event submit a pull request.

First appeared on Trusty Interior, last update 26 Nov 2023