Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'easy-install'

  • Loading branch information...
commit 2fdd7b34f5a4c3b61228da248b0bfbdb4d59eb02 2 parents af7fd0b + b3cab45
Sitaram Chamarty authored
116 README.mkd
@@ -3,19 +3,22 @@
> [IMPORTANT: There is now an "upgrade" document in the "doc" directory;
> please read if upgrading gitolite]
+> [Update 2009-10-10: apart from all the nifty new features, there's now an
+> "easy install" script in the src directory. Please see the INSTALL
+> document in the doc directory for details]
-Gitolite is the bare essentials of gitosis, with a completely different
-config file that allows (at last!) access control down to the branch level,
-including specifying who can and cannot *rewind* a given branch. It is
-released under GPL v2. See COPYING for details.
+Gitolite is a rewrite of gitosis, with a completely different config file that
+allows (at last!) access control down to the branch level, including
+specifying who can and cannot *rewind* a given branch.
In this document:
* why
- * what's gone
- * what's new
- * the workflow
+ * what's extra
+ * security
+ * contact and license
@@ -28,30 +31,15 @@ a typical $DAYJOB setting, there are some issues:
and be done
* often, "python-setuptools" isn't installed (and on a Solaris9 I was trying
to help remotely, we never did manage to install it eventually)
- * or you don't have root access, or the ability to add users
+ * you don't have root access, or the ability to add users (this is also true
+ for people who have just one userid on a hosting provider)
* the most requested feature (see "what's new?") had to be written anyway
-### what's gone
-While I was pondering the need to finally learn python[1] , I also realised
- * no one in $DAYJOB type environments will use or approve access methods
- that work without any authentication, so I didn't need gitweb/daemon
- support in the tool or in the config file.
- Update 2009-09-24: I don't use this feature but someone wanted it, so I
- added it... see the "faq, tips, etc" document for more
- * the idea that you admin it by pushing to a special repo is nice, but not
- really necessary because of how rarely these changes are made, especially
- considering how much code is involved in that piece
All of this pointed to a rewrite. In perl, naturally :-)
-### what's new
+### what's extra
-Per-branch permissions. You will not believe how often I am asked this at
+**Per-branch permissions**. You will not believe how often I am asked this at
$DAYJOB. This is almost the single reason I started *thinking* about rolling
my own gitosis in the first place.
@@ -61,50 +49,42 @@ deleting a branch (which is really just an extreme form of rewind). I needed
something in between allowing anyone to do it (the default) and disabling it
completely (`receive.denyNonFastForwards` or `receive.denyDeletes`).
-Take a look at the example config file in the repo to see how I do this. I
-copied the basic idea from `update-hook-example.txt` (it's one of the "howto"s
-that come with the git source tree). However, please note the difference in
-the size and complexity of the *operational code* between the update hook in
-that example, and in mine :-) The reason is in the next section.
-### the workflow
-In order to get per-branch access, you *must* use an update hook. However,
-that only gets invoked on a push; "read" access still has to be controlled
-right at the beginning, before git even enters the scene (just the way gitosis
-currently works).
+Here're **some more features**. All of them are documented in detail
+somewhere in the `doc/` subdirectory.
+ * simpler, yet far more powerful, config file syntax, including specifying
+ gitweb/daemon access. You'll need this power if you manage lots of users
+ + repos + combinations of access
+ * config file syntax gets checked upfront, and much more thoroughly
+ * if your requirements are still too complex, you can split up the config
+ file and delegate authority over parts of it
+ * more comprehensive logging [aka: management does not think "blame" is just
+ a synonym for "annotate" :-)]
+ * "personal namespace" prefix for each dev
+ * migration guide and simple converter for gitosis conf file
+ * "exclude" (or "deny" rights in the config file) -- this is the "rebel"
+ branch in the repository, and always will be ;-)
+### security
+Due to the environment in which this was created and the need it fills, I
+consider this a "security" program, albeit a very modest one. The code is
+very small and easily reviewable -- the 2 programs that actually control
+access when a user logs in total about 200 lines of code (about
+80 lines according to "sloccount").
+For the first person to find a security hole in it, defined as allowing a
+normal user (not the gitolite admin) to read a repo, or write/rewind a ref,
+that the config file says he shouldn't, and caused by a bug in *code* that is
+in the "master" branch, (not in the other branches, or the configuration file
+or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like
+djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize
-So: either split the access control into two config files, or have two
-completely different programs *both* parse the same one and pick what they
-want. Crap... I definitely don't want the hook doing any parsing, (and it
-would be nice if the auth-control program didn't have to either).
-So I changed the workflow completely:
- * all admin changes happen *on the server*, in a special directory that
- contains the config and the users' pubkeys. But there's no commit and
- push afterward
- * instead, after making changes, you "compile" the configuration. This
- refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the
- access list in a file for the other two pieces to use.
-The pre-parsed form is basically a huge perl variable. It's human readable
-too (never mind what the python guys say!)
-So the admin knows immediately if the config file had any problems, which is
-good. Also, the relatively complex parse code is not part of the actual
-access control points, which are:
- * the program that is run via `~/.ssh/authorized_keys` (I call it
- `gl-auth-command`, equivalent to `gitosis-serve`); this decides whether
- git should even be allowed to run (basic R/W/no access)
- * the update-hook on each repo, which decides the per-branch permissions
-### footnotes
-[1] I hate whitespace to mean anything significant except for text; this is a
-personal opinion *only*, so pythonistas please back off :-)
+### contact and license
-### contact
+Gitolite is released under GPL v2. See COPYING for details.
12 conf/example.gitolite.rc
@@ -5,7 +5,7 @@
# this file is meant to be pulled into a perl program using "do" or "require".
# You do NOT need to know perl to edit the paths; it should be fairly
-# self-explanatory
+# self-explanatory and easy to maintain perl syntax :-)
# --------------------------------------
@@ -89,6 +89,16 @@ $PERSONAL="";
# it fully qualified -- that is, starting with "refs/"
# --------------------------------------
+# if git on your server is on a standard path (that is
+# ssh git@server git --version
+# works), leave this setting as is. Otherwise, choose one of the
+# alternatives, or write your own
+# $GIT_PATH="/opt/bin/";
+# --------------------------------------
# per perl rules, this should be the last line in such a file:
135 doc/0-INSTALL.mkd
@@ -1,98 +1,75 @@
# installing gitolite
-### pre-requisites
+This document tells you how to install gitolite. After the install is done,
+you may want to see the "admin" document for adding users, repos, etc.
-If you managed to install git, you might already have what gitolite needs:
+There's an easy install script for Linux, and for other Unixes there's a
+slightly more manual process. Both are explained here.
- * git itself, the more recent the better
- * perl, typically installed with git, since git sort of needs it; any
- version that includes `Data::Dumper`[1] will do.
- * one user account on the server, with password access [2]
+In this document:
-A major objective is to allow use by people without root access, permissions
-to create other userids, etc. Even if you have root, please add a user just
-for gitolite and do all this from that user.
+ * easy install
+ * manual install
+ * other notes
+ * next steps
-### getting a tar file from a clone
+### easy install
+There is an easy install script that makes installing very easy for the common
+case. **This script is meant to be run on your workstation, not on the
+server!** It will take care of all the server side work, *and* get you
+"push-to-admin" too :-) In short, it does **everything**!
-You can clone the repo from github, then execute a make command to extract a
-tar file of the branch you want. Please use the make command, not a plain
-"git archive". The comments in the `Makefile` will explain why.
+ * you have a server to host gitolite
+ * git is installed on that server (and so is perl)
+ * you have a userid on that server
+ * you have ssh-pubkey (password-less) login to that userid
+ * (if you have only password access, run `ssh-keygen -t rsa` to create a
+ new keypair if needed, then run `ssh-copy-id user@host`)
+ * you have a clone or an archive of gitolite somewhere on your workstation
- git clone git://
- cd gitolite
- make master.tar
- # or maybe "make rebel.tar" or "make pu.tar"
+If so, just `cd` to that clone and run `src/` and follow the
+prompts! (Running it without any arguments shows you usage plus other useful
-### quick install from tar file
+#### advantages over the older install methods
- * make a temp directory somewhere, cd to it, and unpack the tar file
- * run `src/` and follow the prompts
+ * all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey
+ (password-less) access to the server from your workstation first
+ * the script takes care of all the server side work
+ * when done:
+ * you get two different pubkeys (the original one for command line
+ access as before, plus a new one, created by the script, for gitolite
+ access)
+ * you can admin gitolite by commit+push a "gitolite-admin" repo, just
+ like gitosis (i.e., full "push to admin" power!)
-**When you are told to edit some file, please read the comments in the file**.
-And if you can make some time to read the documentation, please do.
-Especially if you have problems.
+#### disadvantages
+ * has been tested only with Linux
- * At present the location of `~/.gitolite.rc` is fixed (maybe later I'll
- change it to a "git config" variable but I don't see much need right now)
+### manual install
- If you edit it and change any paths, be sure to keep the perl syntax --
- you *don't* have to know perl to do so, it's fairly easy to guess in this
- limited case. And of course, make sure you adjust the commands shown
- above to suit the new locations
+If for some reason you cannot use the easy-install method, (for example,
+you're on a non-Linux machine), it's not very complicated. Just open the file
+`src/` in a nice, syntax coloring, text editor, and follow
+the instructions marked "MANUAL" :-)
+### other notes
+ * If you edit `~/.gitolite.rc` and change any paths, be sure to keep the
+ perl syntax -- you *don't* have to know perl to do so, it's fairly easy to
+ guess in this limited case
* the config file is (by default) at `~/.gitolite/conf/gitolite.conf`,
though you can change its location in the "rc" file. Edit the file as you
- wish. The comments in the file ought to be clear enough but let me know
- if not
- * if you want to bring in existing (bare, server) repos into gitolite, this
- should work (refer to `~/.gitolite.rc` for *your* values of the pathnames
- below):
- * backup the repo, then move it to `$BASE_REPO`
- * copy `$GL_ADMINDIR/src/` to
- `[reponame].git/hooks/update` -- if you don't do this, per branch
- restrictions will not work
- * then update the keys and the config file and "compile" (see "admin"
- document)
-### Footnotes:
-[1] Actually, due to the way gitolite is architected, you can manage
-without `Data::Dumper` on the server if you have no choice. Only
-`gl-compile-conf` needs it, so just run that on some other machine and copy
-the two output files across. Cumbersome but doable... the advantage of
-separating all the hard work into a manually-run piece :)
-[2] If you have *only* pubkey access, and **no** password access, then your
-pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need
-to access git as a developer (clone, push, etc), do *not* submit this same
-pubkey to gitolite -- it won't work.
-Instead, create a different keypair for your "developer" role (by, e.g.,
-`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/` to
-gitolite as "", just like you would do for any other user.
-Then you create a suitable `~/.ssh/config` to use the correct key
-automatically, something like this:
- host gitadm
- hostname my.server
- user my_userid_on_server
- host gitdev
- hostname my.server
- user my_userid_on_server
- identityfile ~/.ssh/gitdev
-From now on, `ssh gitadm` will get you a command line on the server, to do
-gitolite admin and other work. And your repository URLs would look like
-`gitdev:reponame.git`. Very, very, simple...
-And as with gitosis, there's more "ssh" magic than "git" magic here :-)
+ wish. The comments in the example file (`conf/example.conf`) ought to be
+ clear enough but let me know if not
+### next steps
-gitolite is released under the GPL v2 license. See COPYING for details
+See the "admin" document for how to add users, etc.
40 doc/0-UPGRADE.mkd
@@ -1,11 +1,19 @@
# upgrading gitolite atomically
+Upgrading is done **manually, on the server** (except the last step, which is
+on your admin repo clone), even if you installed it using the easy install
+script on the client. First, it's not as difficult as an install so you don't
+really need a script. Second, you may have customised the "rc" file
+(`~/.gitolite.rc` on the server) and I'm reluctant to mess with that in an
+automated way.
### general upgrade notes
If you follow the steps below, you can make the upgrade "atomic", so you don't
have to do it at a "quiet" time or something.
-1. untar the new version to some temp directory and `cd` to it
+1. copy a tar file containing the new version to the server, untar it to some
+ temp directory and `cd` to it
2. *prepare* the new version of `~/.gitolite.rc`. It **must** have **all**
the variables defined in `conf/example.gitolite.rc` (the "new" rc file),
@@ -31,12 +39,11 @@ have to do it at a "quiet" time or something.
5. compile the config once again, in case the *internal* format of the
- compiled config file (`$GL_CONF_COMPILED`) has changed
- src/gl-compile-conf
+ compiled config file (`$GL_CONF_COMPILED`) has changed.
- (if you've already setup "push-to-admin", this step should be replaced by
- a "git push". Make a dummy commit if needed, to make the push happen).
+ To do this, you have to do a "git push" on the client side. That might
+ require a dummy change (maybe add a blank line somewhere) because
+ otherwise the push will not happen.
And you're done.
@@ -45,6 +52,27 @@ And you're done.
If any extra steps beyond the generic ones above are needed, they will be
listed here, newest first.
+#### upgrading from 410c9ba
+Between 410c9ba and this version, gitolite managed to make "push to admin" the
+default for new installs, but in a much more painless way. If you're
+upgrading, you're not forced to use "push to admin", but I'd suggest you:
+ * make sure you have password-less (pubkey) login to a command line on your
+ server
+ * save your `~/.gitolite.rc`, `keydir/*.pub` and your `conf/gitolite.conf`
+ files from the server, bring them to your workstation
+ * then run `src/` on the workstation, as if it were a
+ fresh install
+ * when the editor pops up to edit the rc file, delete all the lines in
+ it and copy them from the saved `~/.gitolite.rc`
+ * at the end of the script, after the gitolite-admin repo has been
+ cloned successfully, copy the saved `conf/gitolite.conf` and
+ `keydir/*.pub` to the clone, then add, commit, and push
+Gitolite also learnt to delegate parts of the config to other users. See
+`doc/5-delegation.mkd` for details.
#### upgrading from 8217ef9
Between 8217ef9 and this version, gitolite learnt to handle gitweb/daemon
63 doc/1-migrate.mkd
@@ -3,14 +3,9 @@
[TODO: make the migration tool fix up gitweb and daemon control also...]
Migrating from gitosis to gitolite is pretty easy, because the basic design is
-the same. The differences are:
+the same.
- * gitolite does not use a special repo for the configuration, pubkeys, etc.
- You can choose to version that directory but it is not required that you
- do so
-Here's how we migrated my work repos (note: substitute real paths, from your
-`~/.gitolite.rc`, for `$REPO_BASE` and `$GL_ADMINDIR` below):
+Here's how we migrated my work repos:
1. login as the `git` user on the server, and get a bash shell prompt
@@ -18,10 +13,17 @@ Here's how we migrated my work repos (note: substitute real paths, from your
else. This will prevent users from pushing anything while you do the
backup, migration, etc.
-3. For added safety, **delete** the post-update hook that gitosis-admin
+3. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
+ containing "gitosis-serve", as well as the marker line that says
+ "auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
+ file did not have any other keys and is now empty, don't worry -- save it
+ anyway because gitolite expects the file to be present (even if it is
+ empty).
+4. For added safety, **delete** the post-update hook that gitosis-admin
- rm $REPO_BASE/gitosis-admin.git/hooks/post-update
+ rm ~/repositories/gitosis-admin.git/hooks/post-update
or at least rename it to `.sample` like all the other hooks hanging
around, or edit it and comment out the line that calls `gitosis-run-hook
@@ -30,39 +32,34 @@ Here's how we migrated my work repos (note: substitute real paths, from your
If you do not do this, an accidental push to the gitosis-admin repo will
mess up your `~/.ssh/authorized_keys` file
-4. take a **backup** of the `$REPO_BASE` directory
-5. untar gitolite to some temporary directory and follow the instructions to
- **install** it using `src/`
+5. take a **backup** of the `~/repositories` directory
-6. **convert** your gitosis config file:
+Now, log off the server and get back to the client:
- src/ < ~/.gitosis.conf > conf/gitolite.conf
+1. follow instructions to install gitolite; see install document. Make sure
+ that you **don't** change the default path for `$REPO_BASE`!
- be sure to check the file to make sure it converted correctly
+2. **convert** your gitosis config file. Substitute the path for your
+ gitosis-admin clone in `$GSAC` below, and similarly the path for your
+ gito**lite**-admin clone in `$GLAC`
-7. **copy** the update hook to each of the existing repos (if you have repos
- in subdirectories, this won't work as is; adapt it):
+ src/ < $GSAC/gitosis.conf > $GLAC/gitolite.conf
- for i in $REPO_BASE/*.git
- do
- cp src/ $i/hooks/update
- done
+ Be sure to check the file to make sure it converted correctly
-8. **copy** the keys from gitosis's keydir
+3. **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
- cp $REPO_BASE/gitosis-admin.git/gitosis-export/keydir/* keydir
+ cp $GSAC/keydir/* $GLAC/keydir
-9. **Important: expand** any multi-key files you may have. See the "faq,
+4. **Important: expand any multi-key files you may have**. See the "faq,
tips, etc" document in the doc directory for an explanation of what
multi-keys are, how gitosis does them and how gitolite does it
You can split the keys manually, or use the following code (just
- copy-paste it into your xterm):
+ copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo
+ clone):
wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b
@@ -82,12 +79,4 @@ Here's how we migrated my work repos (note: substitute real paths, from your
"" and "" or whatever. *Please check
the files to make sure this worked properly*
-10. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
- containing "gitosis-serve", as well as the marker line that says
- "auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
- file did not have any other keys and is now empty, don't worry -- save it
- anyway because gitolite expects the file to be present (even if it is
- empty).
-At this point you're ready to "compile" the configuration. See the "admin"
-document for what to do, and how to check the outputs, etc.
+5. Check all your changes to your gitolite-admin clone, commit, and push
55 doc/2-admin.mkd
@@ -22,15 +22,15 @@ Please read on to see how to do this correctly.
extension, like `` or ``. You can also use
periods and underscores
- * copy all these `*.pub` files to `$GL_KEYDIR`
+ * copy all these `*.pub` files to `keydir` in your gitolite-admin repo clone
- * the config file (`$GL_CONF`) is very well commented, please take a couple
- of minutes to read it. Then edit it and
+ * edit the config file (`conf/gitolite.conf` in your admin repo clone). See
+ `conf/example.conf` in the gitolite source for details on what goes in
+ that file, syntax, etc. Just add new repos as needed, and add new users
+ and give them permissions as required. The users names should be exactly
+ the same as their keyfile names, but without the `.pub` extension
- * add new repos as needed
- * add new users and give them permissions as required. The users names
- should be exactly the same as their keyfile names, but without the
- `.pub` extension
+ * when done, commit your changes and push
#### specifying gitweb and daemon access
@@ -51,41 +51,6 @@ one-time setup you must do separately. All this does is:
value you specified for `$projects_list` when setting up gitweb)
* for daemon, create the file `git-daemon-export-ok` in the repository
-`src/gl-compile-conf` will keep these files consistent with the config
-settings -- this includes removing such settings if you remove "read"
-permissions for the special usernames.
-#### compiling
- * backup your `~/.ssh/authorized_keys` file if you feel nervous :-)
- * that's "backup" as in "copy", not "move". The next step won't work if
- the file doesn't exist. Even an empty one is fine but it must be
- present
- * if you don't have an `~/.ssh/authorized_keys` file at all, you may
- have logged in with a password, which in turn might mean you are not
- familiar with ssh and authkeys etc. If so, please read up at least
- [this](,
- and preferably also the man pages for sshd and sshd\_config, to make
- sure you understand the security implications of what you are doing.
- Once you have understood that, create at least an empty
- `~/.ssh/authorized_keys` file before proceeding to the next step
- * cd to `$GL_ADMINDIR` and run `src/gl-compile-conf`
-That should be it, really. However, if you want to be doubly sure, or maybe
-the first couple of times you use it, you may want to check these:
- * check the outputs
- * `~/.ssh/authorized_keys` should contain one line for each "user" pub
- key added, between two "marker" lines (which you should please please
- not remove!). The line should contain a "command=" pointing to a
- `$GL_ADMINDIR/src/gl-auth-command` file, then some sshd restrictions, the
- key, etc.
- * `$GL_CONF_COMPILED` should contain an expanded list of the access
- control rules. It may look a little long, but it's fairly intuitive!
- * if the run threw up any "initialising empty repo" messages, check the
- individual repos (inside `$REPO_BASE`) if you wish. Especially make sure
- the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is
- executable
+The "compile" script will keep these files consistent with the config settings
+-- this includes removing such settings if you remove "read" permissions for
+the special usernames.
158 doc/3-faq-tips-etc.mkd
@@ -5,19 +5,22 @@ In this document:
* common errors and mistakes
* git version dependency
* other errors, warnings, notes...
+ * getting a tar file from a clone
* differences from gitosis
* simpler syntax
* two levels of access rights checking
* error checking the config file
* delegating parts of the config file
* easier to specify gitweb/daemon access
- * built-in logging
+ * better logging
* one user, many keys
+ * support for git installed outside default PATH
* who am I?
* other cool things
- * developer specific branches
+ * "personal" branches
* design choices
* why we don't do "excludes"
+ * keeping the parser and the access control separate
### common errors and mistakes
@@ -37,10 +40,10 @@ In this document:
Here's a workaround for a version dependency that the normal flow of gitolite
-When you edit your config file to create a new repo, and run
-`src/gl-compile-conf`, gitolite creates an empty, bare repo for you.
-Normally, you're expected to clone this on the client side, and start working
--- make your first commit(s), then push, etc.
+When you edit your config file to create a new repo, and push the changes to
+the server, gitolite creates an empty, bare repo for you. Normally, you're
+expected to clone this on the client side, and start working -- make your
+first commit(s), then push, etc.
However, cloning an empty repo requires a server side git version that is at
least 1.6.2. Gitolite detects this when creating a repo, and warns you.
@@ -74,21 +77,27 @@ normal way, since it's not empty anymore.
* if you specify a repo that is not at the top level `$REPO_BASE`, be sure
to manually create the intermediate directories first. For instance if
- you specify a new repo called "a/b/c" to the config file and "compile",
- the "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has
- already been created
- * if you run `git init` inside `$GL_ADMINDIR` (that is, make it a normal,
- non-bare, repo), then, everytime you "compile" (run
- `src/gl-compile-conf`), any changes to `conf` and `keydir` will
- automatically be committed. This is a simple safety net in case you
- accidentally delete the whole config or something. Also see
- [4-push-to-admin.mkd](
- if you really know what you're doing and want "push to admin"
+ you specify a new repo called "a/b/c" to the config file and push, the
+ "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has already
+ been created
* gitweb not able to read your repos? You can change the umask for newly
created repos to something more relaxed -- see the `~/.gitolite.rc` file
+### getting a tar file from a clone
+You can clone the repo from github or indefero, then execute a make command to
+extract a tar file of the branch you want. Please use the make command, not a
+plain "git archive", because the Makefile adds a file called
+`.GITOLITE-VERSION` that will help you identify which version you are using.
+ git clone git://
+ # (OR)
+ git clone git://
+ cd gitolite
+ make master.tar
+ # or maybe "make rebel.tar" or "make pu.tar"
### differences from gitosis
Apart from the big ones listed in the top level README, and subjective ones
@@ -176,9 +185,8 @@ gitosis does not do any. I just found out that if you mis-spell `members` as
`member`, gitosis will silently ignore it, and leave you wondering why access
was denied.
-In gitolite, you have to "compile" the config file first (this step takes the
-place of the commit+push in gitosis), and keyword typos *are* caught so you
-know right away.
+Gitolite "compiles" the config file first and keyword typos *are* caught so
+you know right away.
#### delegating parts of the config file
@@ -219,24 +227,21 @@ bits and pieces. Here's an example, using short repo names for convenience:
repo r2
# ...and so on...
-#### built-in logging
+#### better logging
-...just in case of emergency :-)
+If you have been too liberal with the permission to rewind, it has built-in
+logging as an emergency fallback if someone goes too far, or for audit
+purposes [`*`]. The logfile names and location are configurable, and can
+include the year/month/day etc in the filename for easy archival or further
+processing. The log file even tells you which pattern in the config file
+matched to allow that specific access to proceed.
-Let's say you gave a dev the right to rewind a branch and he went and rewound
-it all the way, or pushed something drastically different on it. Now you need
-to recover the commit that got wiped out.
+> [`*`] setting `core.logAllRefUpdates true` does provide a safety net
+> against over-zealous rewinds, but it does not tell you "who". And
+> strangely, management does not seem to share the view that "blame" is just
+> a synonym for "annotate" ;-)]
-If you'd remembered to `git config core.logAllRefUpdates` for that repo, or
-globally, you'd be fine -- the reflog will tell you. Otherwise you'd be left
-grubbing around in `git fsck --unreachable` a bit :-(
-And even if you recover the correct commit, you'll never know *who* did it --
-not unless you add a one-line patch to gitosis, plus a `post-receive` hook to
-every repository.
-With gitolite, there's a log file in `$GL_ADMINDIR` that contains lines like
+The log lines look like this:
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
@@ -259,9 +264,35 @@ each of my pubkeys. In gitolite, we keep them separate: ""
and "". The part before the "@" is the username, so
gitolite knows these two keys belong to the same person.
+Note that you don't say "sitaram@laptop" and so on in the **config** file --
+as far as the config file is concerned there's just **one** user called
+"sitaram" -- so you only say "sitaram" there. Only the **pubkey files** have
+the extra "@" stuff.
I think this is easier to maintain if you have to delete or change one of
those keys.
+#### support for git installed outside default PATH
+The normal solution is to add to the system default PATH somehow, either by
+munging `/etc/profile` or by enabling `PermitUserEnvironment` in
+`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`.
+All these are security risks because they allow a lot more than just you and
+your git install :-)
+And if you don't have root, you can't do this anyway.
+The only solution till now has been to ask every client to set the config
+parameters `remote.<name>.receivepack` and `remote.<name>.uploadpack`. But
+telling *every* client to do so is a pain...
+Gitolite lets you specify the directory in which git binaries are to be found,
+via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is
+non-empty, it will be appended to the PATH environment variable before
+attempting to run git stuff.
+Very easy, very simple, and completely transparent to the users :-)
#### who am I?
As a developer, I send a file called `` to the gitolite admin. He
@@ -283,33 +314,31 @@ In gitolite, it's simple: just ask nicely :-)
### other cool things
-#### developer specific branches
+#### "personal" branches
-So I know what gitolite calls me. Big deal... who cares?
+"personal" branches are great for corporate environments, where
+unauthenticated pull/clone is a no-no. Since a dev workstation cannot do
+authentication, even work shared just between 2 devs has to go *via* the
+server. This causes the same branch name clutter as in a centralised VCS,
+plus setting up permissions for this becomes a chore for the admin.
-Here is an idea: give every developer a personal "scratch" namespace within
-which she can create, rewind, or delete any branch. For example, I would own
-anything under
+gitolite lets you define a "personal" or "scratch" namespace prefix for
+each developer (e.g., `refs/personal/<devname>/*`), with full
+permissions for that dev and read-only for everyone else. And you get
+this without adding a single line to the access config file -- pretty
+much fire and forget as far as the admin is concerned, even if there is
+constant churn in the project teams.
+Not bad for something that took just *one* line of code to implement.
+And that's one clean, readable, line, by the way ;-)
-The admin could set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate
+The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate
this to all users. It could be something like `refs/heads/personal`, which
means all such branches will show up in `git branch` lookups and `git clone`
will fetch them. Or he could use, say, `refs/personal`, which means it won't
show up in any normal "branch-y" commands and stuff, and generally be much
less noisy.
-Yes, I know git is all about allowing private branches, but in a corporate
-environment it's not always possible to pull from a co-worker, for the same
-reasons you don't have anonymous access (like the git:// protocol). A normal
-developer workstation cannot do authentication, so how would they know who's
-pulling? This is a perfect way to share code *without* cluttering the global
-namespace, and each developer controls his/her own set of branches!
-The amount of code needed? *One line!* I'll spend about 3x more on declaring
-and initialising the new variable, and 30x more on documenting it :-)
**Note that a user who has NO write access cannot have personal branches**; if
you read the section (above) on "two levels of access rights checking" you'll
understand why.
@@ -341,8 +370,31 @@ Just don't *show* the user this config file; it might sound insulting :-)
### design choices
+#### keeping the parser and the access control separate
+There are two programs concerned with access control:
+ * `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`;
+ this decides whether git should even be allowed to run (basic R/W/no
+ access). (This one cannot decide on the branch-level access; it is not
+ known at this point what branch is being accessed)
+ * the update-hook on each repo, which decides the per-branch permissions
+I have chosen to keep the relatively complex task of parsing the config file
+out of them to keep them simpler (and faster). So any changes to the config
+have to be first "compiled", and the access control programs use this
+"compiled" version of the config. (The compile step also refreshes
+If you choose the "easy install" method, all this is quite transparent to you
+anyway. If you cannot use the easy install and must install manually, I have
+clear instructions on how to set it up.
#### why we don't do "excludes"
+[umm... having said all this, I implemented it anyway; see the "rebel"
I found an error in the example conf file. This snippet *seems* to say that
"bruce" can write versioned tags (`refs/tags/v[0-9].*`), but the other
staffers can't:
@@ -387,3 +439,5 @@ The lack of overlap between refexes ensures ***no confusion*** in specifying,
understanding, and ***auditing***, what is allowed and what is not.
And in security, "no confusion" is a good thing :-)
5 doc/4-push-to-admin.mkd
@@ -1,5 +1,10 @@
# "push to admin" in gitolite
+HISTORICAL PURPOSES**. Gitolite now does "push-to-admin" by default, and does
+it very easily and simply by front-loading the ssh problem. See the install
+doc for details.
Gitosis's default mode of admin is by cloning and pushing the `gitosis-admin`
362 src/
@@ -0,0 +1,362 @@
+# easy install for gitolite
+# you run this on the client side, and it takes care of all the server side
+# work. You don't have to do anything on the server side directly
+# to do a manual install (since I have tested this only on Linux), open this
+# script in a nice, syntax coloring, text editor and follow the instructions
+# prefixed by the word "MANUAL" in the comments below :-)
+# run without any arguments for "usage" info
+# important setting: bail on any errors (else we have to check every single
+# command!)
+set -e
+die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; }
+prompt() {
+ echo
+ echo
+ echo ------------------------------------------------------------------------
+ echo " $1"
+ echo
+ read -p ' enter to continue or Ctrl-C to bail out'
+usage() {
+ cat <<EOFU
+Usage: $0 user host port admin_name
+ - "user" is the username on the server where you will be installing gitolite
+ - "host" is that server's hostname (or IP address is also fine)
+ - "port" is optional
+ - "admin_name" is *your* name as you want it to appear in the eventual
+ gitolite config file
+Example usage: $0 git my.git.server sitaram
+ - a proper gitolite admin repo in $HOME/gitolite-admin
+ - "user" and "admin_name" must be simple names -- no special characters etc
+ please (only alphanumerics, dot, hyphen, underscore)
+ - traditionally, the "user" is "git", but it can be anything you want
+ - "admin_name" should be your name, for clarity, or whoever will be the
+ gitolite admin
+ - you must run this from the gitolite working tree top level directory.
+ This means you run this as "src/"
+ - you must already have pubkey based access to user@host. If you currently
+ only have password access, use "ssh-copy-id" or something. Somehow get to
+ the point where you can type "ssh user@host" and get a command line. Run
+ this program only after that is done
+ - if you get a "pubkey [...filename...] exists" error, it is either leftover
+ from a previous, failed, run, or a genuine file you need. Decide which it
+ is, and remove it and retry, or use a different "admin_name", respectively.
+ exit 1;
+# ----------------------------------------------------------------------
+# basic sanity / argument checks
+# ----------------------------------------------------------------------
+# MANUAL: this *must* be run as "src/", not by cd-ing to src
+# and then running "./"
+[[ $0 =~ ^src/$ ]] ||
+ echo "please cd to the gitolite repo top level directory and run this as
+ 'src/'"
+ exit 1;
+# MANUAL: (info) we'll use "git" as the user, "server" as the host, and
+# "sitaram" as the admin_name in example commands shown below, if any
+[[ -z $3 ]] && usage
+# but if the 3rd arg is a number, that's a port number, and the 4th arg is the
+# admin_name
+[[ $3 =~ ^[0-9]+$ ]] && {
+ port=$3
+ [[ -z $4 ]] && usage
+ admin_name=$4
+[[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid"
+[[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid"
+# MANUAL: make sure you're in the gitolite directory, at the top level.
+# The following files should all be visible:
+ls src/gl-auth-command \
+ src/gl-compile-conf \
+ src/ \
+ src/ \
+ conf/example.conf \
+ conf/example.gitolite.rc >/dev/null ||
+ die "cant find at least some files in gitolite sources/config; aborting"
+# MANUAL: make sure you have password-less (pubkey) auth on the server. That
+# is, running "ssh git@server" should log in straight away, without asking for
+# a password
+ssh -p $port -o PasswordAuthentication=no $user@$host true ||
+ die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something"
+# MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually
+# for the manual flow this doesn't matter so much!)
+[[ -d $HOME/gitolite-admin ]] &&
+ die "please delete or move aside the \$HOME/gitolite-admin directory"
+# MANUAL: create a new key for you as a "gitolite user" (as opposed to you as
+# the "gitolite admin" who needs to login to the server and get a command
+# line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create
+# two files in ~/.ssh (sitaram and
+prompt "the next command will create a new keypair for your gitolite access
+ The pubkey will be $HOME/.ssh/$ You will have to
+ choose a passphrase or hit enter for none. I recommend not having a
+ passphrase for now, and adding one with 'ssh-keygen -p' *as soon as*
+ all the setup is done and you've successfully cloned and pushed the
+ gitolite-admin repo.
+ After that, I suggest you (1) install 'keychain' or something
+ similar, and (2) add the following command to your bashrc (since
+ this is a non-default key)
+ ssh-add \$HOME/.ssh/$admin_name
+ This makes using passphrases very convenient."
+if [[ -f $HOME/.ssh/$ ]]
+ prompt "Hmmm... pubkey $HOME/.ssh/$ exists; should I just re-use it?
+ Be sure you remember the passphrase, if you gave one when you created it!"
+ ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..."
+# MANUAL: copy the pubkey created to the server, say to /tmp. This would be
+# "scp ~/.ssh/ git@server:/tmp" (the script does this at a later
+# stage, you do it now for convenience). Note: only the pubkey (
+# Do NOT copy the ~/.ssh/sitaram file -- that is a private key!
+# MANUAL: if you're running ssh-agent (see if you have an environment variable
+# called SSH_AGENT_PID in your "env"), you should add this new key. The
+# command is "ssh-add ~/.ssh/sitaram"
+if ssh-add -l &>/dev/null
+ prompt "you're running ssh-agent. We'll try and do an ssh-add of the
+ private key we just created, otherwise this key won't get picked up. If
+ you specified a passphrase in the previous step, you'll get asked for one
+ now -- type in the same one."
+ ssh-add $HOME/.ssh/$admin_name
+# MANUAL: you now need to add some lines to the end of your ~/.ssh/config
+# file. If the file doesn't exist, create it. Make sure the file is "chmod
+# 644".
+# The lines to be included look like this:
+# host gitolite
+# user git
+# hostname server
+# port 22
+# identityfile ~/.ssh/sitaram
+echo "
+host gitolite
+ user $user
+ hostname $host
+ port $port
+ identityfile ~/.ssh/$admin_name" > $HOME/.ssh/.gl-stanza
+if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null
+ prompt "your \$HOME/.ssh/config already has settings for gitolite. I will
+ assume they're correct, but if they're not, please edit that file, delete
+ that paragraph (that line and the following few lines), Ctrl-C, and rerun.
+ In case you want to check right now (from another terminal) if they're
+ correct, here's what they are *supposed* to look like:
+$(cat ~/.ssh/.gl-stanza)"
+ prompt "creating settings for your gitolite access in $HOME/.ssh/config;
+ these are the lines that will be appended to your ~/.ssh/config:
+$(cat ~/.ssh/.gl-stanza)"
+ cat $HOME/.ssh/.gl-stanza >> $HOME/.ssh/config
+ # if the file didn't exist at all, it might have the wrong permissions
+ chmod 644 $HOME/.ssh/config
+rm $HOME/.ssh/.gl-stanza
+# ----------------------------------------------------------------------
+# client side stuff almost done; server side now
+# ----------------------------------------------------------------------
+# MANUAL: copy the gitolite directories "src", "conf", and "doc" to the
+# server, to a directory called (for example) "gitolite-install". You may
+# have to create the directory first.
+ssh -p $port $user@$host mkdir -p gitolite-install
+rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/
+# MANUAL: now log on to the server (ssh git@server) and get a command line.
+# This step is for your convenience; the script does it all from the client
+# side but that may be too much typing for manual use ;-)
+# MANUAL: cd to the "gitolite-install" directory where the sources are. Then
+# copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you wish to
+# change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you
+# will need them later
+prompt "the gitolite rc file needs to be edited by hand. The defaults
+ are sensible, so if you wish, you can just exit the editor.
+ Otherwise, make any changes you wish and save it. Read the comments to
+ understand what is what -- the rc file's documentation is inline.
+ Please remember this file will actually be copied to the server, and that
+ all the paths etc. represent paths on the server!"
+# lets try and get the file from there first
+if scp -P $port $user@$host:.gitolite.rc .
+ prompt "Oh hey... you already had a '.gitolite.rc' file on the server.
+ Let's see if we can use that instead of the default one..."
+ sort < .gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.old
+ sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' >
+ if diff -u glrc.old
+ then
+ ${VISUAL:-${EDITOR:-vi}} .gitolite.rc
+ else
+ prompt " looks like you're upgrading! I'm going to run your editor
+ with *both* the old and the new files (in that order), so you can add
+ in the lines pertaining to the variables shown with a '+' sign in the
+ above diff. This is necessary; please dont skip this
+ [It's upto you to figure out how your editor handles 2 filename
+ arguments, switch between them, copy lines, etc ;-)]"
+ ${VISUAL:-${EDITOR:-vi}} .gitolite.rc conf/example.gitolite.rc
+ fi
+ cp conf/example.gitolite.rc .gitolite.rc
+ ${VISUAL:-${EDITOR:-vi}} .gitolite.rc
+# copy the rc across
+scp -P $port .gitolite.rc $user@$host:
+prompt "ignore any 'please edit this file' or 'run this command' type
+ lines in the next set of command outputs coming up. They're only relevant
+ for a manual install, not this one..."
+# extract the GL_ADMINDIR and REPO_BASE locations
+GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'")
+REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'")
+# MANUAL: still in the "gitolite-install" directory? Good. Run
+# "src/"
+ssh -p $port $user@$host "cd gitolite-install; src/"
+# MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf
+# and add at least the following lines to it:
+# repo gitolite-admin
+# RW+ = sitaram
+echo "#gitolite conf
+#please see conf/example.conf for details on syntax and features
+repo gitolite-admin
+ RW+ = $admin_name
+repo testing
+ RW+ = @all
+" > gitolite.conf
+# send the config and the key to the remote
+scp -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/
+scp -P $port $HOME/.ssh/$ $user@$host:$GL_ADMINDIR/keydir
+# MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf"
+ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf"
+# ----------------------------------------------------------------------
+# hey lets go the whole hog on this; setup push-to-admin!
+# ----------------------------------------------------------------------
+# MANUAL: make the first commit in the admin repo. This is a little more
+# complex, so read carefully and substitute the correct paths. What you have
+# to do is:
+# cd $REPO_BASE/gitolite-admin.git
+# GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
+# GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
+# Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no
+# space around the "=" in the second and third lines.
+echo "cd $REPO_BASE/gitolite-admin.git
+GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
+GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty
+" | ssh -p $port $user@$host
+# MANUAL: now that the admin repo is created, you have to set the hooks
+# properly. The install program does this. So cd back to the
+# "gitolite-install" directory and run "src/"
+ssh -p $port $user@$host "cd gitolite-install; src/"
+prompt "now we will clone the gitolite-admin repo to your workstation
+ and see if it all hangs together. We'll do this in your \$HOME for now,
+ and you can move it elsewhere later if you wish to."
+# MANUAL: you're done! Log out of the server, come back to your workstation,
+# and clone the admin repo using "git clone gitolite:gitolite-admin.git"!
+cd $HOME
+git clone gitolite:gitolite-admin.git
+# MANUAL: be sure to read the message below; this applies to you too...
+echo ------------------------------------------------------------------------
+echo "Cool -- we're done. Now you can edit the config file (currently
+in ~/gitolite-admin/conf/gitolite.conf) to add more repos, users, etc.
+When done, 'git add' the changed files, 'git commit' and 'git push'.
+Read the comments in conf/example.conf for information about the config
+file format -- like the rc file, this also has inline documentation.
+Your URL for cloning any repo on this server will be
+ gitolite:reponame.git
+However, any other users you set up will have to use
+ $user@$host:reponame.git
+unless they also create similar settings in their '.ssh/config' file."
5 src/gl-auth-command
@@ -24,13 +24,16 @@ use warnings;
# ----------------------------------------------------------------------------
our %repos;
my $glrc = $ENV{HOME} . "/.gitolite.rc";
die "parse $glrc failed: " . ($! or $@) unless do $glrc;
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
+# add a custom path for git binaries, if specified
# ----------------------------------------------------------------------------
# definitions specific to this program
# ----------------------------------------------------------------------------
5 src/gl-compile-conf
@@ -47,7 +47,7 @@ $Data::Dumper::Indent = 1;
# common definitions
# ----------------------------------------------------------------------------
# now that this thing *may* be run via "push to admin", any errors have to
# grab the admin's ATTENTION so he won't miss them among the other messages a
@@ -57,6 +57,9 @@ my $ATTN = "\n\t\t***** ERROR *****\n ";
my $glrc = $ENV{HOME} . "/.gitolite.rc";
die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc;
+# add a custom path for git binaries, if specified
# ----------------------------------------------------------------------------
# definitions specific to this program
# ----------------------------------------------------------------------------
16 src/
@@ -3,7 +3,7 @@
use strict;
use warnings;
# wrapper around mkdir; it's not an error if the directory exists, but it is
# an error if it doesn't exist and we can't create it
@@ -33,6 +33,9 @@ sub wrap_mkdir
# ok now $glrc exists; read it to get the other paths
die "parse $glrc failed: " . ($! or $@) unless do $glrc;
+# add a custom path for git binaries, if specified
# mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
@@ -66,7 +69,10 @@ sub wrap_mkdir
# oh and one of those repos is a bit more special and has an extra hook :)
-system("cp $GL_ADMINDIR/src/ gitolite-admin.git/hooks/post-update");
-system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)",
- "gitolite-admin.git/hooks/post-update");
-chmod 0755, "gitolite-admin.git/hooks/post-update";
+if ( -d "gitolite-admin.git/hooks" ) {
+ print STDERR "copying post-update hook to gitolite-admin repo...\n";
+ system("cp -v $GL_ADMINDIR/src/ gitolite-admin.git/hooks/post-update");
+ system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)",
+ "gitolite-admin.git/hooks/post-update");
+ chmod 0755, "gitolite-admin.git/hooks/post-update";
Please sign in to comment.
Something went wrong with that request. Please try again.