Gitolite is a missing puzzle with git toolset. It was a great discovery because until I found it I had hard time to sort out access privileges to git repositories. It is simple solution with aesthetic interface and privileges. Also well tested in practice because is used by kernel.org. But devil is in the detail. Once a list of my repositories grow, access privileges complicate. And I have recently wasted well over an hour of my time which I had to spend figuring out how its access control system really works.

Permissions

In its principles it is enough to work with just a couple of basic permissions like R (read), RW (R + create/fastforward/push a ref) and RW+ (RW + rewind/delete a ref). The - (minus sign) removes an access.

There are some other other permissions like D/C/M which I was not interested in yet.

Gitolite Access Check Flow

The config file is parsed from top to bottom. But there is one more thing - there are two different flows: Flow #1 for read only operations (R - fetch, clone), and flow #2 for changes (RW+). Normally the read access does not respect deny rules. There is an option to change that but by default read only access discards all

1
2
3
4
5
repo testing
    RW+         = jxa
    -    master = aga
    RW+ dev/    = aga
    RW          = aga

In the example above user jxa has all access to all branches, including delete/rewind. User aga has all possible privileges to do anything with branches starting with dev/, for branches not starting with dev/, aga may create and fast forward, but not rewind nor delete. For the master read access (clone, pull) shall be granted (because of flow #1), but all other privileges revoked.

However it did not work with my testing procedures. See below how I test it.

Testing

I found it comfortable to test directly at the server. Gitolite documentation mentions gitolite access command. Here is its syntax:

   gitolite access [-q|-s] <repo> <user> <perm> <ref>

Instead of changing the gitolite.conf at remote git repository, commiting and pushing it to the git server - I choose to edit the file at the server, usually it is $HOME/.gitolite/conf/gitolite.conf. It needs to be compiled after any change by gitolite compile.

Unexpected Results

So I had to spend more time to figure out why it works differently than what was described above and is written in the documentation. Below we follow a scenario where user aga requests fetch/clone access to master branch.

git@aws4:~/.gitolite/conf$ gitolite access -s testing aga R master
legend:
    d => skipped deny rule due to ref unknown or 'any',
    r => skipped due to refex not matching,
    p => skipped due to perm (W, +, etc) not matching,
    D => explicitly denied,
    A => explicitly allowed,
    F => denied due to fallthru (no rules matched)

  D        gitolite.conf:3          -    master = aga

R refs/heads/master testing aga DENIED by refs/heads/master
git@aws4:~/.gitolite/conf$ 

According to our rules, aga should be granted fetch/clone to master, because flow #1 ignores deny rules and grants access in line number 5. So gitolite does not behave as described.

Let’s check another scenario, instead of naming a particular branch we may use word any to test an access to unspecified brach.

git@aws4:~/.gitolite/conf$ gitolite access -s testing aga R any
legend:
    d => skipped deny rule due to ref unknown or 'any',
    r => skipped due to refex not matching,
    p => skipped due to perm (W, +, etc) not matching,
    D => explicitly denied,
    A => explicitly allowed,
    F => denied due to fallthru (no rules matched)

  d        gitolite.conf:3          -    master = aga
  A        gitolite.conf:4          RW+ dev/    = aga

refs/heads/dev/
git@aws4:~/.gitolite/conf$ 

This time access tool explicitely skips the deny rule of line 3 and grants access based on the line 4, because as I presume, any means any branch, dev including. This kind of testing with any does not look useful at first glance. But it is actually useful, just follow my path and look what I have found:

git@aws4:~/.gitolite/conf$ less ~/bin/commands/access
[...]

Notes:
    [...]
    The 'any' ref is special -- it ignores deny rules, thus simulating
    gitolite's behaviour during the pre-git access check (see 'deny-rules'
    section in rules.html for details).

Why did they hide it here? Indeed it matches flow #1 definition - it is supposed to ignore all deny rules and all refexes.

Wrap Up

So actually the gitolite access utility works as designed but we have to test it twice: once for write access, and another one for read access using any as the branch name.

Another motion - the documentation is usually poor, check the source code by yourself.