4.8 Git on the Server - Gitolite

Gitolite

Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was created to help with those requirements.

Gitolite allows you to specify permissions not just by repository (like Gitosis does), but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others.

Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind (various Linux flavours, and Solaris 10, have been tested), with git, perl, and an openssh compatible ssh server installed. In the examples below, we will use the gitolite account on a host called gitserver.

Curiously, Gitolite is installed by running a script on the workstation, so your workstation must have a bash shell available. Even the bash that comes with msysgit will do, in case you're wondering.

You start by obtaining public key based access to your server, so that you can log in from your workstation to the server without getting a password prompt. The following method works on Linux; for other workstation OSs you may have to do this manually. We assume you already had a key pair generated using ssh-keygen.

$ ssh-copy-id -i ~/.ssh/id_rsa gitolite@gitserver

This will ask you for the password to the gitolite account, and then set up public key access. This is essential for the install script, so check to make sure you can run a command without getting a password prompt:

$ ssh gitolite@gitserver pwd
/home/gitolite

Next, you clone Gitolite from the project's main site and run the "easy install" script (the third argument is your name as you would like it to appear in the resulting gitolite-admin repository):

And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called gitolite-admin in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing (just like Gitosis).

[By the way, upgrading gitolite is also done the same way. Also, if you're interested, run the script without any arguments to get a usage message.]

That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for something!)

While the default, quick, install works for most people, there are some ways to customise the install if you need to. Firstly, there are two other branches that you may be interested in installing, instead of "master". The "wildrepos" branch allows you to specify repositories by wildcards (regular expressions) in the configuration file; an extremely powerful feature that we will not be covering in this article. And if your server side git is older than 1.5.6 or so, you should use the "oldgits" branch.

Finally, if you omit the -q argument, you get a "verbose" mode install -- detailed information on what the install is doing at each step. The verbose mode also allows you to change certain server-side parameters, such as the location of the actual repositories, by editing an "rc" file that the server uses. This "rc" file is liberally commented so you should be able to make any changes you need quite easily, save it, and continue.

Notice that "sitaram" (the last argument in the gl-easy-install command you gave earlier) has read-write permissions on the gitolite-admin repository as well as a public key file of the same name.

The config file syntax for Gitolite is quite different from Gitosis. Again, this is liberally documented in conf/example.conf, so we'll only mention some highlights here.

You can group users or repos for convenience. The group names are just like macros; when defining them, it doesn't even matter whether they are projects or users; that distinction is only made when you use the "macro".

You can control permissions at the "ref" level. In the following example, interns can only push the "int" branch. Engineers can push any branch whose name starts with "eng-", and tags that start with "rc" followed by a digit. And the admins can do anything (including rewind) to any ref.

The expression after the RW or RW+ is a regular expression (regex) that the refname (ref) being pushed is matched against. So we call it a "refex"! Of course, a refex can be far more powerful than shown here, so don't overdo it if you're not comfortable with perl regexes.

Also, as you probably guessed, Gitolite prefixes refs/heads/ as a syntactic convenience if the refex does not begin with refs/.

An important feature of the config file's syntax is that all the rules for a repository need not be in one place. You can keep all the common stuff together, like the rules for all oss_repos shown above, then add specific rules for specific cases later on, like so:

repo gitolite
RW+ = sitaram

That rule will just get added to the ruleset for the gitolite repository.

At this point you might be wondering how the access control rules are actually applied, so let's go over that briefly.

There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to any ref in the repository, then you have read (or write) access to the repository. This is the only access control that Gitosis had.

The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (W or +), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied.

As you can see, permissions can be one of R, RW, or RW+. There is another permission available: -, standing for "deny". This gives you a lot more power, at the expense of some complexity, because now fallthrough is not the only way for access to be denied, and so the order of the rules now matters!

Let us say, in the situation above, we want engineers to be able to rewind any branch except master and integ. Here's how:

Again, you simply follow the rules top down until you hit a match for your access mode, or a deny. Non-rewind push to master or integ is allowed by the first rule. A rewind push to those refs does not match the first rule, drops down to the second, and is therefore denied. Any push (rewind or non-rewind) to refs other than master or integ won't match the first two rules anyway, and the third rule allows it.

If that sounds complicated, you may want to play with it to increase your understanding. Also, most of the time you don't need "deny" rules anyway, so you can choose to just avoid them if you prefer.

We'll round off this discussion with a bunch of other features, all of which are described in great detail in the "faqs, tips, etc" document.

Gitolite logs all successful accesses. If you were somewhat relaxed about giving people rewind permissions (RW+) and some kid blew away "master", the log file is a life saver, in terms of easily and quickly finding the SHA that got hosed.

One extremely useful convenience feature in gitolite is support for git installed outside the normal $PATH (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the client-side git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set $GIT_PATH in the "rc" files. No client-side changes are required after that :-)

Another convenient feature is what happens when you try and just ssh to the server. Older versions of gitolite used to complain about the SSH_ORIGINAL_COMMAND environment variable being empty (see the ssh documentation if interested). Now Gitolite comes up with something like this:

For really large installations, you can delegate responsibility for groups of repositories to various people and have them manage those pieces independently. This reduces the load on the main admin, and makes him less of a bottleneck. This feature has its own documentation file in the doc/ directory.

Finally, Gitolite also has a feature called "personal branches" (or rather, "personal branch namespace") that can be very useful in a corporate environment.

A lot of code exchange in the git world happens by "please pull" requests. In a corporate environment, however, unauthenticated access is a no-no, and a developer workstation cannot do authentication, so you have to push to the central server and ask someone to pull from there.

This would normally cause the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin.

Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, refs/personal/<devname>/*), with full permissions for that dev only, and read access for everyone else. Just choose a verbose install and set the $PERSONAL variable in the "rc" file to refs/personal. That's all; it's pretty much fire and forget as far as the admin is concerned, even if there is constant churn in the project team composition.