Compare commits

..

No commits in common. "master" and "v3.02" have entirely different histories.

128 changed files with 7624 additions and 2093 deletions

View file

@ -1,86 +0,0 @@
2012-12-29 v3.3 bug fix: gl-perms propagation to slaves broke sometime
after v3.2 (so if you're only picking up tagged releases
you're OK)
the "D" command now allows rm/unlock to be totally
disabled
new trigger: update-gitweb-daemon-from-options; another
way to update gitweb and daemon access lists
new 'create' command for explicit wild repo creation, and
new AutoCreate trigger to control auto-creation
allow simple macros in conf file
2012-11-14 v3.2 major efficiency boost for large setups
optional support for multi-line pubkeys; see
src/triggers/post-compile/ssh-authkeys-split
bug fix for not creating gl-conf when repo para has only
config lines and no access rules
new 'bg' trigger command to put long jobs started from a
trigger into background
%GL_REPO and %GL_CREATOR now work for 'option's also
test suite now much more BSD friendly
2012-10-05 v3.1 (security) fix path traversal on wild repos
new %GL_CREATOR variable for git-config lines
rsync command to create and send bundles automagically
migrated 'who-pushed'
logical expressions on refexes!!!
2012-06-27 v3.04 documentation graduated and moved out of parents house :)
new trigger for 'repo specific umask'
new 'list-dangling-repos' command
new LOCAL_CODE rc var; allow admin specified programs to
override system-installed ones
new 'upstream' trigger-cum-command to maintain local
copies of external repos
new 'sudo' command
minor backward compat breakage in 'gitolite query-rc'
'perms' command can now create repo if needed
migrated 'symbolic-ref' command
'gitolite setup --hooks-only'
2012-05-23 v3.03 fix major bug that allowed an admin to get a shell
2012-05-20 v3.02 packaging instructions fixed up and smoke tested
make it easier to give some users a full shell
allow aliasing a repo to another name
simulate POST_CREATE for new normal (non-wild) repos
(just for kicks) a VREF that allows for voting on changes
to a branch
bug fix: smart http was not running PRE_ and POST_GIT
triggers
htpasswd migrated
2012-04-29 v3.01 mostly BSD and Solaris compat
also fork command added
2012-04-18 v3.0 first release to "master"
This is a compete rewrite of gitolite; please see
documentation before upgrading.

359
README.md
View file

@ -1,359 +0,0 @@
Github-users: click the 'wiki' link before sending me anything via github.
Existing users: this is gitolite v3.x. If you are upgrading from v2.x this
file will not suffice; you *must* check the online docs (see below for URL).
------------------------------------------------------------------------
This file contains BASIC DOCUMENTATION ONLY.
* It is suitable for a fresh, ssh-based, installation of gitolite and basic
usage of its most important features.
* It is NOT meant to be exhaustive or detailed.
The COMPLETE DOCUMENTATION is at:
http://sitaramc.github.com/gitolite/master-toc.html
Please go there for what/why/how, concepts, background, troubleshooting, more
details on what is covered here, or advanced features not covered here.
------------------------------------------------------------------------
BASIC DOCUMENTATION FOR GITOLITE
================================
This file contains the following sections:
* INSTALLATION AND SETUP
* ADDING USERS AND REPOS
* HELP FOR YOUR USERS
* BASIC SYNTAX
* ACCESS RULES
* GROUPS
* COMMANDS
* THE 'rc' FILE
* GIT-CONFIG
* GIT-DAEMON
* GITWEB
* CONTACT AND SUPPORT
* LICENSE
------------------------------------------------------------------------
INSTALLATION AND SETUP
----------------------
Server requirements:
* any unix system
* sh
* git 1.6.6+
* perl 5.8.8+
* openssh 5.0+
* a dedicated userid to host the repos (in this document, we assume it
is 'git'), with shell access ONLY by 'su - git' from some other userid
on the same server.
Steps to install:
* login as 'git' as described above
* make sure ~/.ssh/authorized_keys is empty or non-existent
* make sure your ssh public key from your workstation is available at $HOME/YourName.pub
* run the following commands:
git clone git://github.com/sitaramc/gitolite
mkdir -p $HOME/bin
gitolite/install -to $HOME/bin
gitolite setup -pk YourName.pub
If the last command doesn't run perhaps 'bin' in not in your 'PATH'.
You can either add it, or just run:
$HOME/bin/gitolite setup -pk YourName.pub
ADDING USERS AND REPOS
----------------------
Do NOT add new repos or users manually on the server. Gitolite users,
repos, and access rules are maintained by making changes to a special repo
called 'gitolite-admin' and pushing those changes to the server.
----
To administer your gitolite installation, start by doing this on your
workstation (if you have not already done so):
git clone git@host:gitolite-admin
**NOTE**: if you are asked for a password, something has gone wrong.
Now if you 'cd gitolite-admin', you will see two subdirectories in it:
'conf' and 'keydir'.
To add new users alice, bob, and carol, obtain their public keys and add
them to 'keydir' as alice.pub, bob.pub, and carol.pub respectively.
To add a new repo 'foo' and give different levels of access to these
users, edit the file 'conf/gitolite.conf' and add lines like this:
repo foo
RW+ = alice
RW = bob
R = carol
See the 'ACCESS RULES' section later for more details.
Once you have made these changes, do something like this:
git add conf
git add keydir
git commit -m 'added foo, gave access to alice, bob, carol'
git push
When the push completes, gitolite will add the new users to
~/.ssh/authorized_keys on the server, as well as create a new, empty, repo
called 'foo'.
HELP FOR YOUR USERS
-------------------
Once a user has sent you their public key and you have added them as
specified above and given them access, you have to tell them what URL to
access their repos at. This is usually 'git clone git@host:reponame'; see
man git-clone for other forms.
**NOTE**: again, if they are asked for a password, something is wrong.
If they need to know what repos they have access to, they just have to run
'ssh git@host info'; see 'COMMANDS' section later for more on this.
BASIC SYNTAX
------------
The basic syntax of the conf file is very simple.
* Everything is space separated; there are no commas, semicolons, etc.,
in the syntax.
* Comments are in the usual perl/shell style.
* User and repo names are as simple as possible; they must start with an
alphanumeric, but after that they can also contain '.', '_', or '-'.
Usernames can optionally be followed by an '@' and a domainname
containing at least one '.'; this allows you to use an email address
as someone's username.
Reponames can contain '/' characters; this allows you to put your
repos in a tree-structure for convenience.
* There are no continuation lines.
ACCESS RULES
------------
This section is mostly 'by example'.
Gitolite's access rules are very powerful. The simplest use was already
shown above. Here is a slightly more detailed example:
repo foo
RW+ = alice
- master = bob
- refs/tags/v[0-9] = bob
RW = bob
RW refs/tags/v[0-9] = carol
R = dave
For clones and fetches, as long as the user is listed with an R, RW
or RW+ in at least one rule, he is allowed to read the repo.
For pushes, rules are processed in sequence until a rule is found
where the user, the permission (see note 1), and the refex (note 2)
*all* match. At that point, if the permission on the matched rule
was '-', the push is denied, otherwise it is allowed. If no rule
matches, the push is denied.
Note 1: permission matching:
* a permission of RW matches only a fast-forward push or create
* a permission of RW+ matches any type of push
* a permission of '-' matches any type of push
Note 2: refex matching:
(refex = optional regex to match the ref being pushed)
* an empty refex is treated as 'refs/.*'
* a refex that does not start with 'refs/' is prefixed with 'refs/heads/'
* finally, a '^' is prefixed
* the ref being pushed is matched against this resulting refex
With all that background, here's what the example rules say:
* alice can do anything to any branch or tag -- create, push, delete, rewind/overwrite etc.
* bob can create or fast-forward push any branch whose name does
not start with 'master' and create any tag whose name does not
start with 'v'+digit.
* carol can create tags whose names start with 'v'+digit.
* dave can clone/fetch.
GROUPS
------
Gitolite allows you to group users or repos for convenience. Here's an
example that creates two groups of users:
@staff = alice bob carol
@interns = ashok
repo secret
RW = @staff
repo foss
RW+ = @staff
RW = @interns
Group lists accumulate. The following two lines have the same effect as
the earlier definition of @staff above:
@staff = alice bob
@staff = carol
You can also use group names in other group names:
@all-devs = @staff @interns
Finally, @all is a special group name that is often convenient to use if
you really mean 'all repos' or 'all users'.
COMMANDS
--------
Users can run certain commands remotely, using ssh. For example:
ssh git@host help
prints a list of available commands.
The most commonly used command is 'info'. All commands respond to a
single argument of '-h' with suitable information.
If you have shell on the server, you have a lot more commands available to
you; try running 'gitolite help'.
THE 'rc' FILE
--------------
Some of the instructions below may require you to edit the rc file
(~/.gitolite.rc on the server).
The rc file is perl code, but you do NOT need to know perl to edit it.
Just mind the commas, use single quotes unless you know what you're doing,
and make sure the brackets and braces stay matched up.
GIT-CONFIG
----------
Gitolite lets you set git-config values for individual repos without
having to log on to the server and run 'git config' commands:
repo foo
config hooks.mailinglist = foo-commits@example.tld
config hooks.emailprefix = '[foo] '
config foo.bar = ''
config foo.baz =
**WARNING**
The last syntax shown above is the *only* way to *delete* a config
variable once you have added it. Merely removing it from the conf
file will *not* delete it from the repo.git/config file.
**SECURITY NOTE**
Some git-config keys allow arbitrary code to be run on the server.
If all of your gitolite admins already have shell access to the server
account hosting it, you can edit the rc file (~/.gitolite.rc) on the
server, and change the GIT_CONFIG_KEYS line to look like this:
GIT_CONFIG_KEYS => '.*',
Otherwise, give it a space-separated list of regular expressions that
define what git-config keys are allowed. For example, this one allows
only variables whose names start with 'gitweb' or with 'gc' to be
defined:
GIT_CONFIG_KEYS => 'gitweb\..* gc\..*',
GIT-DAEMON
----------
Gitolite creates the 'git-daemon-export-ok' file for any repo that is
readable by a special user called 'daemon', like so:
repo foo
R = daemon
GITWEB
------
Any repo that is readable by a special user called 'gitweb' will be added
to the projects.list file.
repo foo
R = gitweb
Or you can set one or more of the following config variables instead:
repo foo
config gitweb.owner = some person's name
config gitweb.description = some description
config gitweb.category = some category
**NOTE**
You will probably need to change the UMASK in the rc file from the
default (0077) to 0027 and add whatever user your gitweb is running as
to the 'git' group. After that, you need to run a one-time 'chmod -R'
on the already created files and directories.
------------------------------------------------------------------------
CONTACT AND SUPPORT
-------------------
Mailing list for support and general discussion:
gitolite@googlegroups.com
subscribe address: gitolite+subscribe@googlegroups.com
Mailing list for announcements and notices:
subscribe address: gitolite-announce+subscribe@googlegroups.com
IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
time zone).
Author: sitaramc@gmail.com, but please DO NOT use this for general support
questions. Subscribe to the list and ask there instead.
LICENSE
-------
The gitolite *code* is released under GPL v2. See COPYING for details.
This documentation, which is part of the source code repository, is
provided under a Creative Commons Attribution-ShareAlike 3.0 Unported
License -- see http://creativecommons.org/licenses/by-sa/3.0/

38
README.mkd Normal file
View file

@ -0,0 +1,38 @@
# Gitolite README
**Github users: please read the "wiki" link at the top of the page before
submitting issues or pull requests**.
----
If you're reading this on the main gitolite page on github, several
**IMPORTANT CHANGES** have happened to gitolite:
1. A competely re-written version of gitolite has been pushed to the "master"
branch, and is now the actively maintained and supported software. Do NOT
try to merge this with your old "master" branch!
The [main page][h-mp] leads to several useful starting points. The [table
of contents][h-mt] is a much more meaningfully ordered/structured list of
links (instead of putting them in alphabetical order of the filename, like
in g2!)
If you are an existing (g2) user and wish to migrate, you MUST read
[this](http://sitaramc.github.com/gitolite/install.html#migr).
2. Versions v2.x are on branch "g2". It will be supported for security
issues and serious bugs in core functionality, but not for anything less
critical. Versions v1.x are completely unsupported now. (Documentation
links for this version are [here][o1] and [here][o2]).
[h-mp]: http://sitaramc.github.com/gitolite/
[h-mt]: http://sitaramc.github.com/gitolite/master-toc.html
[o1]: http://sitaramc.github.com/gitolite/g2/
[o2]: http://sitaramc.github.com/gitolite/g2/master-toc.html
----
License information for code and documentation is at the end of doc/index.mkd
(or you can read it online
[here](http://sitaramc.github.com/gitolite/index.html#license)).

View file

@ -38,9 +38,8 @@ sub intro {
msg( '' => "or that might end up giving *more* access to someone if migrated as-is." );
msg( '' => "It does NOT attempt to catch all the differences described in the docs." );
msg( '', '' );
msg( INFO => "'see docs' usually means the pre-migration checklist in" );
msg( '', => "'g2migr.html'; to get there, start from the main migration" );
msg( '', => "page at http://sitaramc.github.com/gitolite/install.html#migr" );
msg( INFO => "'see docs' usually means doc/g2migr.mkd" );
msg( '', => "(online at http://sitaramc.github.com/gitolite/g3/g2migr.html)" );
msg( '', '' );
}
@ -55,13 +54,14 @@ sub rc_basic {
sub rest_of_rc {
msg( SEVERE => "GIT_PATH found; see docs" ) if $GIT_PATH;
msg( SEVERE => "GL_ALL_INCLUDES_SPECIAL found; see docs" ) if $GL_ALL_INCLUDES_SPECIAL;
msg( SEVERE => "GL_GET_MEMBERSHIPS_PGM not yet implemented" ) if $GL_GET_MEMBERSHIPS_PGM;
msg( SEVERE => "GL_NO_CREATE_REPOS not yet implemented" ) if $GL_NO_CREATE_REPOS;
msg( SEVERE => "rsync not yet implemented" ) if $RSYNC_BASE;
msg( SEVERE => 'htpasswd, rsync, and svnserve not yet implemented' ) if $HTPASSWD_FILE or $RSYNC_BASE or $SVNSERVE;
msg( WARNING => "ADMIN_POST_UPDATE_CHAINS_TO found; see docs" ) if $ADMIN_POST_UPDATE_CHAINS_TO;
msg( WARNING => "GL_NO_DAEMON_NO_GITWEB found; see docs" ) if $GL_NO_DAEMON_NO_GITWEB;
msg( WARNING => "GL_NO_SETUP_AUTHKEYS found; see docs" ) if $GL_NO_SETUP_AUTHKEYS;
msg( WARNING => "UPDATE_CHAINS_TO found; see docs" ) if $UPDATE_CHAINS_TO;
msg( WARNING => "GL_ADC_PATH found; see docs" ) if $GL_ADC_PATH;
msg( WARNING => "GL_ADC_PATH found; many ADCs not yet implemented" ) if $GL_ADC_PATH;
msg( WARNING => "non-default GL_WILDREPOS_PERM_CATS found" ) if $GL_WILDREPOS_PERM_CATS ne 'READERS WRITERS';
}

View file

@ -47,9 +47,6 @@ while (<>)
$groupname = '';
$reponame = $1;
$reponame =~ s/\.git$//;
} elsif (/^\[gitosis\]$/) {
$groupname = '';
$reponame = '@all';
} elsif (/^gitweb\s*=\s*yes/i) {
push @{$repos{$reponame}{R}}, 'gitweb';
} elsif (/^daemon\s*=\s*yes/i) {

22
doc/CHANGELOG Normal file
View file

@ -0,0 +1,22 @@
2012-05-20 v3.02 packaging instructions fixed up and smoke tested
make it easier to give some users a full shell
allow aliasing a repo to another name
simulate POST_CREATE for new normal (non-wild) repos
(just for kicks) a VREF that allows for voting on changes
to a branch
bug fix: smart http was not running PRE_ and POST_GIT
triggers
htpasswd migrated
2012-04-29 v3.01 mostly BSD and Solaris compat
also fork command added
2012-04-18 v3.0 first release to "master"
This is a compete rewrite of gitolite; please see
documentation before upgrading.

1450
doc/Markdown.pl Executable file

File diff suppressed because it is too large Load diff

72
doc/WARNINGS.mkd Normal file
View file

@ -0,0 +1,72 @@
# WARNINGS
Gitolite does NOT like it if you fiddle with files, directories, permissions,
etc., on the server except as directed in the documentation. Gitolite also
expects all the directories and files it manages/uses to be owned by the
hosting user and not have strange permissions and ownerships.
Very few people have fallen foul of this, but the ones who *did* became very
obnoxious about it, hence this warning.
Gitolite depends on several system-installed packages: openssh, git, perl, sh
being the main ones. They should all be configured sensibly and with most of
the normal defaults. (For example, if your sshd config says the authorized
keys file should be placed in some directory other than the default, expect
trouble).
----
For the entertainment of the sensible majority, and as a way of thanking all
of you, here are some examples of requests (demands in some cases) I have
received over the last couple of years.
* deleting environment variables copied from client session
demand: add code to delete certain environment variables at startup
because "the openssh servers in the linux distribution that [he] use[s],
are configured to copy `GIT_*` variables to the remote session".
This is wrong on so many levels it's almost plonk-able!
* using `cp` instead of `ln`
Guy has an NTFS file system mounted on Linux. So... no symlinks (an NTFS
file system on Windows works fine because msysgit/cygwin manage to
*simulate* them. NTFS mounted on Linux won't do that!)
He wanted all the symlink stuff to be replaced by copies.
No. Way.
* non-bare repos on the server
Some guy gave me a complicated spiel about git-svn not liking bare repos
or whatever. I tuned off at the first mention of those 3 letters so I
don't really know what the actual problem was.
But it doesn't matter. Even if someone (Ralf H) had not chipped in with a
workable solution, I still would not do it. A server repo should be bare.
Period.
* incomplete ownership of `GL_REPO_BASE`
This guy had a repo-base directory where not all of the files were owned
by the git user. As a result, some of the hooks did not get created. He
claimed my code should detect OS-permissions issues while it's doing its
stuff.
No. I refuse to have the code constantly look over its shoulder making
sure fundamental assumptions are being met.
* empty template directory
(See man git-init for what a template directory is).
The same guy with the environment variables had an empty template
directory because he "does not like to have sample hooks in every
repository". So naturally, the hooks directory does not get created when
you run a `git init`. He expects gitolite to compensate for it.
Granted, it's only a 1-line change. But again, this falls under
"constantly looking over your shoulder to double check fundamental
assumptions". Where does it end?

50
doc/admin.mkd Normal file
View file

@ -0,0 +1,50 @@
# gitolite administration
## #server server-side administration
The following activities require command line access to the server. They are
usually one-time or rarely done activities.
* Changing anything in the [rc][] file.
* Installing custom [hooks][], whether to all repos or just some repos.
* Moving [existing][] repos into gitolite control.
Please read the [WARNINGS][] page first.
## #adminrepo access control via the gitolite-admin repo
Most day-to-day administration of a gitolite site happens like this:
* [clone][] the gitolite-admin repo to your workstation
* make appropriate changes
* add, commit, and push
### #conf the conf/gitolite.conf file
Most of gitolite's power is in the conf/gitolite.conf file, which specifies
detailed access control for repos. Everything except [adding users][users]
happens from this file.
Here is an example of a simple conf/gitolite.conf file.
@staff = dilbert alice # line 1
@projects = foo bar # line 2
repo @projects baz # line 3
RW+ = @staff # line 4
- master = ashok # line 5
RW = ashok # line 6
R = wally # line 7
config hooks.emailprefix = '[%GL_REPO] ' # line 8
Use the following links to learn more:
* The basic [syntax][] -- comments, whitespace, include files, etc.
* Defining [groups][], as in lines 1 and 2.
* Adding and removing [users][].
* Adding and removing [repos][], as in line 3.
* Defining access [rules][], as in lines 4, 5, 6, and 7.
* Gitolite [options][].
* [Git config][git-config] keys and values, as in line 8.
* ["Wild"][wild] repos -- ad hoc, user-created, repos.

19
doc/clone.mkd Normal file
View file

@ -0,0 +1,19 @@
# cloning the admin repo
This is the third step in using gitolite, after [install][] and [setup][].
To clone the admin repo, go to the workstation where the public key used in
'setup' came from, and run this:
git clone git@host:gitolite-admin
NOTE that (1) you must not include the `repositories/` part (gitolite handles
that internally), and (2) you may include the ".git" at the end but it is
optional.
If this step succeeds, you can add [users][], [repos][], or anything else
described [here][adminrepo].
If this step fails, be sure to look at the [ssh][] documentation before asking
for help. (A very basic first step is to run `ssh git@host info`;
[this][info] page tells you what to expect).

83
doc/cust.mkd Normal file
View file

@ -0,0 +1,83 @@
# customising gitolite
Much of gitolite (g3)'s functionality comes from programs and scripts that are
not considered "core". This keeps the core simpler, and allows you to enhance
gitolite for your own purposes without too much fuss. (As an extreme example,
even mirroring is not in core now!)
(Also, please see the [developer notes][dev-notes] page).
----
[[TOC]]
----
## types of non-core programs
There are 5 basic types of non-core programs.
* *Commands* can be run from the shell command line. Among those, the ones
listed in the COMMANDS hash of the rc file can also be run remotely.
* *Hooks* are standard git hooks; see below.
* *Sugar scripts* change the conf language for your convenience. The word
sugar comes from "syntactic sugar".
* *Triggers* are to gitolite what hooks are to git. I just chose a
different name to avoid confusion and constant disambiguation in the docs.
* **VREFs** are extensions to the access control check part of gitolite.
[Here][non-core] is a list of non-core programs shipped with gitolite, with
some description of each.
## #commands gitolite "commands"
Gitolite comes with several commands that users can run. Remote users run the
commands by saying:
ssh git@host command-name [args...]
while on the server you can run
gitolite command [args...]
Very few commands are designed to be run both ways, but it can be done, by
checking for the presence of env var `GL_USER`.
You can get a **list of available commands** by using the `help` command.
Naturally, a remote user will see a much smaller list than the server user.
You add commands to the "allowed from remote" list by adding its name (or
uncommenting it if it's already added but commented out) to the COMMANDS hash
in the [rc][] file.
If you write your own commands, put them in src/commands.
## #hooks hooks and gitolite
Gitolite uses the `update` hook for all repos. In addition, it uses the
`post-update` hook for the gitolite-admin repo.
If you want to add your own hook, it's easy as long as it's not the 'update'
hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`.
The rest is between you and 'man githooks' :-)
## #sugar syntactic sugar
Sugar scripts help you change the perceived syntax of the conf language. The
base syntax of the language is very simple, so sugar scripts take something
*else* and convert it into that.
That way, the admin sees additional features (like allowing continuation
lines), while the parser in the core gitolite engine does not change.
If you want to write your own sugar scripts, please read the "your own sugar"
section in [dev-notes][] first then email me.
## triggers
Triggers have their own [document][triggers].
## VREFs
VREFs also have their own [document][vref].

118
doc/deleg.mkd Normal file
View file

@ -0,0 +1,118 @@
# delegating access control responsibilities
Delegation allows you to divide up a large conf file into smaller groups of
repos (called **subconf**s) and hand over responsibility to manage them to
**sub-admin**s. Gitolite can prevent one sub-admin from being able to set
access rules for any other sub-admin's repos.
Delegation is achieved by combining two gitolite features: [subconf][] and the
[NAME VREF][NAME].
To understand delegation, read both those links then come back to this
example.
## example
@webbrowsers = firefox lynx browsers/..*
@webservers = apache nginx servers/..*
@malwares = conficker storm ms/..*
# side note: if anyone objects, we claim ms stands for "metasploit" ;-)
# the admin repo access was probably like this to start with:
repo gitolite-admin
RW+ = sitaram
# now add these lines to the config for the admin repo
RW = alice bob mallory
RW VREF/NAME/conf/subs/webbrowsers = alice
RW VREF/NAME/conf/subs/webservers = bob
RW VREF/NAME/conf/subs/malwares = mallory
- VREF/NAME/ = alice bob mallory
Finally, you tell gitolite to pull in these files using the "subconf" command
subconf "subs/*.conf"
And that's it.
## #subconf the subconf command
Subconf is exactly like the include command in syntax:
subconf "foo.conf"
but while reading the included file (as well as anything included from it),
gitolite sets the "subconf name" to "foo".
A "subconf" imposes some restrictions on what repos can be managed.
For example, while the subconf name is "foo", as in the above example,
gitolite will only process "repo" lines for:
* A repo called "foo".
* A group called "@foo", as long as the group is defined in the main conf
file (i.e., *outside* "foo.conf").
* A member of a group called "@foo" (again, defined outside).
* A repo that matches a member of a group called "@foo" if that member is a
regular expression pattern.
Here's an example. If the main conf file contains
@foo = aa bb cc/..*
then the subconf can only accept repo statements that refer to 'foo', '@foo',
'aa', 'bb', or any repo whose name starts with 'cc/'.
**Note**: the subconf name "master" is special; it is the default subconf in
effect for the main conf file and has no restrictions.
### how the "subconf name" is derived
For subconf lines that look just like include statements, i.e.,
subconf "foo/bar.conf"
subconf "frob/*.conf"
# assume frob has files aa.conf, bb.conf
the subconf name as each file is being processed is the base name of the file.
This means it would be "bar" for the first line, "aa" when processing
"frob/aa.conf", and "bb" when processing "frob/bb.conf".
A variation of subconf exists that can explicitly state the subconf name:
subconf foo "frob/*.conf"
In this variation, regardless of what file in "frob/" is being read, the
subconf name in effect is "foo".
## security notes
### group names
You can use "@group"s defined in the main config file but do not attempt to
redefine or extend them in your own subconf file. If you must extend a group
(say `@foo`) defined in the main config file, do this:
@myfoo = @foo
# now do whatever you want with @myfoo
Group names you define in your subconf will not clash even if the exact same
name is used in another subconf file, so you need not worry about that.
### delegating pubkeys
Short answer: not gonna happen.
The delegation feature is meant only for access control rules, not pubkeys.
Adding/removing pubkeys is a much more significant event than changing branch
level permissions for people already on staff, and only the main admin should
be allowed to do it.
Gitolite's "userids" all live in the same namespace. This is unlikely to
change, so please don't ask -- it gets real complicated to do otherwise.
Allowing sub-admins to add users means username collisions, which also means
security problems (admin-A creates a pubkey for Admin-B, thus gaining access
to all of Admin-B's stuff).
If you feel the need to delegate even that, please just go the whole hog and
give them separate gitolite instances (i.e., running under different gitolite
hosting users)!

113
doc/dev-notes.mkd Normal file
View file

@ -0,0 +1,113 @@
# notes for developers
Gitolite has a huge bunch of existing features that gradually need to moved
over. Plus you may want to write your own programs to interact with it.
Here are some random notes on developing hooks, commands, triggers, and sugar
scripts.
## environment variables and other inputs
In general, the following environment variables should always be available:
GL_BINDIR
GL_REPO_BASE
GL_ADMIN_BASE
Commands invoked by a remote client will also have `GL_USER` set. Hooks will
have `GL_REPO` also set.
Finally, note that triggers get a lot of relevant information from gitolite as
arguments; see [here][triggers] for details.
## APIs
### the shell API
The following commands exist to help you write shell scripts that interact
easily with gitolite. Each of them responds to `-h` so please run that for
more info.
* `gitolite access` to check access rights given repo, user, type of access
(R, W, ...) and refname (optional). Example use: src/commands/desc.
* `gitolite creator` to get/check the creator of a repo. Example use:
src/commands/desc.
* `gitolite git-config` to check gitolite options or git config variables
directly from gitolite's "compiled" output, (i.e., without looking at the
actual `repo.git/config` file or using the `git config` command). Example
use: none yet.
* `gitolite query-rc` to check the value of an RC variable. Example use:
src/commands/desc.
In addition, you can also look at the comments in src/lib/Gitolite/Easy.pm
(the perl API module) for ideas.
### the perl API
...is implemented by Gitolite::Easy; the comments in src/lib/Gitolite/Easy.pm
serve as documentation.
## writing your own...
### ...hooks
#### anything but the update hook
If you want to add your own hook, it's easy as long as it's not the 'update'
hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`.
The rest is between you and 'man githooks' :-)
#### update hook
If you want to add additional `update` hook functionality, do this:
* Write and test your update hook separately from gitolite.
* Now add the code to src/VREF. Let's say it is called "foo".
* To call your new update hook to all accesses for all repos, add this to
the end of your conf file:
repo @all
- VREF/foo = @all
As you probably guessed, you can now make your additional update hooks more
selective, applying them only to some repos / users / combinations.
Note: a normal update hook expects 3 arguments (ref, old SHA, new SHA). A
VREF will get those three, followed by at least 4 more. Your VREF should just
ignore the extra args.
### ...commands
You can add your own commands. You can run them on the server (example,
`gitolite access`). Then you can enable certain commands to be allowed to run
by a remote user by adding them to the "COMMANDS" hash of the [rc][] file.
Commands are standalone programs, in any language you like. They simply
receive the arguments you append. In addition, the env var `GL_USER` is
available if it is being run remotely. src/commands/desc is the best example
at present.
### ...trigger programs
Trigger programs run at specific points in gitolite's execution, with specific
arguments being passed to them. See the [triggers][] page for details.
You can write programs that are both manually runnable as well as callable by
trigger events, especially if they don't *need* any arguments.
### ..."sugar"
Syntactic sugar helpers are NOT complete, standalone, programs. They must
include a perl sub called `sugar_script` that takes in a listref, and returns
a listref. The listrefs point to a list that contains the entire conf file
(with all [include][] processing already done). You create a new list with
contents modified as you like and return a ref to it.
There are a couple of examples in src/syntactic-sugar.

35
doc/dev-status.mkd Normal file
View file

@ -0,0 +1,35 @@
## #dev-status g3 development status
Not yet done (will be tackled in this order unless someone asks):
* mechanism for ADCs using unchecked arguments -- this is not just a matter
of writing it; I have to think about *how* it will be done. (AFAIK the
only tool affected is git-annexe; if there are more let me know)
Help needed:
* I'd like distro packagers to play with it and help with migration advice
for distro-upgrades
* rsync, htpasswd
* git-annexe support (but this has a pre-requisite in the previous list)
Won't be done unless someone asks (saw no evidence that anyone used them in g2
anyway!):
* mob branches
* password access
* some of the more arcane rc variables!
* specific ADCs -- there are too many for me to bother without applying
Pareto somewhere, so I choose to not do any and wait for people to ask :-)
Done:
* core code
* test suite
* mirroring
* documentation
* migration documentation
* distro packaging instructions
* migration advice for common cases
* smart http
* svnserve

190
doc/emergencies.mkd Normal file
View file

@ -0,0 +1,190 @@
# help for emergencies
----
"Don't Panic!"
----
## #lost-key lost admin key/access
If you lost your gitolite **admin** key or access, here's what you do. We'll
assume your username is 'alice'.
* Make yourself a new keypair and copy the public key to the server as
'alice.pub'.
* Log on to the server, and run `gitolite setup -pk alice.pub`.
That's it; the new alice.pub file replaces whatever existed in the repo
before.
## #bypass bypassing gitolite
You may have lost access because of a conf file error, in which case the above
trick won't help. What you want is to make changes to the repo (or perhaps
just rewind) and push that. Here's how to do that:
* Log on to the server.
* Clone the admin repo using the full path: `git clone
$HOME/repositories/gitolite-admin.git temp`.
* Make whatever changes you want -- add/replace a key, 'git revert' or 'git
reset --hard' to an older commit, etc. Anything you need to fix the
problem, really.
* Run `gitolite push` (or possibly `gitolite push -f`). Note that's
'gitolite push', not 'git push'.
<font color="red">
**NOTE**: gitolite does **no access checking** when you do this!
</font>
## #clean cleaning out a botched install
If you've read the [files involved in gitolite][files] page, you probably know
the answer, but here's a list of files you should blow away.
* **Gitolite sources** -- can be found by running `which gitolite`. If it's
a symlink, go to its target directory.
* **Gitolite admin directory** -- `$HOME/.gitolite`. Save the 'logs'
directory if you want to preserve them for any reason.
* **The rc file** -- `$HOME/.gitolite.rc`. If you made any changes to it
you can save it as some other name instead of deleting it.
* **The gitolite-admin repo** -- `$HOME/repositories/gitolite-admin.git`.
You can clone it somewhere to save it before blowing it away if you wish.
* **Git repositories** -- `$HOME/repositories`. The install process will
not touch any existing repos except 'gitolite-admin.git', so you do not
have to blow away (or move) your work repos to fix a botched install.
Only when you update the conf to include those repos and push the changes
will those repos be touched. And even then all that happens is that the
update hook, if any, is replaced with gitolite's own hook.
* **Ssh stuff** -- exercise caution when doing this, but in general it
should be safe to delete all lines between the "gitolite start" and
"gitolite end" markers in `$HOME/.ssh/authorized_keys`.
Gitolite does not touch any other files in the ssh directory.
## #ce common errors
* `WARNING: keydir/<yourname>.pub duplicates a non-gitolite key, sshd will ignore it`
You used a key that is already set to give you shell access. You cannot
use the same key to get shell access as well as access gitolite repos.
Solution: use a different keypair for gitolite. There's a slightly longer
discussion in the [setup][] page. Also see [why bypassing causes a
problem][ybpfail] and both the documents in [ssh][] for background.
* `Empty compile time value given to use lib at hooks/update line 6`
(followed by `Can't locate Gitolite/Hooks/Update.pm in @INC` a couple of
lines later).
You're bypassing gitolite. You cloned the repo using the full path (i.e.,
including the `repositories/` prefix), either directly on the server, or
via ssh but with a key that gives you **shell** access.
Solution: same as for the previous bullet.
NOTE: If you really *must* do it, and this is a one-time thing, you can
try `gitolite push` instead of `git push`. **BUT**... this defeats all
gitolite access control, so if you're going to do this often, maybe you
don't need gitolite!
## #ue uncommon errors
* `WARNING: split conf not set, gl-conf present for <repo>`
(Case 1) This can happen if you have a *bare* repo (i.e., some `repo.git`
directory) copied from g2 with `GL_BIG_CONFIG` on, and you pushed a change
to the conf or ran certain commands *before* adding the newly added repo
to the conf file.
(Case 2) This can also happen if you changed something like this
repo foo
...<some rules>...
to this
@grp = foo
repo @grp
...<some rules>...
Also, even running `gitolite setup` will not fix this.
The root cause is an internal consistency check that I do not wish to
disable or subvert. It is there for a reason, and I would prefer a
warning that a human can investigate.
If you're sure the reasons are one of the two above, you can either add
the repo to the conf file in case 1, or manually remove the gl-conf file
from the repo.git directory in case 2.
Either way, run `gitolite setup` afterwards to make sure things are in
good shape.
If you think neither of those is the cause, email me.
## #ngp things that are not gitolite problems
There are several things that appear to be gitolite problems but are not. I
cannot help with most of these (although the good folks on irc or the mailing
list -- see [contact][] -- might be able to; they certainly appear to have a
lot more patience than I do, bless 'em!)
* **Client side software**
* putty/plink
* jgit/Eclipse
* Mac OS client **or** server
* putty/plink
* windows as a server
* ...probably some more I forgot; will update this list as I remember...
* did I mention putty/plink?
* **Ssh**
The *superstar* of the "not a gitolite problem" category is actually ssh.
Surprised? It is so common that it has [its own document][auth] to tell
you why it is *not* a gitolite problem, while [another one][ssh] tries to
help you anyway!
Everything I know is in that latter link. Please email me about ssh ONLY
if you find something wrong or missing in those documents.
* **Git**
I wish I had a dollar for each time someone did a *first push* on a new
repo, got an error because there were "no refs in common (etc.)", and
asked me why gitolite was not allowing the push.
Gitolite is designed to look like just another bare repo server to a
client (except requiring public keys -- no passwords allowed). It is
*completely transparent* when there is no authorisation failure (i.e.,
when the access is allowed, the remote client has no way of knowing
gitolite was even installed!)
Even "on disk", apart from reserving the `update` hook for itself,
gitolite does nothing to your bare repos unless you tell it to (for
example, adding 'gitweb.owner' and such to the config file).
BEFORE you think gitolite is the problem, try the same thing with a normal
bare repo. In most cases you can play with it just by doing something
like this:
mkdir /tmp/throwaway
cd /tmp/throwaway
git clone --mirror <some repo you have a URL for> bare.git
git clone bare.git worktree
cd worktree
<...try stuff>

71
doc/external.mkd Normal file
View file

@ -0,0 +1,71 @@
# interfacing with external tools
> ----
> **Note**: The old gitolite (v1.x, v2.x) used to tie itself into knots
> dealing with gitweb and daemon. One of the goals of g3 was to get out of
> that game, which your author does not play anyway. This means statements
> like "...special user called 'gitweb'..." really apply to the [non-core][]
> programs that gitolite ships with, not to "core" gitolite, and any or all
> of this functionality can be disabled by commenting out certain lines in
> the [rc][] file.
> ----
> Also, **note** that gitolite does **not** install or configure
> gitweb/git-daemon -- that is a one-time setup you must do separately.
> ----
## gitweb
The following repos are deemed to be readable by gitweb:
* any repos readable by the special user `gitweb`
* any repos containing one or more of the following types of lines:
config gitweb.owner = owner name
config gitweb.description = some description
config gitweb.category = some category
Side note: the following shorter forms are available as [syntactic
sugar][sugar] for the above longer forms:
owner = owner name
desc = some description
category = some category
The list of gitweb-readable repos is written to a file whose name is given by
the [rc][] file variable `GITWEB_PROJECTS_LIST`. The default value of this
variable, if it is not specified or empty, is `$HOME/projects.list`.
In addition, each of the config variables described above is written to the
repo to which it pertains, so that gitweb can use them.
### #umask changing the UMASK
Gitweb typically runs under a different userid, and the default permissions
that gitolite sets make them unreadable.
See the section on the `UMASK` variable in the documentation for the [rc
file][rc].
## git-daemon
Any repo readable by the special user `daemon` is deemed to be readable by
git-daemon. For each of these repos, an empty file called
`git-daemon-export-ok` is created in the repository (i.e., the `repo.git`
directory inside `$HOME/repositories`).
## tips
Setting descriptions en-masse usually does not make sense, but you can
certainly do things like
repo @all
R = gitweb daemon
assuming you have other means of setting 'gitweb.description' and
'gitweb.owner'.
Also see [this][deny-rules] for a twist on that.

106
doc/extras/auth.mkd Normal file
View file

@ -0,0 +1,106 @@
# authentication versus authorisation
This document will explain why an "ssh issue" is almost never a "gitolite
issue", and, indirectly, why I dont get too excited about the former.
Note: for actual ssh troubleshooting see [this][sts].
Here is a fundamental point: <font color="red">**Gitolite does not do
authentication. It only does authorisation**.</font>
So first, let's loosely define these words:
> **Authentication** is the process of verifying that you are who you claim
> to be. An authentication system will establish that I am the user
> "sitaram" on my work system. The one behind gmail will similarly
> establish that I am "sitaramc". And so on...
> **Authorisation** is the process of asking what you want to do and
> deciding if you're allowed to do it or not.
Now, if you managed to read about [gitolite and ssh][glssh], you
know that gitolite is meant to be invoked as:
/full/path/to/gitolite-shell some-authenticated-gitolite-username
(where the "gitolite username" is a "virtual" username; it does not have to
be, and usually *isn't*, an actual *unix* username).
As you can see, authentication happens before gitolite is called.
## but... but... you have all that ssh stuff in gitolite!
No I don't. Not in "core" gitolite from g3 onwards :-)
The default setup does use ssh keys, but it's only helping you **setup**
ssh-based authentication **as a convenience to you**. But in fact it is a
*completely* separate program that you can disable (in the rc file) or replace
with something else of your choice.
For example, in both [smart http][http] and ldap-backed sshd, gitolite has no
role to play in creating users, setting up their passwords/keys, etc.
## so you're basically saying you won't support "X"
(where "X" is some ssh related behaviour change or feature)
Well, if it's not a security issue I won't. But since it's no longer part of
"core" gitolite, I can be much more relaxed about taking patches, or even
alternative implementations.
While we're on the subject, locking someone out is *not* a security issue.
Even if you [lost the admin key][lost-key], the docs tell you how to recover
from such errors. You do need some password based method to get a shell
command line on the server, of course.
## #otherauth how to use other authentication systems with gitolite
The bottom line in terms of how to invoke gitolite has been described above,
and as long as you manage to do that gitolite won't even know how the
authentication was done. Which in turn means you can use whatever
authentication scheme you want.
It also expects the `SSH_ORIGINAL_COMMAND` environment variable to contain the
full command (typically starting with git-receive-pack or git-upload-pack)
that the client sent. Also, when using [smart http][http], things are somewhat
different: gitolite uses certain environment variables that it expects httpd
to have set up. Even the user name comes from the `REMOTE_USER` environment
variable instead of as a command line argument in this case.
However, it has to be an authentication system that is compatible with sshd or
httpd in some form. Why? Because the git *client* accessing the server only
knows those 2 protocols to "speak git". (Well, the `git://` protocol is
unauthenticated, and `file://` doesn't really apply to this discussion, so
we're ignoring those).
For example, let's say you have an LDAP-based authentication system somewhere.
It is possible to make apache use that to authenticate users, so when a user
accesses a git url using `http://sitaram:password@git.example.com/repo`, it is
LDAP that does the actual authentication. [I wouldn't know how to do it but I
know it is possible. Patches to this doc explaining how are welcome!]
There are also ssh daemons that use LDAP to store the authorised keys (instead
of putting them all in `~/.ssh/authorized_keys`). The clients will still need
to generate keypairs and send them to the admin, but they can be more
centrally stored and perhaps used by other programs or tools simultaneously,
which can be useful.
## #ldap getting user group info from LDAP
Gitolite's [groups][] are pretty convenient, but some organisations already
have similar (or sufficient) information in their LDAP store.
Gitolite can tap into that information, with a little help. Write a program
which, given a username, queries your LDAP store and returns a space-separated
list of groups that the user is a member of. Then put the full path to this
program in an [rc][] variable called `GROUPLIST_PGM`, like so:
GROUPLIST_PGM => '/home/git/bin/ldap-query-groups',
Now you can use those groupnames in access rules in gitolite, just as if you
had declared their memberships in the conf file.
Caution: your program must do its own logging if you want the audit trail of
"why/how did this user get access to this repo at this time?" to resolve
properly. Gitolite does not do any logging of the results of the queries
because for people who don't need it that would be a huge waste.

145
doc/extras/glssh.mkd Normal file
View file

@ -0,0 +1,145 @@
## #glssh how gitolite uses ssh
Although other forms of authentications exist (see the document on
[authentication versus authorisation][auth]), ssh is the one that most git
users use.
***Therefore, gitolite is (usually) heavily dependent on ssh***.
Most people didn't realise this, and even if they did they don't know ssh
well enough to help themselves. If you don't understand how ssh public key
authentication works, or how the `~/.ssh/authorized_keys` file can be used to
restrict users, etc., you will have endless amounts of trouble getting
gitolite to work, because you'll be attacking the wrong problem.
So please please please understand this before tearing your hair out and
blaming ***git/gitolite*** for whatever is going wrong with your setup :-)
### ssh basics
Let's start with some basics, focusing *only* on the pieces relevant to
`gitolite`. If this is not detailed enough, please use google and learn more
from somewhere, or maybe buy the OReilly ssh book.
* You can login to an ssh server by typing a password, but ssh can also use
***public-private keys*** (also called "key pairs") for authentication.
`gitolite` *requires* you to use this mechanism for your users -- they
cannot log in using passwords. Hopefully by the time you finish reading
this document you will understand why :-)
The way you set this up is you generate a key pair on your workstation,
and give the server the public key. (I need not add that the "private"
key must be, well, kept *private*!)
* **Generating a key pair on your workstation** is done by running the
command `ssh-keygen -t rsa`. This produces two files in `~/.ssh`. One is
`id_rsa`; this is the **private** key -- ***never*** let it out of your
machine. The other is `id_rsa.pub`, which is the corresponding public
key. This public key is usually just one long line of text.
* On Windows machines with msysgit installed, you should do this from
within a "git bash" window. The command will report the full path where
the files have been written; make a note of this, and use those files in
any of the description that follows.
* **Adding your public key to the server**'s `~/.ssh/authorized_keys`
file is how ssh uses pubkeys to authenticate users. Let's say
sita@work.station is trying to log in as git@serv.er. What you have to do
is take the `~/.ssh/id_rsa.pub` file for user sita on work.station and
append its contents (remember it's only one line) to
`~/.ssh/authorized_keys` for user git on serv.er.
The `authorized_keys` file can have multiple public keys (from many
different people) added to it so any of them can log in to git@serv.er.
In the normal case (not gitolite, but your normal everyday shell access),
there's a command that does this, `ssh-copy-id`, which also fixes up
permissions etc., as needed, since sshd is a little picky about allowing
pubkey access if permissions on the server are loose. Or you can do it
manually, as long as you know what you're doing and you're careful not to
erase or overwrite the existing contents of `~/.ssh/authorized_keys` on
the server!
But in the gitolite case, it's different; we'll get to that in a minute.
* **Troubleshooting pubkey authentication failures**: if you are unable to
get ssh access to the server after doing all this, you'll have to look
in `/var/log/secure` or `/var/log/auth.log` or some such file on the
server to see what specific error `sshd` is complaining about.
* **Restricting users to specific commands** is very important for gitolite.
If you read `man sshd` and look for `authorized_keys file format`, you'll
see a lot of options you can add to the public key line, which restrict
the incoming user in various ways. In particular, note the `command=`
option, which means "regardless of what the incoming user is asking to do,
forcibly run this command instead".
Also note that when there are many public keys (i.e., lines) in the
`authorized_keys` file, each line can have a *different* set of options
and `command=` values.
Without this `command=` option, the ssh daemon will simply give you a
shell, which is not what we want for our gitolite keys (although we may
well have other keys which we use to get a shell).
**This is the backbone of what makes gitolite work; please make sure you
understand this**.
### how does gitolite use all this ssh magic?
These are two different questions you ought to be having by now:
* How does it distinguish between me and someone else, since we're all
logging in as the same remote user "git".
* How does it restrict what I can do within a repository.
#### restricting shell access/distinguishing one user from another
The answer to the first question is the `command=` we talked about before. If
you look in the `authorized_keys` file, you'll see entries like this (I chopped
off the ends of course; they're pretty long lines):
command="[path]/gitolite-shell sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t...
command="[path]/gitolite-shell usertwo",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...
First, it finds out which of the public keys in this file match the incoming
login. That's crypto stuff, and I won't go into it. Once the match has been
found, it will run the command given on that line; e.g., if I logged in, it
would run `[path]/gitolite-shell sitaram`. So the first thing to note is
that such users do not get "shell access", which is good!
Before running the command, however, sshd sets up an environment variable
called `SSH_ORIGINAL_COMMAND` which contains the actual git command that your
workstation sent out. This is the command that *would have run* if you did
not have the `command=` part in the authorised keys file.
When `gitolite-shell` gets control, it looks at the first argument
("sitaram", "usertwo", etc) to determine who you are. It then looks at the
`SSH_ORIGINAL_COMMAND` variable to find out which repository you want to
access, and whether you're reading or writing.
Now that it has a user, repository, and access requested (read/write), gitolite looks
at its config file, and either allows or rejects the request.
But this cannot differentiate between different branches within a repo; that
has to be done separately.
#### restricting branch level actions
[If you look inside the git source tree, there's a file among the "howto"s in
there called `update-hook-example.txt`, which was the inspiration for this
part of gitolite.]
Git allows you to specify many "hooks", which get control as various events
happen -- see `git help hooks` for details. One of those hooks is the
`update` hook, which, if it is present, is invoked just before a branch or a
tag is about to be updated. The hook is passed the name of the branch or tag,
the old SHA1 value, and the new SHA1 value, as arguments. Hooks that are
called *before* an action happens are allowed to prevent that action from
happening by returning an error code.
When gitolite is told to create a new repository (by the admin), it installs
a special update hook. This hook takes all the information presented, looks
at the config file, and decides to allow or reject the update.
And that's basically it.

203
doc/extras/putty.mkd Normal file
View file

@ -0,0 +1,203 @@
# putty and msysgit
This document is intended for those who wish to use Putty/Plink with msysgit.
If you need more help with putty or component programs I suggest looking at [the official putty documentation](http://the.earth.li/~sgtatham/putty/latest/htmldoc/).
**If you are not already using Putty for SSH it is recommended you do _NOT_ use it with msysgit.**
**Please note that this only covers the client side of things, and does not involve server side components to troubleshooting. For that, please see the [ssh-troubleshooting document][sts].**
<a name="msysgit_setup"/>
## msysgit setup
Provided you have putty sessions msysgit should give you the option of specifying a location to plink. If it did not then you will need to add an environment variable named "GIT\_SSH" to point at plink.exe, wherever you have that sitting.
How to do that on your version of windows will likely vary, and is not covered here. For purposes of example, on a 64 bit Windows Vista machine the GIT\_SSH value could be:
C:\Program Files (x86)\PuTTY\plink.exe
Note the lack of quotes.
Testing that msysgit is properly configured can be done from the git bash shell. Simply type (case sensitive, include the quotes):
"$GIT_SSH" -V
You should get a response similar to this:
plink: Release 0.60
If instead you get a "command not found" type error you likely have a typo in your environment variable.
<a name="Going_back_to_OpenSSH"/>
## Going back to OpenSSH
If you wish to go back to OpenSSH all you need to do is delete the GIT\_SSH environment variable. This will vary by your version of windows and thus is not covered here.
<a name="Putty_keys"/>
## Putty keys
If you do not already have putty private key files (.ppk) you will need to make at least one. You can either make a new one or convert an existing key to putty private key format.
Either way, you will want to use puttygen. Note that you can go the other way if you want to stop using putty but keep the key by exporting the key to OpenSSH format.
<a name="Creating_a_new_key"/>
### Creating a new key
To make it simple, I suggest SSH-2 RSA and a bit size of at least 1024. Larger keys will take longer to generate and will take longer to authenticate you on most systems. Making the key is as simple at hitting "Generate".
It is recommended to give the key a meaningful comment.
<a name="Importing_an_existing_key"/>
### Importing an existing key
If you already have an OpenSSH or ssh.com key you can import it using the "Import" option on the "Conversions" menu.
If the key does not have a meaningful comment I would suggest adding one at this point.
<a name="Loading_an_existing_key"/>
### Loading an existing key
If you need to load an existing key to edit or view it you can do so from the File menu.
<a name="Public_key"/>
### Public key
To get your public key for use with gitolite, load (or generate, or import) your key into puttygen. There is a box labeled "Public key for pasting into OpenSSH `authorized_keys` file" there. Copy the text into your preferred text editor and save.
<a name="Putty_ageant"/>
### Putty ageant
Though not required in all cases you may wish to use the putty ageant, pageant, to load your key(s). This will allow for your key(s) to be passphrase protected but not have to enter the passphrase when you go to use them, provided you have already loaded the key into the ageant.
<a name="Sessionless_or_raw_hostname_usage"/>
## Sessionless or raw hostname usage
When using plink without a putty session you pretty much have to load your keys with putty ageant, if only so that plink can find them.
<a name="Putty_sessions"/>
## Putty sessions
In addition to hostnames msysgit can, when using putty, use putty sessions. This works in a manner similar to definitions in OpenSSH's `ssh_config` file. All settings in the session that apply to plink usage will be loaded, including the key file to use and even the username to connect to. Thus, instead of:
ssh://user@host.example.ext:port/repo
You can use:
ssh://session_name/repo
<a name="Host_key_authentication"/>
## Host key authentication
Whether you are using hostnames or sessions you still run into one potential problem. Plink currently wants to validate the server's SSH host key before allowing you to connect, and when git calls plink there is no way to tell it yes. Thus, you may get something like this:
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
Connection abandoned.
fatal: The remote end hung up unexpectedly
Or, in the case of the host key changing, something like this:
WARNING - POTENTIAL SECURITY BREACH!
The server's host key does not match the one PuTTY has
cached in the registry. This means that either the
server administrator has changed the host key, or you
have actually connected to another computer pretending
to be the server.
The new rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
Connection abandoned.
fatal: The remote end hung up unexpectedly
The solution is to call plink directly, or start putty and connect with it first. To use plink, open the Git Bash shell and enter:
"$GIT_SSH" hostname_or_session_name
When you do you will see something like this:
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
Or, in the case of a changed key, a response like this:
WARNING - POTENTIAL SECURITY BREACH!
The server's host key does not match the one PuTTY has
cached in the registry. This means that either the
server administrator has changed the host key, or you
have actually connected to another computer pretending
to be the server.
The new rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
If you were expecting this change and trust the new key,
enter "y" to update PuTTY's cache and continue connecting.
If you want to carry on connecting but without updating
the cache, enter "n".
If you want to abandon the connection completely, press
Return to cancel. Pressing Return is the ONLY guaranteed
safe choice.
Update cached key? (y/n, Return cancels connection)
In either case hit y and the key will be stored.
<a name="Debugging_multiple_putty_ageant_keys"/>
## Debugging multiple putty ageant keys
In the event you are using putty ageant with multiple keys loaded you may see the wrong key being used. In general, pageant keys are tried in the order they were loaded into the ageant. If you have descriptive comment on each of your keys you can try connecting with plink in verbose mode to see what keys are being tried. Simply open the Git bash shell and run:
"$GIT_SSH" -v user@hostname
Or, if using sessions with a pre-entered username:
"$GIT_SSH" -v session_name
In either case, you should look for lines like:
Trying Pageant key #0
Authenticating with public key "My Key" from agent
The first says which (numerical) key the ageant is trying. The second tells you the key comment for the authenticating key. To my knowledge the second line should only show up once, for the valid key.
<a name="Setperms_and_other_commands"/>
## Setperms and other commands
When using wildcard repos the setperms command is very important, and other commands can come in handy as well. See their documentation for how to use them, but where they use:
ssh user@host command etc etc
You will want to use:
"$GIT_SSH" user@host command etc etc
Otherwise everything should be identical.
<a name="About_this_document"/>
## About this document
This document was written by Thomas Berezansky (tsbere (at) mvlc (dot) org) in the hopes that it would be useful to those using putty on windows and wishing to use git/gitolite with their putty keys and sessions.

34
doc/extras/regex.mkd Normal file
View file

@ -0,0 +1,34 @@
# extremely brief regex overview
Regexes are powerful. Gitolite uses that power as much as it can. If you
can't handle that power, hire someone who can and become a manager.
That said, here's a very quick overview of the highlights.
`^` and `$` are called "anchors". They anchor the match to the beginning and
end of the string respectively.
^foo matches any string starting with 'foo'
foo$ matches any string ending with 'foo'
^foo$ matches exact string 'foo'.
To be precise, the last one is "any string starting and ending with *the same*
'foo'". "foofoo" does not match.
`[0-9]` is an example of a character class; it matches any single digit.
`[a-z]` matches any lower case alpha, and `[0-9a-f]` is the range of hex
characters. You should now guess what `[a-zA-Z0-9_]` does.
`.` (the period) is special -- it matches any character. If you want to match
an actual period, you need to say `\.`.
`*`, `?`, and `+` are quantifiers. They apply to the previous token. `a*`
means "zero or more 'a' characters". Similarly `a+` means "one or more", and
`a?` means "zero or one".
As a result, `.*` means "any number (including zero) of any character".
The previous token need not be a single character; you can use parens to make
it longer. `(foo)+` matches one or more "foo", (like "foo", "foofoo",
"foofoofoo", etc.)

16
doc/extras/ssh.mkd Normal file
View file

@ -0,0 +1,16 @@
# ssh
If you're installing gitolite, you're a "system admin", like it or not. If
you're using the default ssh mode (i.e., not [http][] mode), ssh is a
necessary skill. Please take the time to learn at least enough to get
passwordless access working.
There are two documents you need to read, in order:
* [Gitolite and ssh][glssh] explains how gitolite uses openssh features to
create any number of virtual users over just one actual (unix) user, and
distinguish between them by their public keys.
* [Ssh troubleshooting][sts] is a rather long document that, as far as I
know, covers almost every known ssh related issue. If you find something
missing, send me an email with details so I can update it.

428
doc/extras/sts.mkd Normal file
View file

@ -0,0 +1,428 @@
## #sts ssh troubleshooting
**This document must be read in full the first time. If you start from some
nice looking section in the middle it may not help you unless you're already
an expert at ssh**.
This document should help you troubleshoot ssh-related problems in installing
and accessing gitolite.
### IMPORTANT -- READ THIS FIRST
#### caveats
* Before reading this document, it is **mandatory** to read and **completely
understand** [this][ssh], which is a very detailed look at how gitolite
uses ssh's features on the server side. Don't assume you know all that;
if you did, you wouldn't be needing *this* document either!
* This document, and others linked from this, together comprise all the help
I can give you in terms of the ssh aspect of using gitolite. If you're
installing gitolite, you're a "system admin", like it or not. Ssh is
therefore a necessary skill. Please take the time to learn at least
enough to get passwordless access working.
* Please note that authentication is not really gitolite's job at all. I'd
rather spend time on actual gitolite features, code, and documentation
than authentication (i.e., ssh, in the common case).
Surprised? [This][auth] might help explain better.
#### naming conventions used
* Your workstation is the **client**. Your userid on the client does not
matter, and it has no relation to your gitolite username.
* The server is called **server** and the "hosting user" is **git**. If
this is an RPM/DEB install, the hosting user is probably called
"gitolite", however we will use "git" in this document.
#### taking stock -- relevant files and directories
* The client has a `~/.ssh` containing a few keypairs. It may also have a
`config` file.
* The client also has a clone of the "gitolite-admin" repo, which contains a
bunch of `*.pub` files in `keydir`. We assume this clone is in `$HOME`;
if it is not, adjust instructions accordingly when needed.
* The git user on the server has a `~/.ssh/authorized_keys` file that the
ssh daemon uses to authenticate incoming users. We often call this file
**authkeys** to save typing, and it always means the one on the server
(we're not interested in this file on the client side).
* The server also has a `~/.gitolite/keydir` which contains a bunch of
`*.pub` files.
#### normal gitolite key handling
Here's how normal gitolite key handling works:
* (On client) pub key changes like adding new ones, deleting old ones, etc.,
are done in the `keydir` directory in the gitolite-admin repo clone. Then
the admin `git add`s and `git commit`s those changes, then `git push`es
them to the server.
* (On server) a successful push from the client makes git invoke the
post-update hook in the gitolite-admin repo. This hook is installed by
gitolite, and it does a bunch of things which are quite transparent to
the admin, but we'll describe briefly here:
* The pubkey files from this push are checked-out into
`~/.gitolite/keydir` (and similarly the config files into
`~/.gitolite/conf`).
* The "compile" script then runs, which uses these files to populate
`~/.ssh/authorized_keys` on the server.
The authkeys file may have other, (non-gitolite) keys also. Those
lines are preserved. Gitolite only touches lines that are found
between gitolite's "marker" lines (`# gitolite start` and `# gitolite
end`).
### common ssh problems
Since I'm pretty sure at least some of you didn't bother to read the
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
you there again. Especially the first bullet.
Done? OK, read on...
The following problem(s) indicate that pubkey access is not working at all, so
you should start with [appendix 1][stsapp1]. If that doesn't fix the problem, continue
with the other appendices in sequence.
* Running any git clone/fetch/ls-remote or just `ssh git@server info` asks
you for a password.
The following problem(s) indicate that your pubkey is bypassing gitolite and
going straight to a shell. You should start with [appendix 2][sshkeys-lint]
and continue with the rest in sequence. [Appendix 5][ybpfail] has some
background info.
* Running `ssh git@server info` gets you the output of the GNU 'info'
command instead of gitolite's version and access info.
* Running `git clone git@server:repositories/reponame` (note presence of
`repositories/` in URL) works.
[A proper gitolite key will only let you `git clone git@server:reponame`
(note absence of `repositories/`)]
* You are able to clone repositories but are unable to push changes back
(the error complains about the `GL_BINDIR` environment variable not being
set, and the `hooks/update` failing in some way).
[If you run `git remote -v` you will find that your clone URL included the
`repositories/` described above!]
* Conversely, using the correct syntax, `git clone git@server:reponame`
(note absence of `repositories/` in the URL), gets you `fatal: 'reponame'
does not appear to be a git repository`, and yet you are sure 'reponame'
exists, you haven't mis-spelled it, etc.
### step by step
Since I'm pretty sure at least some of you didn't bother to read the
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
you there again. Especially the first bullet.
Done? OK, now the general outline for ssh troubleshooting is this:
* Make sure the server's overall setup even *allows* pubkey based login.
I.e., check that git fetch/clone/ls-remote commands or a plain `ssh
git@server info` do NOT ask for a password. If you do get asked for a
password, see [appendix 1][stsapp1].
* Match client-side pubkeys (`~/.ssh/*.pub`) with the server's authkeys
file. To do this, run `sshkeys-lint`, which tells you in detail what key
has what access. See [appendix 2][sshkeys-lint].
* At this point, we know that we have the right key, and that if sshd
receives that key, things will work. But we're not done yet. We still
need to make sure that this specific key is being offered/sent by the
client, instead of the default key. See [appendix 3][stsapp3] and
[appendix 4][ssh-ha].
### random tips, tricks, and notes
#### giving shell access to gitolite users
Thanks to an idea from Jesse Keating, a single key can allow both gitolite
access *and* shell access.
To do this:
* add the list of users who will have shell access -- one username per line,
no extra whitespace -- to a plain text file of your choice.
* put the name of this file in a new rc variable `SHELL_USERS_LIST`. For
example it could be
SHELL_USERS_LIST => "$ENV{HOME}/.gitolite.shell-users",
* add the line `'Shell::input',` to the `INPUT` list in the rc file.
* add the line `'post-compile/ssh-authkeys-shell-users',` to the
`POST_COMPILE` list, *after* the `'post-compile/ssh-authkeys',` line.
Then run `gitolite compile; gitolite trigger POST_COMPILE` or push a dummy
change to the admin repo.
#### simulating ssh-copy-id
don't have `ssh-copy-id`? This is broadly what that command does, if you want
to replicate it manually. The input is your pubkey, typically
`~/.ssh/id_rsa.pub` from your client/workstation.
* It copies it to the server as some file.
* It appends that file to `~/.ssh/authorized_keys` on the server
(creating it if it doesn't already exist).
* It then makes sure that all these files/directories have go-w perms
set (assuming user is "git"):
/home/git/.ssh/authorized_keys
/home/git/.ssh
/home/git
[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`,
typically) also must be `go-w`, but that needs root. And typically
they're already set that way anyway. (Or if they're not, you've got
bigger problems than gitolite install not working!)]
#### problems with using non-openssh public keys
Gitolite accepts public keys only in openssh format. Trying to use an "ssh2"
key (used by proprietary SSH software) will not be a happy experience.
src/triggers/post-compile/ssh-authkeys can be made to detect non-openssh
formats and automatically convert them; patches welcome!
The actual conversion command, if you want to just do it manually for now and
be done with it, is:
ssh-keygen -i -f /tmp/ssh2/YourName.pub > /tmp/openssh/YourName.pub
then use the resulting pubkey as you normally would in gitolite.
#### windows issues
On windows, I have only used msysgit, and the openssh that comes with it.
Over time, I have grown to distrust putty/plink due to the number of people
who seem to have trouble when those beasts are involved (I myself have never
used them for any kind of git access). If you have unusual ssh problems that
just don't seem to have any explanation, try removing all traces of
putty/plink, including environment variables, etc., and then try again.
Thankfully, someone contributed [this][putty].
### #stsapp1 appendix 1: ssh daemon asks for a password
> **NOTE**: This section should be useful to anyone trying to get
> password-less access working. It is not necessarily specific to gitolite,
> so keep that in mind if the wording feels a little more general than you
> were expecting.
You have generated a keypair on your workstation (`ssh-keygen`) and copied the
public part of it (`~/.ssh/id_rsa.pub`, by default) to the server.
On the server you have appended this file to `~/.ssh/authorized_keys`. Or you
ran something, like the `gitolite setup` step during a gitolite install, which
should have done that for you.
You now expect to log in without having to type in a password, but when you
try, you are being asked for a password.
This is a quick checklist:
* Make sure you're being asked for a password and not a pass*phrase*. Do
not confuse or mistake a prompt saying `Enter passphrase for key
'/home/sitaram/.ssh/id_rsa':` for a password prompt from the remote
server!
When you create an ssh keypair using `ssh-keygen`, you have the option of
protecting it with a passphrase. When you subsequently use that keypair
to access a remote host, your *local* ssh client needs to unlock the
corresponding private key, and ssh will probably ask for the passphrase
you set when you created the keypair.
You have two choices to avoid this prompt every time you try to use the
private key. The first is to create keypairs *without* a passphrase (just
hit enter when prompted for one). **Be sure to add a passphrase later,
once everything is working, using `ssh-keygen -p`**.
The second is to use `ssh-agent` (or `keychain`, which in turn uses
`ssh-agent`) or something like that to manage your keys. Other than
discussing one more potential trouble-spot with ssh-agent (see below),
further discussion of ssh-agent/keychain is out of scope of this document.
* Ssh is very sensitive to permissions. An extremely conservative setup is
given below, but be sure to do this on **both the client and the server**:
cd $HOME
chmod go-rwx .
chmod -R go-rwx .ssh
* Actually, every component of the path to `~/.ssh/authorized_keys` all the
way upto the root directory must be at least `chmod go-w`. So be sure to
check `/` and `/home` also.
* While you're doing this, make sure the owner and group info for each of
these components are correct. `ls -ald ~ ~/.ssh ~/.ssh/authorized_keys`
will tell you what they are.
* You may also want to check `/etc/ssh/sshd_config` to see if the "git" user
is allowed to login at all. For example, if that file contains an
`AllowUsers` config entry, then only users mentioned in that line are
allowed to log in!
* While you're in there, check that file does NOT have a setting for
`AuthorizedKeysFile`. See `man sshd_config` for details. This setting is
a show stopper for gitolite to use ssh.
* Some OSs/distributions require that the "git" user should have a password
and/or not be a locked account. You may want to check that as well.
* If all that fails, log onto the server as root, `cd /var/log`, and look
for a file called `auth.log` or `secure` or some such name. Look inside
this file for messages matching the approximate time of your last attempt
to login, to see if they tell you what is the problem.
### #sshkeys-lint appendix 2: which key is which -- running sshkeys-lint
The sshkeys-lint program can be run on the server or the client. Run it with
'-h' to get a help message.
On the server you can run `gitolite sshkeys-lint` and it will tell you, for
each key in the admin directory's keydir, what access is available. This is
especially good at finding duplicate keys and such.
To run it on the client you have to copy the file src/commands/sshkeys-lint
from some gitolite clone, then follow these steps:
* Get a copy of `~/.ssh/authorized_keys` from the server and put it in
`/tmp/foo` or something.
* cd to `~/.ssh`.
* Run `/path/to/sshkeys-lint *.pub < /tmp/foo`.
Note that it is not trying to log in or anything -- it's just comparing
fingerprints as computed by `ssh-keygen -l`.
If the pubkey file you're interested in appears to have the correct access to
the server, you're done with this step.
Otherwise you have to rename some keypairs and try again to get the effect you
need. Be careful:
* Do not just rename the ".pub" file; you will have to rename the
corresponding private key also (the one with the same basename but without
an extension).
* If you're running ssh-agent, you may have to delete (using `ssh-add -D`)
and re-add identities for it to pick up the renamed ones correctly.
#### typical cause(s)
The admin often has passwordless shell access to `git@server` already, and
then used that same key to get access to gitolite (i.e., copied that same
pubkey as YourName.pub and ran `gitolite setup` on it).
As a result, the same key appears twice in the authkeys file now, and since
the ssh server will always use the first match, the second occurrence (which
invokes gitolite) is ignored.
To fix this, you have to use a different keypair for gitolite access. The
best way to do this is to create a new keypair, copy the pubkey to the server
as YourName.pub, then run `gitolite setup -pk YourName.pub` on the server.
Remember to adjust your agent identities using ssh-add -D and ssh-add if
you're using ssh-agent, otherwise these new keys may not work.
### #stsapp3 appendix 3: ssh client may not be offering the right key
* Make sure the right private key is being offered. Run ssh in very
verbose mode and look for the word "Offering", like so:
ssh -vvv user@host pwd 2> >(grep -i offer)
If some keys *are* being offered, but not the key that was supposed to be
used, you may be using ssh-agent (next bullet). You may also need to
create some host aliases in `~/.ssh/config` ([appendix 4][ssh-ha]).
* (ssh-agent issues) If `ssh-add -l` responds with either "The agent has no
identities." or "Could not open a connection to your authentication
agent.", then you can skip this bullet.
However, if `ssh-add -l` lists *any* keys at all, then something weird
happens. Due to a quirk in ssh-agent, ssh will now *only* use one of
those keys, *even if you explicitly ask* for some other key to be used.
In that case, add the key you want using `ssh-add ~/.ssh/YourName` and try
the access again.
### #ssh-ha appendix 4: ssh host aliases
(or "making git use the right options for ssh")
The ssh command has several options for non-default items to be specified.
Two common examples are `-p` for the port number if it is not 22, and `-i` for
the public key file if you do not want to use just `~/.ssh/id_rsa` or such.
Git has two ssh-based URL syntaxes, but neither allows specifying a
non-default public key file. And a port number is only allowed in one of
them. (See `man git-clone` for details). Finally, hosts often have to be
referred with IP addresses (such is life), or the name is very long, or hard
to remember.
Using a "host" para in `~/.ssh/config` lets you nicely encapsulate all this
within ssh and give it a short, easy-to-remember, name. Example:
host gitolite
user git
hostname a.long.server.name.or.annoying.IP.address
port 22
identityfile ~/.ssh/id_rsa
Now you can simply use the one word `gitolite` (which is the host alias we
defined here) and ssh will infer all those details defined under it -- just
say `ssh gitolite` and `git clone gitolite:reponame` and things will work.
(By the way, the 'port' and 'identityfile' lines are needed only if you have
non-default values, although I put them in anyway just to be complete).
If you have *more than one* pubkey with access to the *same* server, you
**must** use this method to make git pick up the right key. There is no other
way to do this, as far as I know.
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial
### #ybpfail appendix 5: why bypassing gitolite causes a problem
When you bypass gitolite, you end up running your normal shell instead of the
special gitolite entry point script `gitolite-shell`.
This means commands (like 'info') are interpreted by the shell instead of
gitolite.
It also means git operations look for repos in `$HOME`.
However, gitolite places all your repos in a subdirectory pointed to by
`$REPO_BASE` in the rc file (default: `repositories`), and internally prefixes
this before calling the actual git command you invoked. Thus, the pathname of
the repo that you use on the client is almost never the correct pathname on
the server. (This is by design. Don't argue...)
This means that, you get 2 kinds of errors if you bypass gitolite
* When you use `git@server:reponame` with a key that bypasses gitolite
(i.e., gets you a shell), this prefixing does not happen, and so the repo
is not found. Neither a clone/fetch nor a push will work.
* Conversely, consider `git@server:repositories/reponame.git`. The clone
operation will work -- you're using the full Unix path, (assuming default
`$REPO_BASE` setting), and so the shell finds the repo where you said it
would be. However, when you push, gitolite's **update hook** kicks in,
and fails to run because some of the environment variables it is expecting
are not present.

86
doc/files.mkd Normal file
View file

@ -0,0 +1,86 @@
# files involved in gitolite
## install
Let's say you start from a totally clean slate:
$ pwd
/home/gl-test
$ ls -a
. .. .bash_logout .bash_profile .bashrc
You run `git clone git://github.com/sitaramc/gitolite`.
Now you have
$ ls -aF
./ ../ .bash_logout .bash_profile .bashrc gitolite/
$ ls -aF gitolite
./ ../ check-g2-compat* doc/ dot.pl .git/ install* src/ t/
If we were an existing user, we'd run the migration checker 'check-g2-compat'
(see [here][g2migr]). For now we're only interested in "gitolite/src", so:
$ ls -aF gitolite/src
./ ../ commands/ gitolite* Gitolite/ gitolite-shell* syntactic-sugar/ triggers/ VREF/
We check our PATH to make sure `$HOME/bin` is in it:
$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/gl-test/.local/bin:/home/gl-test/bin
Since it is, we run `mkdir bin; gitolite/install -ln`, and get:
$ mkdir bin
$ gitolite/install -ln
$ ls -aF
./ ../ .bash_logout .bash_profile .bashrc bin/ gitolite/
$ ls -aF bin
./ ../ gitolite@
$ file bin/gitolite
bin/gitolite: symbolic link to `/home/gl-test/gitolite/src/gitolite'
That's the install done. At the end of it, we have just *one* symlink in
`$HOME/bin`; everything else is in a different directory.
## setup
Now you copy your pubkey (typically `id_rsa.pub`) from your workstation to the
server, then run `gitolite setup -pk "yourname.pub"`. Which gives you this:
$ ls -aF
./ .bash_logout .bashrc gitolite/ .gitolite.rc repositories/ .ssh/
../ .bash_profile bin/ .gitolite/ projects.list sitaram.pub
$ ls -aF bin
./ ../ gitolite@
$ ls -aF .gitolite
./ ../ conf/ hooks/ keydir/ logs/
$ ls -aF repositories
./ ../ gitolite-admin.git/ testing.git/
$ ls -aF .ssh
./ ../ authorized_keys*
And that's the setup done. At the end of this step, you have
* `~/bin/gitolite` -- a symlink to `~/gitolite/src/gitolite`. The target of
this symlink tells gitolite where the rest of the code is.
* `~/gitolite/src` -- the rest of the code.
* `~/.gitolite` -- the gitolite "admin" directory. The only thing you
should directly touch here are the [log][] files and [hooks][]. (I.e., do
NOT change the conf or keydir contents here; see adding [users][] and
[repos][] for how to do that.
* `~/.gitolite.rc` -- the [rc][] file.
* `~/repositories` -- which contains all the repositories that gitolite will
be managing.
* `~/.ssh` -- which contains (at least) the `authorized_keys` file that
provides access to users. You can look inside that file and correlate it
with what the [ssh][] docs tell you, if you wish.

103
doc/g2incompat.mkd Normal file
View file

@ -0,0 +1,103 @@
# #g2incompat incompatibility with g2
This page expands on some incompatibilities that were only briefly mentioned
in the [migration][g2migr] page.
## #g2i-name NAME rules
1. NAME/ rules must be changed to VREF/NAME/
2. Fallthru on all VREFs is "success" now, so any NAME/ rules you have
**MUST** change the ruleset in some way to maintain the same restrictions.
The simplest is to add the following line to the end of each repo's rule
list:
- VREF/NAME/ = @all
## #g2i-subconf subconf command in admin repo
(This is also affected by the previous issue, 'NAME rules'; please read that
as well).
If you're using delegation in your admin conf setup, please add the following
lines to the end of the gitolite-admin rules in your conf/gitolite.conf file:
repo gitolite-admin
- VREF/NAME/ = @all
subconf "fragments/*.conf"
The first part compensates for fallthru now being a success when processing
[VREF][vref] rules (NAME rules are just one specific VREF). Although,
**ideally**, you should change your ruleset so that you no longer require that
line. As the [vref documentation][vref] says:
> **Virtual refs are best used as additional "deny" rules**, performing
> extra checks that core gitolite cannot.
The second part explicitly says when and where to include the subconf files.
(Before subconf was invented, this used to happen implicitly at the end of the
main conf file, and was hardcoded to that specific glob.)
## #g2i-gl-time gl-time for performance measurement
If you've been using gl-time for performance measurement, there's a much
better system available now.
gl-time used to only log elapsed time. The new 'CpuTime' trigger module
shipped with gitolite, if enabled in the rc file, can also report CPU times
using perl's 'times()' function. See comments within that file and in the
default rc file that contain the word "cpu", for more details.
Further, you can copy that module with a different name, add your own
functionality, and invoke *that* from the rc file instead.
## #g2i-mirroring changes in mirroring setup
There are several changes with regard to mirroring:
* There is no 'post-receive' hook to be installed. Mirroring is handled by
g3's [triggers][] mechanism. Gitolite triggers are enabled by adding (or
uncommenting, in this case) appropriate lines in the rc file.
* The `GL_HOSTNAME` variable is now `HOSTNAME`. (Note that the rc file
syntax itself has changed quite a bit; to be accurate, HOSTNAME is not a
variable but a hash key with an associated value).
* The `GL_GITCONFIG_KEYS` variable is now `GIT_CONFIG_KEYS`, **but** you no
longer need to set it to anything for mirroring to work.
* The `gl-tool` program does not exist anymore. Adding keys for peer
servers is done just like adding user keys, except that the pubkey file
name must start with `server-`. For example, to add a peer host called
frodo, you will acquire its pubkey and add it as `server-frodo.pub`.
* The config variables are quite different now. The main ones now look like
this:
option mirror.master = sam
option mirror.slaves = frodo gollum
The redirectOK looks like this:
option mirror.redirectOK = frodo
The special value "true" to say that all slaves are trusted is now "all":
option mirror.redirectOK = all
* There are no more mirroring "keys", (lists of servers named in config keys
like 'gitolite.mirror.nightly', etc). You can certainly add lines like
option mirror.nightly = merry pippin
but they will not be processed by gitolite. Your cron jobs should use
`gitolite git-config` to query this variable, grab the list of repos, and
run `gitolite mirror` on each of them.
* The external command to resync mirrors is 'mirror', run just like any
other [command][commands]. In particular, you can run `gitolite mirror
-h` to get help. It cannot be run from a slave to ask a master to push
(unlike in the old system) but what's more convenient is that any user who
has any access to the master can run it remotely (if you allow it) to
invoke a push.

269
doc/g2migr-example.mkd Normal file
View file

@ -0,0 +1,269 @@
# migration example
This shows what a typical migration would look like, using a test setup.
[[TOC]]
## existing setup
The existing gitolite is the latest in the "g2" (v2.x) branch.
First, the rc file has the following lines different from the default:
-$GL_WILDREPOS = 0;
+$GL_WILDREPOS = 1;
-$GL_GITCONFIG_KEYS = "";
+$GL_GITCONFIG_KEYS = ".*";
Next, the conf/gitolite.conf file in `~/.gitolite`:
repo gitolite-admin
RW+ = tester u1
repo testing
RW+ = @all
repo foo
RW+ = u1 u2
RW+ NAME/ = u1
RW+ NAME/u2 = u2
repo bar
RW = u2
repo baz/..*
C = u3 u4
RW+ = CREATOR
config foo.bar = baz
(Note that this conf file has NAME/ rules, which **have changed**
significantly in g3; see [here][g2i-name] for details).
These are the repos already existing
$ find repositories -name "*.git" | sort
repositories/bar.git
repositories/baz/u3.git
repositories/baz/u4.git
repositories/baz/uthree.git
repositories/foo.git
repositories/gitolite-admin.git
repositories/testing.git
The config entries exist for all the baz/ repos:
$ grep -2 foo `find repositories -name "config" `
repositories/baz/uthree.git/config-[gitweb]
repositories/baz/uthree.git/config- owner = u3
repositories/baz/uthree.git/config:[foo]
repositories/baz/uthree.git/config- bar = baz
--
repositories/baz/u4.git/config-[gitweb]
repositories/baz/u4.git/config- owner = u4
repositories/baz/u4.git/config:[foo]
repositories/baz/u4.git/config- bar = baz
--
repositories/baz/u3.git/config-[gitweb]
repositories/baz/u3.git/config- owner = u3
repositories/baz/u3.git/config:[foo]
repositories/baz/u3.git/config- bar = baz
## preparing for the migration
### getting g3
Fortunately this is easy here; I just happened to have the repo already
fetched so I just had to switch branches. You may have to 'git clone ...'
from github.
$ cd gitolite
$ git checkout master
Branch master set up to track remote branch master from origin.
Switched to a new branch 'master'
### run check-g2-compat
This is a quick and dirty program to catch some of the big issues.
$ cd
$ gitolite/check-g2-compat
INFO This program only checks for uses that make the new g3 completely unusable
or that might end up giving *more* access to someone if migrated as-is.
It does NOT attempt to catch all the differences described in the docs.
INFO 'see docs' usually means doc/g2migr.mkd
(online at http://sitaramc.github.com/gitolite/g3/g2migr.html)
checking rc file...
NOTE GL_ADMINDIR is in the right place; assuming you did not mess with
GL_CONF, GL_LOGT, GL_KEYDIR, and GL_CONF_COMPILED
checking conf file(s)...
SEVERE NAME rules; see docs
checking repos...
WARNING found 3 gl-creater files; see docs
...all done...
## the actual migration
Here's the actual migration, step by step
### step 1
$ ls -a bin
. gl-admin-push gl-install gl-setup-authkeys gl-VREF-DUPKEYS
.. gl-auth-command gl-mirror-push gl-system-install gl-VREF-EMAIL_CHECK
gitolite_env.pm gl-compile-conf gl-mirror-shell gl-time gl-VREF-FILETYPE
gitolite.pm gl-conf-convert gl-query-rc gl-tool gl-VREF-MERGE_CHECK
gitolite_rc.pm gl-dryrun gl-setup gl-VREF-COUNT sshkeys-lint
$ rm -rf bin;mkdir bin
$ grep GL_PACKAGE .gitolite.rc
$GL_PACKAGE_CONF = "/home/g3/share/gitolite/conf";
$GL_PACKAGE_HOOKS = "/home/g3/share/gitolite/hooks";
$ rm -rf share
$GL_PACKAGE_HOOKS = "/home/g3/share/gitolite/hooks";
$ rm -rf share
$ mv .gitolite.rc old.grc
(still on step 1, this is substep 3) notice we are cloning **on the server**,
using a **full path** to the repo.
$ git clone repositories/gitolite-admin.git old.ga
Cloning into 'old.ga'...
done.
$ rm -rf repositories/gitolite-admin.git/
Since I'm not interested in preserving the logs and don't have any custom
hooks:
$ rm -rf .gitolite
### step 2
I have no variables that *must* be preset, since the report by
`check-g2-compat` is clear.
### step 3
Here we install the new gitolite. Remember we already got the new software
(in order to run 'check-g2-compat').
Just check that bin is empty, then run 'install -ln' from the gitolite source
tree:
$ ls -al bin
total 8
drwxrwxr-x 2 g3 g3 4096 Apr 24 10:57 .
drwx------ 8 g3 g3 4096 Apr 24 10:59 ..
$ gitolite/install -ln
$ ls -al bin
total 8
drwxrwxr-x 2 g3 g3 4096 Apr 24 11:01 .
drwx------ 8 g3 g3 4096 Apr 24 10:59 ..
lrwxrwxrwx 1 g3 g3 30 Apr 24 11:01 gitolite -> /home/g3/gitolite/src/gitolite
OK that went well. Now setup gitolite. You don't need a key here; just use a
random name:
$ gitolite setup -a admin
Initialized empty Git repository in /home/g3/repositories/gitolite-admin.git/
### step 4
Now go to your old clone, and push it:
$ cd old.ga
$ gitolite push -f
...usual git progress output deleted...
remote: FATAL: git config foo.bar not allowed
remote: check GIT_CONFIG_KEYS in the rc file
To /home/g3/repositories/gitolite-admin.git
+ 7eb8163...1474770 master -> master (forced update)
Aaha! I forgot to set `CONFIG_KEYS` (new name for `GL_GIT_CONFIG_KEYS`) in
the new rc file so fix that:
$ vim ~/.gitolite.rc
(edit and set it to `.*` for now)
and push again:
$ gitolite push -f
Everything up-to-date
Damn. We have to make a dummy commit to allow the push to do something.
But wait! We forgot fix the [NAME/][g2i-name] rules, so may as well fix
those, add, and push:
$ vim conf/gitolite.conf
# change all NAME/ to VREF/NAME/
# append a '- VREF/NAME/ = @all' at the end
# save
git add conf
$ git commit -m name-rules
... some output for add...
$ gitolite push -f
Counting objects: 1, done.
Writing objects: 100% (1/1), 181 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (1/1), done.
To /home/g3/repositories/gitolite-admin.git
1474770..4c2b41d master -> master
### step 5
The only thing left is to fix up the gl-creater files:
$ cd $HOME/repositories
$ find . -type d -name "*.git" -prune | while read r
> do
> mv $r/gl-creater $r/gl-creator
> done 2>/dev/null
And we're done!
## checking things out
Let's see what repos u3 has:
ssh u3 info
hello u3, this is g3@sita-lt running gitolite3 v3.0-11-g090b0f5 on git 1.7.7.6
C baz/..*
R W baz/u3
R W baz/uthree
R W gitolite-admin
R W testing
That's a combination of 'info' and 'expand', by the way. There is no expand
command any more.
How about adding a new repo and checking if the config entries made it?
$ git ls-remote u4:baz/ufour
Initialized empty Git repository in /home/g3/repositories/baz/ufour.git/
$ grep -A1 foo `find repositories -name "config" `
repositories/baz/u3.git/config:[foo]
repositories/baz/u3.git/config- bar = baz
--
repositories/baz/u4.git/config:[foo]
repositories/baz/u4.git/config- bar = baz
--
repositories/baz/ufour.git/config:[foo]
repositories/baz/ufour.git/config- bar = baz
--
repositories/baz/uthree.git/config:[foo]
repositories/baz/uthree.git/config- bar = baz
And there it is, in the second block of lines...
And now we're really done.

277
doc/g2migr.mkd Normal file
View file

@ -0,0 +1,277 @@
# #g2migr migrating from g2
<font color="red"> **This document is a *MUST* read if you are currently using
g2 and want to move to g3.** </font>
----
[[TOC]]
----
First things first: g2 will be supported for a good long time for critical
bugs, although enhancements and new features won't happen.
Migration should be straightforward, but it is not automatic. The biggest
differences are in the rc file, mirroring, "NAME/" rules, and delegation.
> ----
> **Presetting the rc file**
> Some rc settings in the older gitolite are such that you cannot directly
> run `gitolite setup` when you're ready to migrate. **Doing that will
> clobber something important**. See [presetting the rc file][rc-preset]
> for details.
> ----
The `check-g2-compat` program attempts to identify any *big* issues you will
be facing; run that first. See [later][cg2c] in this page for what its
messages mean. If it does not report any issues, your migrate will probably
go quickly. I still suggest you go through the links below in case that
program missed something.
## incompatible features
Here's a list of incompatible features and what you need to do to migrate.
Some of them have links where there is more detail than I want to put here.
### high impact
(serious loss of functionality and/or access control compromised)
* [`NAME/`][g2i-name] rules: thes need to change to `VREF/NAME/`, and you
need to add a deny rule at the end because fallthru is "success" for all
[VREFs][vref] now, including the "NAME" VREF.
* [subconf][g2i-subconf]: if you're using [delegation][deleg], there is no
implicit "subconf" at the end; you'll have to add it in.
* There are several important differences in mirroring. You can start from
scratch by reading the new [mirroring][mirroring] doc or
[migrate][g2i-mirroring] (carefully!).
* `ADMIN_POST_UPDATE_CHAINS_TO` -- **dropped**. Add your script to the
`POST_COMPILE` trigger chain. Your script won't be getting the arguments
that *git* sends to the post-update hook, but for the admin repo the only
argument that even comes in (or is significant) is "refs/heads/master"
anyway.
* `GL_ALL_INCLUDES_SPECIAL` -- **dropped**, **requires presetting**.
@all always includes gitweb and daemon now. Use [deny-rules][] if you
want to say `R = @all` but not have the repo(s) be visible to gitweb or
daemon.
* `GL_NO_CREATE_REPOS` -- **dropped**. If you think you need this, email
me. I know one group who does need this so I will be putting it in
eventually but not right away. It's almost certain to be renamed anyway.
* `GL_NO_DAEMON_NO_GITWEB` **dropped**, **requires presetting**. Default
will clobber your projects.list file and git-daemon-export-ok files.
Comment out the appropriate lines in the rc file, in the `POST_COMPILE`
*and* the `POST_CREATE` trigger sections. As a bonus, gitweb and daemon
can now be separately disabled, instead of both being tied to the same
setting.
* `GL_NO_SETUP_AUTHKEYS` **dropped**, **requires presetting**. Default will
clobber your authkeys file.
Comment out all the line(s) that call ssh-authkeys in the rc file.
* `UPDATE_CHAINS_TO` **dropped**, **requires presetting**. Default will
fail to run this extra check when users push.
Use a [vref][] instead. You can directly use any existing chained-to
script as a VREF; they'll work. Don't forget to add a rule that
references the new VREF!
* `GIT_PATH` **dropped**, **requires presetting**.
If you need its functionality, add these lines to the end of the rc file:
$ENV{PATH}="...whatever you want...";
1;
### medium impact
(important functionality lost, but access control not compromised)
* `GL_ADMINDIR` -- **dropped**, is now at a fixed location: `~/.gitolite`.
If you want it somewhere else go ahead and move it, then place a symlink
from the assumed location to the real one.
* `GL_GET_MEMBERSHIPS_PGM` -- is now `GROUPLIST_PGM`, see
[here][ldap].
* `GL_WILDREPOS_DEFPERMS` -- is now `DEFAULT_ROLE_PERMS`.
* `REPO_BASE` -- **dropped**, is now at a fixed location: `~/repositories`.
If you want it somewhere else go ahead and move it, then place a symlink
from the assumed location to the real one.
### low impact
(ancillary, non-core, or minor functionality lost)
* Built-in command `expand` -- **dropped**. The 'info' command shows you
both normal and wild repos now. The output format is also much simpler.
* Built-in commands 'getperms', 'setperms' -- **merged** into external
command 'perms'. Run `ssh git@host perms -h` for details.
Similarly, 'getdesc' and 'setdesc' have been merged into 'desc'.
* Several 'ADC's -- see the [dev-status][] page for more on this.
* [gl-time][g2i-gl-time]: the CpuTime module replaces gl-time.
* `BIG_INFO_CAP` -- **dropped**. If you think you must have this, try it
without and see if there's a difference. If you *know* you need this,
convince me.
* `GL_ADC_PATH` -- **dropped**. It is obsolete; use [commands][] or add
[your own][dev-notes].
* `GL_ALL_READ_ALL` -- **dropped**. If you think you must have this, try it
without and see if there's a difference. If you *know* you need this,
convince me.
* `GL_BIG_CONFIG` -- **dropped**. This feature is default now.
* `GL_CONF`, `GL_CONF_COMPILED`, and `GL_KEYDIR` -- **dropped**. You had no
business touching these anyway; if you did, move them into the expected
default locations before attempting to run `gitolite setup`
* `GL_GITCONFIG_KEYS` -- is now `GIT_CONFIG_KEYS`.
* `GL_LOGT` -- now has a fixed value; email me if this is a problem.
* `GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS` -- **dropped**. They are not
needed anymore, but check if you had any custom hooks set in the latter
location and copy them across.
* `GL_PERFLOGT` -- **dropped**. See [gl-time][g2i-gl-time].
* `GL_SITE_INFO` -- is now `SITE_INFO`.
* `GL_WILDREPOS` -- **dropped**. This feature is default now.
* `GL_WILDREPOS_PERM_CATS` -- is now the ROLES hash in the rc file.
* `HTPASSWD_FILE`, `RSYNC_BASE`, `SVNSERVE` -- need work. Email me if you
are using any of these.
* `NICE_VALUE` -- **dropped**. Uncomment the 'renice 10' line in the rc
file. You can also change the 10 to something else if you wish.
* `PROJECTS_LIST` -- is now `GITWEB_PROJECTS_LIST`. More importantly, it is
only used by update-gitweb-access-list in src/commands/post-compile. This
variable now has nothing to do with gitolite core, and the rc is just
helping to store settings for external programs like that one.
* `REPO_UMASK` -- is now `UMASK`.
* `WEB_INTERFACE` and `GITWEB_URI_ESCAPE` -- **dropped**. Patches to the
update program to directly do those things are welcome. Personally, I
think people who use spaces and other funky characters in dir/file names
should be shot but no one listens to me anyway.
## #cg2c using the "check-g2-compat" program
This program checks a few things only, not everything. In particular, it
looks for settings and status that might:
* make g3 unusable for lots of users
* make g3 give *more* access than g2 under some conditions
It does NOT look for or warn about anything else; you're expected to read (and
act upon, if needed) the rest of the migration guide links given a few paras
above to cover everything else.
Here's an explanation of those messages that the check-g2-compat program may
put that contain the words "see docs":
* `GL_ADMINDIR in the wrong place -- aborting`
It expects to find `GL_ADMINDIR` and `REPO_BASE` pointing to the right
places. It aborts if these conditions are not met and does not scan
further since that sort of guesswork is not good. If you are in that
position, make a symlink from the real location to the expected location,
change the RC accordingly, and re-try.
* `REPO_BASE in the wrong place -- aborting`
same as above
* `NAME rules`
**This is a significant difference and affects access badly (gives access
that would otherwise not be given)**. Please see the [list of non-RC
incompatibilities][g2incompat].
* `subconf command in admin repo`
This is not so bad security wise but it might *reduce* access by not
processing files you intended to. Again, see the same link as in the
previous bullet.
* `mirroring used`
There have been quite a few changes to mirroring. You can start from
scratch by reading the new [mirroring][mirroring] doc or
[migrate][g2i-mirroring] (carefully!).
* `found N gl-creater files`
These need to be renamed to `gl-creator` (the correct spelling at last,
hooray!). Suggested command sequence:
cd $HOME/repositories
find . -type d -name "*.git" -prune | while read r
do
mv $r/gl-creater $r/gl-creator
done 2>/dev/null
Once you do this, the g2 will not work completely unless you change them
back.
* `found N gl-perms files with R or RW`
Setting perms of R and RW will no longer work; you have to say READERS and
WRITERS now. Suggested command:
## #rc-preset presetting the rc file
Some rc settings in the older gitolite are such that you cannot directly run
`gitolite setup` when you're ready to migrate. **Doing that will clobber
something important**. You have to create a default rc file, edit it
appropriately, and *then* run `gitolite setup`.
The most serious example of this is `GL_NO_SETUP_AUTHKEYS`, which tells the
(old) gitolite that you want to manage `~/.ssh/authorized_keys` yourself and
it should not fiddle with it.
If you don't preset the rc (in this case, by commenting out the 'ssh-authkeys'
line) **before** running `gitolite setup`, **your `~/.ssh/authorized_keys`
file will get clobbered**.
The actual rc settings that require presetting are listed in the "high
impact" section above. This section tells you how to do the presetting.
* save your old (g2) rc file somewhere, just in case
* run
gitolite print-default-rc > $HOME/.gitolite.rc
* edit the file
${EDITOR:-vim} $HOME/.gitolite.rc
make appropriate changes as described elsewhere in this migration guide,
and save it.
* *then* you can run [gitolite setup][setup].

97
doc/g3why.mkd Normal file
View file

@ -0,0 +1,97 @@
# why a completely new version?
Gitolite started life as 400 lines of perl written on a weekend because I was
quickly losing control of my projects at work, exacerbated by git newbies
doing all the wrong things. I really needed it!
That little 400 line thing is now a huge bunch of programs that do all sorts
of things (I mean, rsync access control in a git related program? WTF!),
because it kinda just *grew* while I wasn't looking.
So, briefly, here are the advantages of g3:
* compile versus everything else
g2's "compile" script was doing way, way too much. For example, dealing
with gitweb and git-daemon was a good chunk of code in g2. In contrast,
here's how g3 generates gitweb's projects.list file:
(
gitolite list-phy-repos | gitolite access % gitweb R any | grep -v DENIED
gitolite list-phy-repos | gitolite git-config -r % gitweb\\.
) |
cut -f1 | sort -u | sed -e 's/$/.git/' > $plf
* core versus non-core
That's just the tip of the iceberg. The commands above run from a script
that is itself outside gitolite, and can be enabled and disabled from the
rc file. There are six different "events" within gitolite that can
trigger external programs, with specific arguments passed to them, much
like git's own hooks. The example you saw is called from the
"POST_COMPILE" trigger.
And as you can see, these programs be in any language.
* get/set perms/desc, and ADCs
I've always wanted to kick setperms out of core and make it an ADC.
Sadly, it couldn't be done because when you update your repo's permissions
using setperms, that can affect gitweb/daemon access, which -- you guessed
right -- feeds back into the main code in complex ways. It *had* to be an
"inside job".
But now, the new 'perms' program is quite external to gitolite. And how
does it fix up gitweb/daemon permissions after it is done updating the
"gl-perms" file?
system("gitolite", "trigger", "POST_CREATE");
* syntax versus semantics
I got tired of people asking things like "why can't I have
backslash-escaped continuation lines?" I designed it differently because
I don't like them but perhaps it's reasonable for some people.
Someone else wanted to use subdirectories of 'keydir' as group names. Why
not?
G3 comes with a stackable set of "syntactic sugar" helpers. And you can
write your own, though they do have to be in perl (because they're not
standalone programs).
Once the code is written and placed in the right place, all a site has to
do to enable it is to uncomment some lines in the rc file:
# these will run in sequence during the conf file parse
SYNTACTIC_SUGAR =>
[
# 'continuation-lines',
# 'keysubdirs-as-groups',
<etc>
* roll your own
Having a decent shell API helps enormously. You saw an example above but
how about if your boss asks you "I need a list of everyone who *currently*
has read access to the 'foo' repo"?
Sure you could look in conf/gitolite.conf, all its include files (if you
have any), and if the repo is user-created, then in its gl-perms.
Or you could do something like this:
gitolite list-users | gitolite access foo % R any | cut -f1
* over-engineered
g2 was, to some extent, over-engineered. One of the best examples is the
documentation on hook-propagation in g2, which required even a *picture*
to make clear (always a bad sign). In g3, the [hooks][] section is 4
sentences.
Anyway you get the idea.
The point is not that you can do all these cool tricks. The point is they are
possible because of the redesign. There is no way on God's green earth I
could have done this with the old code.

66
doc/git-config.mkd Normal file
View file

@ -0,0 +1,66 @@
# "git-config" keys and values
Here's all you want to know about setting repo-specific git-config values.
(Original version thanks to teemu dot matilainen at iki dot fi)
> ----
> **Note**: this won't work unless the rc file has the right settings;
> please see `$GIT_CONFIG_KEYS` in the [rc file doc][rc].
> ----
The syntax is simple:
config sectionname.keyname = [optional value_string]
For example:
repo gitolite
config hooks.mailinglist = gitolite-commits@example.tld
config hooks.emailprefix = "[gitolite] "
config foo.bar = ""
config foo.baz =
This does either a plain "git config section.key value" (for the first 2
examples above) or "git config --unset-all section.key" (for the last 2
examples). Other forms of the `git config` command (`--add`, the
`value_regex`, etc) are not supported.
> ----
> **WARNING**: simply deleting the config line from the `conf/gitolite.conf`
> file will *not* delete the variable from `repo.git/config`. The syntax in
> the last example is the *only* way to make gitolite execute a
> `--unset-all` operation on the given key.
> ----
You can also use the special value `%GL_REPO` in the string to save typing:
repo foo bar baz
config hooks.mailinglist = %GL_REPO-commits@example.tld
config hooks.emailprefix = "[%GL_REPO] "
You can repeat the 'config' line as many times as you like, and the last
occurrence will be the one in effect. This allows you to override settings
just for one project, as in this example:
repo @all
config hooks.mailinglist = %GL_REPO-commits@example.tld
config hooks.emailprefix = "[%GL_REPO] "
repo customer-project
# different mailing list
config hooks.mailinglist = announce@customer.tld
The "delete config variable" syntax can also be used, if you wish:
repo secret # no emails for this one please
config hooks.mailinglist =
config hooks.emailprefix =
As you can see, the general idea is to place the most generic ones (`repo
@all`, or repo patterns like `repo foo.*`) first, and place more specific ones
later to override the generic settings.

30
doc/groups.mkd Normal file
View file

@ -0,0 +1,30 @@
# group definitions
You can group repos or users for convenience. The syntax is the same for both
and does not distinguish; until you *use* the group name it could really be
either.
Here's an example:
@developers = dilbert alice wally
Group definitions accumulate; this is the same as the above:
@developers = dilbert
@developers = alice
@developers = wally
You can use one group in another group definition; the values will be expanded
right there (meaning later additions will not appear in the second group).
@developers = dilbert alice
@interns = ashok
@staff = @interns @developers
@developers = wally
# wally is NOT part of @staff
## special group `@all`
`@all` is a special group name that is often convenient to use if you really
mean "all repos" or "all users".

125
doc/gsmigr.mkd Normal file
View file

@ -0,0 +1,125 @@
# migrating from gitosis to gitolite
[2012-04-09] Modified for gitolite g3. These instructions have not really
been tested with g3, but are expected to work.
Migrating from gitosis to gitolite is fairly easy, because the basic design is
the same.
There's only one thing that might trip up people: the userid. Gitosis uses
`gitosis`. Gitolite can use any userid you want; most of the documentation
uses `git`, while DEB/RPM packages use `gitolite`.
Here are the steps on the server:
* (as 'gitosis' on the server) **Rename** `~/.ssh/authorized_keys` to
something else so that no one can accidentally push while you're doing
this.
* (as 'gitosis' on the server) For added safety, **delete** the post-update
hook that gitosis-admin installed
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
post-update`.
* (as 'gitosis' on the server) If you already use the `update` hook for some
reason, you'll have to make that a [VREF][vref]. This is because gitolite
uses the update hook for checking write access.
* (as 'root' on the server) copy all of `~/repositories` to the gitolite
hosting user's home directory. Something like
cp -a /home/gitosis/repositories /home/git
chown -R git.git /home/git/repositories
* (as 'root' and/or 'git' on the server) Follow instructions to install
gitolite; see the [install document][install].
This will give you a gitolite config that has the required entries for the
"gitolite-admin" repo.
Now, log off the server and get back to the client. All subsequent
instructions are to be read as "on gitolite admin's workstation".
* **Clone** the new gitolite-admin repo to your workstation. (You already
have a clone of the gitosis-admin repo so now you have both).
* **Convert** your gitosis config file and append it to your gitolite 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`. (The
convert-gitosis-conf program is a standalone program that you can bring
over from any gitolite clone; you don't have to install all of gitolite on
your workstation to use this):
./convert-gitosis-conf < $GSAC/gitosis.conf >> $GLAC/conf/gitolite.conf
**Be sure to check the file to make sure it converted correctly** -- due
it's "I only need it once" nature, this program has not received too much
attention from anyone!
* Remove the entry for the 'gitosis-admin' repo. You do not need it here
and it may cause confusion.
* **Copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
cp $GSAC/keydir/* $GLAC/keydir
If your gitosis-admin key was `you@machine.pub`, and you supplied the same
one to gitolite's gl-setup program as `you.pub` when you installed
gitolite, then you should remove `you@machine.pub` from the new keydir
now. Otherwise you will have 2 pubkey files (`you.pub` and
`you@machine.pub`) which are identical, which is *not* a good idea.
Similarly, you should replace all occurrences of `you@machine.pub` with
`you` in the `conf/gitolite.conf` file.
* **IMPORTANT**: if you have any users with names like `user@foo`, where the
part after the `@` does *not* have a `.` in it (i.e., does not look like
an email address), you need to change them, because gitolite uses that
syntax for [enabling multi keys][multi-key].
You have two choices in how to fix this. You can change the gitolite
config so that all mention of `user@foo` is changed to just `user`.
Or you can change each occurrence of `user@foo` to, say, `user_foo` *and*
change the pubkey filename in keydir/ also the same way (`user_foo.pub`).
Just to repeat, you do NOT need to do this if the username was like
`user@foo.bar`, i.e., the part after the `@` had a `.` in it, because then
it looks like an email address.
* **IMPORTANT: expand any multi-key files you may have**. Gitosis is happy
to accept files containing more than one public key (one per line) and
assign all the keys to the same user. Gitolite does not allow that; see
[here][multi-key]'s for how gitolite handles multi-keys.
So if you had any multi-keys in gitosis, they need to be carefully split
into individual keys.
You can split the keys manually, or use the following code (just
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
do
i=1
cat $b|while read l
do
echo "$l" > ${b%.pub}@$i.pub
(( i++ ))
done
mv $b $b.done
done
This will split each multi-key file (say "sitaram.pub") into individual
files called "sitaram@1.pub", "sitaram@2.pub", etc., and rename the
original to "sitaram.pub.done" so gitolite won't pick it up.
At this point you can rename the split parts more appropriately, like
"sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check
the files to make sure this worked properly*
* Check all your changes to your gitolite-admin clone, commit, and push.

157
doc/http.mkd Normal file
View file

@ -0,0 +1,157 @@
# how to setup gitolite to use smart http mode
**Note**: "smart http" refers to the feature that came with git 1.6.6, late
2009 or so. The base documentation for this is `man git-http-backend`. Do
**NOT** read `Documentation/howto/setup-git-server-over-http.txt` and think
that is the same or even relevant -- that is from 2006 and is quite different
(and arguably obsolete).
## WARNINGS and important notes
* Please read [authentication versus authorisation][auth] first, and make
sure you understand what is gitolite's responsibility and what isn't.
* I have tested this only on stock Fedora 16; YDMV.
## assumptions:
* Apache 2.x and git installed.
* Httpd runs under the "apache" userid; adjust instructions below if not.
* Similarly for "/var/www" and other file names/locations.
## instructions
The detailed instructions I used to have in g2 have now been replaced by a
script called `t/smart-http.root-setup`. **Do NOT run this script as is -- it
is actually meant for my testing setup and deletes stuff**. However, it does
provide an excellent (and working!) narration of what you need to do to
install gitolite in smart http mode.
Make a copy of the script, go through it carefully, (possibly removing lines
that delete files etc.), change values per your system, and only then run it.
<font color="gray">Note that the `GIT_PROJECT_ROOT` variable (see "man
git-http-backend") is no longer optional. Make sure you set it to some place
outside apache's `DOCUMENT_ROOT`.</font>
## Making repositories available to both ssh and http mode clients
This section has been contributed by Thomas Hager (duke at sigsegv dot at).
Assumptions:
* Apache 2.x with CGI and Suexec support installed.
* Git and Gitolite installed with user "git" and group "git", and pubkey SSH
access configured and working.
* Git plumbing installed to /usr/libexec/git-core
* Gitolite base located at /opt/git
* Apache `DOCUMENT_ROOT` set to /var/www
* Apache runs with user www and group www
Please adjust the instructions below to reflect your setup (users and paths).
Edit your .gitolite.rc and add
$ENV{PATH} .= ":/opt/git/bin";
at the very top (as described in `t/smart-http.root-setup`).
Next, check which document root your Apache's suexec accepts:
# suexec -V
-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="www"
-D AP_LOG_EXEC="/var/log/apache/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=100
-D AP_USERDIR_SUFFIX="public_html"
We're interested in `AP_DOC_ROOT`, which is set to `/var/www` in our case.
Create a `bin` and a `git` directory in `AP_DOC_ROOT`:
install -d -m 0755 -o git -g git /var/www/bin
install -d -m 0755 -o www -g www /var/www/git
`/var/www/git` is just a dummy directory used as Apache's document root (see below).
Next, create a shell script inside `/var/www/bin` named `gitolite-suexec-wrapper.sh`,
with mode **0700** and owned by user and group **git**. Add the following content:
#!/bin/bash
#
# Suexec wrapper for gitolite-shell
#
export GIT_PROJECT_ROOT="/opt/git/repositories"
export GITOLITE_HTTP_HOME="/opt/git"
exec ${GITOLITE_HTTP_HOME}/gitolite-source/src/gitolite-shell
Edit your Apache's config to add http pull/push support, preferably in
a dedicated `VirtualHost` section:
<VirtualHost *:80>
ServerName git.example.com
ServerAlias git
ServerAdmin you@example.com
DocumentRoot /var/www/git
<Directory /var/www/git>
Options None
AllowOverride none
Order allow,deny
Allow from all
</Directory>
SuexecUserGroup git git
ScriptAlias /git/ /var/www/bin/gitolite-suexec-wrapper.sh/
ScriptAlias /gitmob/ /var/www/bin/gitolite-suexec-wrapper.sh/
<Location /git>
AuthType Basic
AuthName "Git Access"
Require valid-user
AuthUserFile /etc/apache/git.passwd
</Location>
</VirtualHost>
This Apache config is just an example, you probably should adapt the authentication
section and use https instead of http!
Finally, add an `R = daemon` access rule to all repositories you want to
make available via http.
## usage
### client side
Git URLs look like `http://user:password@server/git/reponame.git`.
The custom commands, like "info", "expand" should be handled as follows. The
command name will come just after the `/git/`, followed by a `?`, followed by
the arguments, with `+` representing a space. Here are some examples:
# ssh git@server info
curl http://user:password@server/git/info
# ssh git@server info repopatt
curl http://user:password@server/git/info?repopatt
# ssh git@server info repopatt user1 user2
curl http://user:password@server/git/info?repopatt+user1+user2
With a few nice shell aliases, you won't even notice the horrible convolutions
here ;-) See t/smart-http for a couple of useful ones.
### server side
The 'gitolite' command (for example, 'gitolite compile', 'gitolite query-rc',
and so on) *can* be run on the server, but it's not straightforward. Assuming
you installed exactly as given in this document, you should
* get a shell by using, say, `su -s /bin/bash - apache`
* run `export HOME=$HOME/gitolite-home`
* run `export PATH=$PATH:$HOME/bin`
and *then* you can run `gitolite <subcommand>`

66
doc/index.mkd Normal file
View file

@ -0,0 +1,66 @@
# Hosting git repositories
Gitolite allows you to setup git hosting on a central server, with
fine-grained access control and many more powerful features.
Here's more on [what][] it is and [why][] you might need it.
<font color="gray">
## #g2 (for older gitolite (v1.x and v2.x) users)
For users of gitolite v2.x (call it "g2" for convenience),
* [Why][g3why] I rewrote gitolite.
* Development [status][dev-status].
* Information on [migrating][migr] from g2 to g3.
</font>
## #ql quick links
* [Trying][trying] out gitolite.
* Minimum [requirements][req].
* Here's how to do a [quick install, setup, and clone][qi].
* Don't know ssh well enough? [Learn][ssh]. It's **IMPORTANT**.
* Add [users][] and [repos][].
* Learn about fine-grained access control with the [conf][] file.
* Explain gitolite to your [users][user].
The rest of the documentation is in the "master index" link at the top of each
page on this website. This is the first place you should search when looking
for specific information.
## #what What is gitolite?
Gitolite is an access control layer on top of git. Here are the features that
most people see:
* Use a single unix user ("real" user) on the server.
* Provide access to many gitolite users:
* they are not "real" users,
* they do not get shell access.
* Control access to many git repositories:
* read access controlled at the repo level,
* write access controlled at the branch/tag/file/directory level,
including who can rewind, create, and delete branches/tags.
* Can be installed without root access, assuming git and perl are already
installed.
* Authentication is most commonly done using sshd, but you can also use
[http][] if you prefer (this may require root access).
## #contact contact
* author: sitaramc@gmail.com, sitaram@atc.tcs.com
* mailing list: gitolite@googlegroups.com
* list subscribe address : gitolite+subscribe@googlegroups.com
* IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
time zone).
## #license license
The gitolite *code* is released under GPL v2. See COPYING for details.
The gitolite documentation is provided under a [Creative Commons
Attribution-NonCommercial-ShareAlike 3.0 Unported
License](http://creativecommons.org/licenses/by-nc-sa/3.0/).

265
doc/install.mkd Normal file
View file

@ -0,0 +1,265 @@
# installing gitolite
<font color="red">**NOTE**: if you're migrating from g2, there are some
settings that MUST be dealt with **before** running `gitolite setup`; please
start [here][migr]. RTFM is *mandatory* for migrations.</font>
----
This is the first step in using gitolite, and happens on the server. It is
followed by [setup][], then [clone][].
----
[[TOC]]
----
## notes and naming conventions
Gitolite uses a single "real" (i.e., unix) user to provide secure access to
git repos to any number of "virtual" users, without giving them a shell.
The real user used is called the **hosting user**. Typically this user is
*git*, and that is what we will use throughout the documentation. However
RPMs and DEBs create a user called *gitolite* for this, so adjust instructions
and examples accordingly.
**Unless otherwise stated, everything in this page is to be done by logging in
as this "hosting user"**.
Notes:
* Any unix user can be a hosting user.
* Which also means you can have several hosting users on the same machine.
* The URLs used will be of the form `git@host:reponame` (or its longer
equivalent starting with `ssh://`). The `.git` at the end is optional. I
recommend you leave it out, so your reponames are consistent with what the
conf file uses.
## #req requirements
### your skills
* If you're installing gitolite, you're a "system admin", like it or not.
Ssh is therefore a necessary skill. Please take the time to learn at
least enough to get passwordless access working.
* You also need to be somewhat familiar with git itself. You cannot
administer a whole bunch of git repositories if you don't know the basics
of git.
* Some familiarity with Unix and shells is probably required.
* Regular expressions are a big part of gitolite in many places but
familiarity is not necessary to do *basic* access control.
### server
* Any Unix system with a posix compatible "sh".
* Git version 1.6.6 or later.
* Perl 5.8.8 or later.
* Openssh (almost any version). Optional if you're using [smart
http][http].
* A dedicated Unix userid to be the hosting user, usually "git" but it can
be any user, even your own normal one. (If you're using an RPM/DEB the
install probably created one called "gitolite").
Also see the [WARNINGS][] page for more on what gitolite expects on the server
side.
### client
* Openssh client.
* Git 1.6.6 or later. Almost any git client will work, as long as it knows
how to use ssh keys and send the right one along.
## getting the software
git clone git://github.com/sitaramc/gitolite
## the actual install
**Note**: This section describes installing an ssh-based setup. For smart
http setup click [here][http].
Gitolite has only one server side "command" now, much like git itself. This
command is `gitolite`. You don't need to place it anywhere special; worst
case you run it with the full path.
"Installation" consists of the following options:
1. Keep the sources anywhere and use the full path to run the `gitolite`
command.
2. Keep the sources anywhere and symlink *just* the `gitolite` program to
some directory on your `$PATH`.
3. Copy the sources somewhere and use that path to run the `gitolite`
command.
Option 2 is the best for general use.
There is a program called 'install' that helps you do these easily. Assuming
your cloned the repo like this:
git clone git://github.com/sitaramc/gitolite
you can run the 'install' command in 3 different ways:
# option 1
gitolite/install
# option 2
gitolite/install -ln
# defaults to $HOME/bin (which is assumed to exist)
# ** or **
# or use a specific directory (please supply full path):
gitolite/install -ln /usr/local/bin
# option 3
# (again, please supply a full path)
gitolite/install -to /usr/local/gitolite/bin
Creating a symlink doesn't need a separate program but 'install' also runs
`git describe` to create a VERSION file, which, trust me, is important!
**Next step**: run [**setup**][setup].
## upgrading
* Update your clone of the gitolite source.
* Repeat the install command you used earlier (make sure you use the same
arguments as before).
* Run `gitolite setup`.
## #package packaging gitolite
Gitolite has broad similarities to git in terms of packaging requirements.
* Git has 150 executables to marshal and put somewhere. Gitolite has the
directories `commands`, `lib`, `syntactic-sugar`, `triggers`, and `VREF`.
It doesn't matter what this directory is. As an example, Fedora keeps
git's 150 executables in /usr/libexec/git-core, so /usr/libexec/gitolite
may be a good choice; it's upto you.
*The rest of this section will assume you chose /usr/libexec/gitolite as
the location, and that this location contains the 5 directories named
above*.
* Git has the `GIT_EXEC_PATH` env var to point to this directory. Gitolite
has `GL_BINDIR`. However, in git, the "make" process embeds a suitable
default into the binary, making the env var optional.
With that said, here's one way to package gitolite:
* Put the executable `gitolite` somewhere in PATH. Put the executable
`gitolite-shell` in /usr/libexec/gitolite (along with those 5 directories).
Change the 2 assignments to `$ENV{GL_BINDIR}`, one in 'gitolite', one in
'gitolite-shell', to "/usr/libexec/gitolite" from `$FindBin::RealBin`.
This is equivalent to "make" embedding the exec-path into the executable.
**OR**
Put both executables `gitolite` and `gitolite-shell` also into
/usr/libexec/gitolite (i.e., as siblings to the 5 directories mentioned
above). Then *symlink* `/usr/libexec/gitolite/gitolite` to some directory
in the PATH. Do not *copy* it; it must be a symlink.
Gitolite will find the exec-path by following the symlink.
* The `Gitolite` subdirectory in `/usr/libexec/gitolite/lib` can stay right
there, **OR**, if your distro policies don't allow that, can be put in any
directory in perl's `@INC` path (such as `/usr/share/perl5/vendor_perl`).
* Finally, a file called `/usr/libexec/gitolite/VERSION` must contain a
suitable version string.
## #migr migrating
<font color="gray">This section is about migrating from older gitolite to
"g3". If you're migrating from gitosis, see [here][gsmigr].</font>
First things first: g2 will be supported for a good long time for critical
bugs, although enhancements and new features won't happen.
If you're an existing (gitolite v1.x or v2.x) user, and wish to migrate , here
are the steps:
### pre-migration checks
1. Check the [dev-status][] page to make sure all the features you want have
been implemented in g3.
2. Read the [g2 migration][g2migr] page to see what changes affect you and
your users, and how much time it might take you to migrate. (The closer
you were to a default install of the old gitolite, the less time a
migration will take.)
This includes at least running `check-g2-compat` to see what are the big
issues you might need to address during the migration.
### the actual migration
(Note: You may also like the [example migration][g2migr-example] page).
**Note**: nothing in any of the gitolite install/setup/etc will ever touch the
*data* in any repository except the gitolite-admin repo. The only thing it
will normally touch in normal repos is the `update` hook.
**Note: all migration happens on the server; you do not need your
workstation**.
1. Carefully wipe out the old gitolite:
* The **code**
* Delete or move away all the old gitolite scripts. Check the path
to the gl-auth-command in `~/.ssh/authorized_keys` if you forgot
where you put them.
* Delete or move away the two directories named in the two variables
`GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS` in `~/.gitolite.rc`.
* The **rc file**
* Rename `~/.gitolite.rc` to something else.
* The **admin repo**
* clone `~/repositories/gitolite-admin.git` to someplace safe
* then delete `~/repositories/gitolite-admin.git`
(Make sure you do not delete any other repos!)
* The **admin directory**.
* If you need to preserve logs, move the ~/.gitolite/logs` directory
somewhere else.
* If you added any custom hooks and wish to preserve them, move the
~/.gitolite/hooks` directory somewhere else.
* Delete `~/.gitolite`.
2. Read about [presetting][rc-preset] the rc file; if you're using any
variables listed as requiring preset, follow those instructions to create
your new rc file.
3. Install gitolite g3; see [quick install and setup][qi] or [install][]
followed by [setup][]. However, the 'setup' step need not supply a
private key. You can run it as `gitolite setup -a admin`.
NOTE: ignore any 'split conf not set, gl-conf present...' errors at this
time. You may see none, some, or many. It does not matter right now.
4. Make sure your gitolite-admin clone has the correct pubkey for the
administrator in its `keydir` directory, then run [`gitolite push
-f`][bypass] to overwrite the "default" admin repo created by the install.
5. Handle any errors, look for migration issues, etc., as described in the
links at the top of this page.
This also includes building up your new `~/.gitolite.rc` file.
You're done.

165
doc/master-toc.mkd Normal file
View file

@ -0,0 +1,165 @@
# gitolite documentation
## [Introduction][index]
* (for [current][g2] gitolite (v2) users)
* [quick links][ql]
* [what][] is gitolite
* [why][] might you need it
* [contact][] info, mailing list, IRC channel
* [license][] info
## help for [emergencies][]
* [lost][lost-key] admin key/access
* [bypass][]ing gitolite
* [clean][]ing out a botched install
* [common][ce] errors
* [uncommon][ue] errors
* things that are [not gitolite problems][ngp]
## [WARNINGS][]
## [trying][] out gitolite
## [quick][qi] install, setup, and clone
## [install][]
* notes and naming conventions
* requirements
* your skills
* server
* client
* getting the software
* the actual install
* upgrading
* packaging gitolite
* [migr][]ating
## [setup][]
## [clone][]
## gitolite [admin][]istration
* ([server-side][server]) settings, hooks, etc.
* ([link][WARNINGS]: important cautions on server side activity)
* changing settings in the [rc][] file
* installing custom [hooks][]
* ([link][existing]: moving existing repos into gitolite)
* ([client-side][adminrepo]) access control via the gitolite-admin repo
* basic [syntax][] of the [conf][] file
* include files
* ([link][sugar]: syntactic sugar)
* [groups][] (of users and repos)
* special: '@all'
* (link: storing user group info in LDAP)
* adding and removing [users][]
* multiple keys per user
* adding and removing [repos][]
* renaming repos
* defining access [rules][]
* what does a rule look like?
* when are the rules checked?
* how are the rules matched?
* summary of [permissions][permsum]
* additional topics
* [rule accumulation][rule-accum]
* applying [deny rules][deny-rules] during the pre-git check
* ([link][refex]: refexes)
* ([link][write-types]: different types of write operations)
* ([link][vref]: virtual refs)
* gitolite [options][]
* "[git config][git-config]" keys and values
* ["wild"][wild] repos (user created repos)
* quick introduction
* (admin) declaring wild repos in the conf file
* (user) [creating][create] a specific repo
* repo patterns
* roles
* adding other roles
* [IMPORTANT WARNING ABOUT THIS FEATURE][rolenamewarn]
* listing wild repos
* deleting wild repos
## what your [user][]s need to know
## [special][] features/setups
* [disabling pushes][writable] to take backups
* [personal][pers] branches
* ([link][votes]: voting on commits)
* [delegating][deleg] access control responsibilities
* ([link][NAME]: the NAME VREF)
* the [subconf][] command
* ([link][partial-copy]: faking selective READ control)
## interfacing with [external][] tools
* gitweb
* changing the [UMASK][umask]
* git-daemon
## [mirroring][]
## [rare][]/one-time activities
* moving [existing][] repos into gitolite
* [moving][] servers
## [customisation][cust]
* types of non-core programs
* ([link][non-core]: non-core programs shipped with gitolite)
* [commands][]
* [hooks][]
* syntactic [sugar][]
* ([link][triggers]: triggers)
* ([link][vref]: VREFs)
* [developer notes][dev-notes]
* environment variables and other inputs
* APIs
* the shell API
* the perl API
* writing your own...
* hooks
* commands
* trigger programs
* sugar
## [non-core][] programs shipped with gitolite
* commands
* syntactic sugar
* triggers
* ([link][vref]: VREFs)
* special cases
* [partial-copy][]: selective read control for branches
## background info
* [files and directories][files] involved in install+setup
* [auth][]entication versus authorisation
* interfacing with [other authentication][otherauth] systems
* getting user group info from [LDAP][ldap]
* [ssh][]
* [regular expressions][regex]
## contributed software, tools, and documentation
* TBD
## TBD
* log file format, LOG_EXTRA
* hub
* mob branches
* password access

209
doc/mirroring.mkd Normal file
View file

@ -0,0 +1,209 @@
# mirroring using gitolite
<font color="red">**WARNING** existing gitolite mirroring users please note:
**there are [significant changes][g2i-mirroring]** in syntax and usage
compared to g2. If you're not the kind who reads documentation before doing
serious system admin things, well... good luck!</font>
----
[[TOC]]
----
Mirroring is simple: you have one "master" server and one or more "slave"
servers. The slaves get updates only from the master; to the rest of the
world they are at best read-only.
Gitolite extends this simple notion in the following ways:
* Different masters and sets of slaves for different repos
This lets you do things like:
* Use the server closest to *most* of its developers as the master for
that repo.
* Not mirror a repo at all to some servers.
* Have repos that are purely local to a server (not mirrored at all).
* Negotiate mirroring with servers that are not even under your control.
* Push to a slave on demand or via cron (helps deal with bandwidth or
connectivity constraints).
* Pushes to a slave can be transparently forwarded to the real master.
Your developers need not worry about where a repo's master is -- they just
write to their local mirror for *all* repos, even if their local mirror is
only a slave for some.
## caveats
* Mirroring will never *create* a repo on a slave; it has to exist and be
prepared to receive updates from the master. (For example, [wild][] repos
must be created on the slave as well, otherwise they will not propagate).
* Mirroring is only for git repos. Ancillary files like gl-creator and
gl-perms in the repo directory are not mirrored; you must do that
separately. Files in the admin directory (like log files) are also not
mirrored.
* If you ever do a [bypass push][bypass], mirroring will not work.
Mirroring checks also will not work -- for example, you can push to a
slave, which is not usually a good idea. So don't bypass gitolite if the
repo is mirrored!
## setting up mirroring
This is in two parts: the initial setup and the rc file, followed by the conf
file settings and syntax.
### the initial setup and the rc file
On **each** server:
* Install gitolite normally. Make clones of the server's 'gitolite-admin'
repo on your workstation so you can admin them all from one place.
* Give the server a short, simple, "hostname" and set the HOSTNAME in the
rc file to this name, for example 'mars'.
* Run ssh-keygen if needed and get an ssh key pair for the server. Copy the
public key to a common area and name it after the host, but with 'server-'
prefixed. So the pubkey for server 'mars' would be stored as
'server-mars.pub'.
* Copy all keys to all the admin repo clones on your workstation and and add
them as usual. This is an `O(N^2)` operation ;-)
You may have guessed that the prefix 'server-' is special, and
distinguishes a human user from a mirroring peer.
* Create "host" aliases to refer to all other machines. See [here][ssh-ha]
for what/how.
The host alias for a host (in all other machines' `~/.ssh/config` files)
MUST be the same as the `HOSTNAME` in the referred host's
`~/.gitolite.rc`. Gitolite mirroring **requires** this consistency in
naming; things will NOT work otherwise.
Normally you should be able to build one common file and append it to all
the servers' `~/.ssh/config` files.
* The following **MUST** work for **each pair** of servers that must talk to
each other:
# on server mars
ssh phobos info
# the response MUST start with "hello, server-mars..."
Note the exact syntax used; variations like "ssh git@phobos.example.com
info" are NOT sufficient. That is why you need the ssh host aliases.
Check this command from *everywhere to everywhere else*, and make sure you
get expected results. **Do NOT proceed otherwise.**
* Setup the gitolite.conf file on all the servers. If the slaves are to be
exact copies of the master, you need to do the complete configuration only
on the master; the slaves can have just this:
repo gitolite-admin
RW+ = <some local admin>
option mirror.master = mars
option mirror.slaves = phobos
because on the first push to the master it will update all the slaves
anyway.
* When that is all done and tested, **enable mirroring** by going through
the rc file and uncommenting all the lines mentioning `Mirroring`.
### conf file settings and syntax
Mirroring is defined by the following [options][]. You can have different
settings for different repos, and of course some repos may not have any mirror
options at all -- they are then purely local.
repo foo
...access rules...
option mirror.master = mars
option mirror.slaves = phobos deimos
option mirror.redirectOK = all
The first line is easy, since a repo can have only one master.
The second is a space separated list of hosts that are all slaves. You can
have several slave lists, as long as the config key starts with
'mirror.slaves' and is unique. For example.
option mirror.slaves-1 = phobos deimos
option mirror.slaves-2 = io europa
option mirror.slaves-3 = ganymede callisto
Do not repeat a key; then only the last line for that key will be effective.
## redirected pushes
**Please read carefully; there are security implications if you enable this
for mirrors NOT under your control**.
Normally, a master, (and *only* a master), pushes to a slave, and the slaves
are "read-only" to the users. Gitolite allows a *slave* to receive pushes
from a user and transparently redirect them to the *master*.
This simplifies things for users in complex setups, letting them use their
local mirror for both fetch and push access to all repos.
Just remember that if you do this, **authentication** happens on the slave,
but **authorisation** is on the master. The master is trusting the slave to
authenticate the user correctly, *and* use the same authentication data (i.e.,
user alice on the slave should be guaranteed to be the same as user alice on
the master).
The syntax for enabling this is one of these:
option mirror.redirectOK = all
option mirror.redirectOK = phobos deimos
The first syntax trusts all valid slaves to redirect user pushes, while the
second one trusts only some slaves.
Note that you cannot redirect gitolite commands (like perms, etc).
## #sync manually synchronising a slave repo
You can use the `gitolite mirror push` command on a master to manually
synchronise any of its slaves. Try it with `-h` to get usage info.
Tip: if you want to do this to all the slaves, try this:
for s in `gitolite git-config -r reponame mirror.slave | cut -f3`
do
gitolite mirror push $s reponame
done
This command can also be run remotely; run `ssh git@host mirror -h` for
details.
## #HOSTNAME appendix A: HOSTNAME substitution
Wherever gitolite sees the word `%HOSTNAME`, it will replace it with the
HOSTNAME supplied in the rc file, if one was supplied. This lets you maintain
configurations for all servers in one repo, yet havethem act differently on
different servers, by saying something like:
subconf "%HOSTNAME/*.conf"
You can use it in other places also, for example:
RW+ VREF/NAME/subs/%HOSTNAME/ = @%HOSTNAME-admins
(you still have to define @mars-admins, @phobos-admins, etc., but the actual
VREF is now one line instead of one for each server!)
## appendix B: efficiency versus paranoia
If you're paranoid enough to use mirrors, you should be paranoid enough to
set this on each server, despite the possible CPU overhead:
git config --global receive.fsckObjects true

126
doc/mkdoc Executable file
View file

@ -0,0 +1,126 @@
#!/usr/bin/perl
# cd <g3 clone>/doc; ./mkdoc # --> creates ../html/*.html
my $MKD = "./Markdown.pl";
use 5.10.0;
use strict;
use warnings;
use lib '../src/lib/Gitolite/Test';
use Tsh;
$ENV{TSH_ERREXIT} = 1;
try "
mkdir ../html; ok
git status -s -uno; !/./
git log --oneline -1
" or die 1;
my $head = (lines())[0];
main();
try "
git checkout gh-pages; ok
git reset --hard github/gh-pages; ok
cd ..; ok
git rm *.html; ok
mv html/*.html .; ok
git add *.html; ok
git commit -m '$head'; ok
git checkout master; ok
rmdir html; ok
" or die 2;
sub main {
chomp(@ARGV = `find . -name "*.mkd" | cut -c3-`) if not @ARGV;
@ARGV = grep { /./ } @ARGV;
my @save = @ARGV;
my $css_block = join("", <DATA>);
my %ct; # chapter tocs
my %title;
my $mf = '';
my $fh;
while (<>) {
$ARGV =~ /^(?:.*\/)?([^\/]+)\.mkd$/;
my $b = $1;
if (/^(#+) (?:#(\S+) )?(.*)/) {
$title{$b} ||= $3;
if ( length($1) == 1 ) {
$ct{$b} .= "\n";
$ct{$b} .= " * [$3][$b]\n";
$mf .= "[$b]: $b.html\n";
} else {
$ct{$b} .= " " x ( 4 * ( length($1) - 1 ) );
$ct{$b} .= " * ";
$ct{$b} .= (
$2
? "[$3][$2]"
: "$3"
);
$ct{$b} .= "\n";
$mf .= "[$2]: $b.html" . ($2 ne $b ? "#$2" : "") . "\n" if $2;
}
}
}
# open($fh, ">", "master-toc.mk2")
# and print $fh $mt
# and close $fh;
# after this, do this for every mkd (including the master-toc.mkd)
# cat $css_block > $base.html
# cat $base.mkd $mf | $MKD >> $base.html
for my $mkd (@save) {
$mkd =~ /^(?:.*\/)?([^\/]+)\.mkd$/;
my $b = $1;
my $css = $css_block;
$css =~ s/%TITLE/$title{$b} || $b/e;
open($fh, ">", "../html/$b.html")
and print $fh $css
and close $fh;
my $mkt = `cat $mkd`;
$mkt =~ s/^(#+) #(\S+) /$1 <a name="$2"><\/a> /mg;
$mkt =~ s/^\[\[TOC\]\]/$ct{$b}/mg;
open($fh, "|-", "$MKD >> ../html/$b.html")
and print $fh $mkt, "\n\n", $mf
and close $fh;
}
}
__DATA__
<head>
<title>%TITLE</title>
<style>
body { background: #fff; text-color: #000; margin-left: 40px; font-size: 0.9em; font-family: sans-serif; max-width: 800px; }
h1 { background: #ffb; text-color: #000; margin-left: -30px; border-top: 5px solid #ccc; }
h2 { background: #ffb; text-color: #000; margin-left: -20px; border-top: 3px solid #ddd; }
h3 { background: #ffb; text-color: #000; margin-left: -10px; }
h4 { background: #ffb; text-color: #000; }
code { font-size: 1.1em; background: #ddf; text-color: #000; }
pre { margin-left: 2em; background: #ddf; text-color: #000; }
pre code { font-size: 1.1em; background: #ddf; text-color: #000; }
</style>
</head>
<p style="text-align:center">
<a href="master-toc.html">master TOC</a>
|
<a href="index.html">main page</a>
|
<a href="index.html#license">license</a>
</p>
<p style="text-align:center">
<font color="gray">This is for gitolite "g3"; for older (v2.x) documentation click <a href="http://sitaramc.github.com/gitolite/g2/master-toc.html">here</a></font>
</p>

137
doc/non-core.mkd Normal file
View file

@ -0,0 +1,137 @@
# non-core programs shipped with gitolite
----
[[TOC]]
----
## commands
A list of these commands can be obtained by running `gitolite help` on the
server. A different (and probably much smaller) list can be obtained by a
remote user running `ssh git@host help`.
All the commands that ship with gitolite will respond to `-h`; please report a
bug to me if they don't.
Here's a list of remote commands that are shipped:
* 'desc' -- get/set the 'description' file for a repo
* 'fork' -- fork a repo
* 'info' -- already documented [here][info]
* 'mirror' -- documented [here][sync]
* 'perms' -- get/set the gl-perms file; see [perms][] for more
* 'sskm' -- self-service key management, see [sskm][] for more
* 'writable' -- disabling pushes to take backups etc
* 'D' -- deleting user-created repos
## syntactic sugar
The following "sugar" programs are available:
* Continuation-lines -- allow the use of C-style backslash escaped
continuation lines in the conf file. I don't like it but some people do,
and now I can support them without bulking up the "core" conf parser!
* Keysubdirs-as-groups -- someone wanted the sub-directory name (of
"keydir/") in which the pubkey was placed to be a group to which the user
automatically belonged. A very unusual requirement, and one which would
*never* have seen the light of day in g2, but in g3 it's easy, and doesn't
affect anyone else!
(Note: the last component of the directory path is used if there are more
than one level between "keydir/" and the actual file).
## triggers
The `PRE_GIT` triggers are:
* partial-copy -- this has its own section later in this page.
* renice -- this renices the entire job to whatever value you specify.
The `POST_GIT` triggers are:
* CpuTime, which is only a sample because it only prints the CPU times data.
In reality you will want to do something else with it.
The `POST_COMPILE` triggers are:
* post-compile/ssh-authkeys -- takes the pubkeys in keydir and populates
`~/.ssh/authorized_keys`.
* post-compile/update-git-configs -- updates individual 'repo.git/config'
files (using the 'git config ...' command) from settings supplied in the
conf file. All sections except 'gitolite-options' are processed. (The
'gitolite-options' section is considered internal to gitolite).
* post-compile/update-git-daemon-access-list -- create/delete
'git-daemon-export-ok' files in each repo based on whether the conf says
'daemon' can read the repo or not.
* post-compile/update-gitweb-access-list -- populates the file named in
`GITWEB_PROJECTS_LIST` in the rc file (default: `$HOME/projects.list`)
with the list of repos that gitweb is allowed to access. This could be
more than just "R = gitweb"; any repo that has any config setting with the
section name 'gitweb' (like 'gitweb.owner', 'gitweb.description', etc) is
considered readable by gitweb, so the final list is a union of these two
methods.
The `POST_CREATE` triggers are:
* The last 3 in the `POST_COMPILE` list also run from `POST_CREATE`, for
obvious reasons.
## VREFs
VREFs have their [own page][vref].
## special cases
### #partial-copy partial-copy: selective read control for branches
Git (and therefore gitolite) cannot do selective read control -- allowing
someone to read branch A but not branch B. It's the entire repo or nothing.
<font color="gray"> [Side note: Gerrit Code Review can do that, but that is
because they have their own git stack (and their own sshd, and so on) all in
one big Java program. Gerrit is *really* useful if you want code review to be
part of the access control decision] </font>
Gitolite can now help you do this. Note that this is only for branches; you
can't do this for files and directories.
Here's how:
1. enable 'partial-copy' in the `PRE_GIT` section in the rc file.
2. for each repo "foo" which has secret branches that a certain set of
developers (we'll use a group called `@temp-emp` as an example) are not
supposed to see, do this:
repo foo
# rules should allow @temp-emp NO ACCESS
repo foo-partialcopy-1
- secret-branch = @temp-emp
# other rules should ensure ONLY @temp-emp has ANY ACCESS
# NO other user should have access
- VREF/partial-copy = @all
config gitolite.partialCopyOf = foo
**IMPORTANT**: if you're using other VREFs, please make sure this one is
placed at the end, after all the others.
And that should be it. **Please test it and let me know if it doesn't work!**
WARNINGS:
* If you change the config to disallow something that used to be allowed,
you should delete the partial repo on the server and then run 'gitolite
compile' to let it build again. See t/partial-copy.t for details.
* Not tested with smart http; probably won't work.
* Also not tested with mirroring, or with wild card repos.

20
doc/options.mkd Normal file
View file

@ -0,0 +1,20 @@
# gitolite options
Some gitolite features are enabled, or gitolite's behaviour changed, by
setting "options".
Options are set by repo. The syntax is very simple:
option foo.bar = baz
Of course this is useless if some other part of gitolite, or some external
command, is not querying for the option key 'foo.bar'!
Options are therefore documented in the section/page they belong in, not here.
Here are the currently recognised options:
* [deny-rules][] -- ask gitolite to honor deny rules during the pre-git
check also.
* [mirroring][] related options -- tell gitolite who is the master server,
and who are the slaves, for each repo.

165
doc/progit.mkd Normal file
View file

@ -0,0 +1,165 @@
# (master copy of progit chapter on gitolite)
## Gitolite ##
Note: the latest copy of this section of the ProGit book is always available within the [gitolite documentation][progit]. The author would also like to humbly state that, while this section is accurate, and *can* (and often *has*) been used to install gitolite without reading any other documentation, it is of necessity not complete, and cannot completely replace the enormous amount of documentation that gitolite comes with.
[Update 2012-04-10]: This page has been completely rewritten for gitolite version 3, informally called "g3". G3 is a *total* rewrite of gitolite to push a lot more features away from "core", give the core a bunch of extension mechanisms, and finally have much better shell and perl APIs to bring all this together.
Git has become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was originally created to help with those requirements, but it turns out that it's equally useful in the open source world: the Fedora Project controls access to their package management repositories (over 10,000 of them!) using gitolite, and this is probably the largest gitolite installation anywhere too. KDE, and kernel.org, are other very high-profile users of gitolite.
Gitolite allows you to specify permissions not just by repository, 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 ###
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. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `git` account on a host called `gitserver`.
Gitolite is somewhat unusual as far as "server" software goes -- access is via ssh, and so every userid on the server is a potential "gitolite host". Gitolite is at present best installed manually, as the "g3" version does not yet have RPM/DEB support from distros. We will describe the simplest install method in this article; for the other methods please see the documentation.
To begin, create a user called `git` on your server and login to this user. Copy your ssh pubkey (a file called `~/.ssh/id_rsa.pub` if you did a plain `ssh-keygen` with all the defaults) from your workstation, renaming it to `YourName.pub`. Then run these commands:
git clone git://github.com/sitaramc/gitolite
gitolite/install -ln
# assumes $HOME/bin exists and is in your $PATH
gitolite setup -pk $HOME/YourName.pub
# for example, I would run 'gitolite setup -pk $HOME/sitaram.pub'
Finally, back on your workstation, run `git clone git@server:gitolite-admin`.
And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in your workstation. You administer your gitolite setup by making changes to this repository and pushing. See adding [users][] and [repos][] to start with.
### Customising the Install ###
While the default, quick, install works for most people, there are some ways to customise the install if you need to. Some changes can be made simply by editing the [rc file][rc], but if that is not sufficient, there's documentation on [customising gitolite][cust] by using non-core programs.
### Config File and Access Control Rules ###
Once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got:
$ cd ~/gitolite-admin/
$ ls
conf/ keydir/
$ find conf keydir -type f
conf/gitolite.conf
keydir/sitaram.pub
$ cat conf/gitolite.conf
repo gitolite-admin
RW+ = sitaram
repo testing
RW+ = @all
Notice that "sitaram" (the name of the pubkey in the gl-setup command you used 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 [well documented][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".
@oss_repos = linux perl rakudo git gitolite
@secret_repos = fenestra pear
@admins = scott # Adams, not Chacon, sorry :)
@interns = ashok # get the spelling right, Scott!
@engineers = sitaram dilbert wally alice
@staff = @admins @engineers @interns
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.
repo @oss_repos
RW int$ = @interns
RW eng- = @engineers
RW refs/tags/rc[0-9] = @engineers
RW+ = @admins
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.
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.
### Advanced Access Control with "deny" rules ###
So far, we've only seen permissions to be one or `R`, `RW`, or `RW+`. However, gitolite allows another permission: `-`, 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, 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 to do that:
RW master integ = @engineers
- master integ = @engineers
RW+ = @engineers
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.
You can also use deny rules to hide specific repos from people (or gitweb, or git-daemon, etc.), when you have otherwise allowed them access to *all* repos. For example, a server containing open source repos may nevertheless wish to hide the special 'gitolite-admin' repo from gitweb, even though all the other repos can be made visible:
repo gitolite-admin
- = gitweb daemon
[... other access rules ...]
option deny-rules = 1
# remember this is for gitolite "g3"; the older gitolite had a
# different syntax
repo @all
R = gitweb daemon
[This][deny-rules] page has more details.
### Restricting pushes by files changed ###
In addition to restricting what branches a user can push changes to, you can also restrict what files they are allowed to touch. For example, perhaps the Makefile (or some other program) is really not supposed to be changed by just anyone, because a lot of things depend on it or would break if the changes are not done *just right*. You can tell gitolite:
repo foo
RW = @junior_devs @senior_devs
- VREF/NAME/Makefile = @junior_devs
User who are migrating from the older gitolite should note that there is a significant change in behaviour with regard to this feature; please see the [migration guide][migr] for details.
### Personal Branches ###
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>/*`); the details are [here][pers].
### "Wildcard" repositories ###
Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented [here][wild].
### Other Features ###
We'll round off this discussion with a sampling of other features, all of which, and many more, are described in great detail in the documentation.
**Logging**: 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.
**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Gitolite shows you what repos you have access to, and what that access may be. Here's an example:
hello sitaram, this is git@git running gitolite3 v0.02-15-g1db50f4 on git 1.7.4.4
R anu-wsd
R entrans
R W git-notes
R W gitolite
R W gitolite-admin
R indic_web_input
R shreelipi_converter
**Delegation**: 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. See [here][deleg] for more on this.
**Mirroring**: Gitolite can help you maintain multiple [mirrors][mirroring], and switch between them easily if the primary server goes down.
[progit]: http://sitaramc.github.com/gitolite/progit.html

48
doc/qi.mkd Normal file
View file

@ -0,0 +1,48 @@
# quick install, setup, and clone
## ASSUMPTIONS
* This is an ssh-based setup. For smart http setup click [here][http].
* This is a fresh install, not a migration from the old gitolite (v1.x,
v2.x).
* On the server, `$HOME/bin` exists and is in your `$PATH`. If you don't
like that, there are [other install methods][install].
* "your-name.pub" is your public key from your workstation.
* Also, this key does not already have shell access to this gitolite
hosting user.
* The setup command does not generate any warnings.
* If it does, please see [common errors][ce] and fix things before
continuing, or read the more complete [setup][] page.
## instructions
On the server, as the hosting user (e.g., 'git'):
# get the software
git clone git://github.com/sitaramc/gitolite
# install it
gitolite/install -ln
# setup the initial repos with your key
gitolite setup -pk your-name.pub
On your workstation:
# clone the admin repo so you can start adding stuff
git clone git@host:gitolite-admin.git
# Note 1: clone path must not include "repositories/"
# Note 2: it may include the ".git" at the end but it is optional
## next steps
If this step succeeds, you can add [users][], [repos][], or anything else
described [here][adminrepo].
If this step fails, be sure to look at the [ssh][] documentation before asking
for help. (A very basic first step is to run `ssh git@host info`;
[this][info] page tells you what to expect).

52
doc/rare.mkd Normal file
View file

@ -0,0 +1,52 @@
# rare or one-time activities
## #existing moving existing repos into gitolite
On the server:
* Move the repos to `$HOME/repositories`.
* Make sure that:
* They are all *bare* repos.
* All the repo names end in ".git".
* All the files and directories are owned and writable by the gitolite
hosting user (especially true if you copied them as root).
* Run `gitolite setup`. **If you forget this step, you can also forget
about write access control!**
Back on your workstation:
* [Add them][repos] to conf/gitolite.conf in your clone of the admin repo,
then commit and push the change.
If the repos are already covered by some [wild][] pattern, this is
optional.
## #moving moving servers
This is adapted from the "migrating" section of the [install][] page; if
you're actually *migrating* from g2 please go there!
Nothing in any of the gitolite install/setup/etc will ever touch the *data* in
any repository except the gitolite-admin repo. The only thing it will
normally touch is the `update` hook. So one fool-proof way of "moving"
servers is this (untested but should work; feedback appreciated):
1. Install gitolite on the new server, using the same key for the admin as
for the old server.
2. Copy the [rc][] file from the old server, overwriting this one.
3. [Disable][writable] the old server so people won't push to it.
4. Copy all the repos over from the old server, including gitolite-admin.
Make sure the files end up with the right ownership and permissions; if
not, chown/chmod them.
5. Run `gitolite setup`.
6. On a clone of the old gitolite-admin, add a new remote (or change an
existing one) to point to the new server. Then `git push -f` to this
remote.

99
doc/rc.mkd Normal file
View file

@ -0,0 +1,99 @@
# the "rc" file (`$HOME/.gitolite.rc`)
**NOTE**: if you're migrating from g2, there are some settings that MUST be
dealt with **before** running `gitolite setup`; please read the [g2
migration][g2migr] page and linked pages, and especially the one on
[presetting the rc file][rc-preset].
----
The rc file for g3 is *quite* different from that of g2.
As before, it is designed to be the only thing unique to your site for most
setups. What is new is that it is easy to extend it when new needs come up,
without having to touch core gitolite.
The rc file is perl code, but you do NOT need to know perl to edit it. Just
mind the commas, use single quotes unless you know what you're doing, and make
sure the brackets and braces stay matched up!
Please look at the `~/.gitolite.rc` file that gets installed when you setup
gitolite. As you can see there are 3 types of variables in it:
* simple variables (like `UMASK`)
* lists (like `POST_COMPILE`, `POST_CREATE`)
* hashes (like `ROLES`, `COMMANDS`)
While some of the variables are documented in this file, many of them are not.
Their purposes are to be found in each of their individual documentation files
around; start with [customising gitolite][cust]. If a setting is used by an
external command then running that command with '-h' may give you additional
information.
## specific variables
* `$UMASK`, octal, default `0077`
The default UMASK that gitolite uses makes all the repos and their
contents have `rwx------` permissions. People who want to run gitweb
realise that this will not do.
The correct way to deal with this is to give this variable a value like
`0027` (note the syntax: the leading 0 is required), and then make the
user running the webserver (apache, www-data, whatever) a member of the
'git' group.
If you've already installed gitolite then existing files will have to be
fixed up manually (for a umask or 0027, that would be `chmod -R g+rX`).
This is because umask only affects permissions on newly created files, not
existing ones.
* `$GIT_CONFIG_KEYS`, string, default empty
This setting allows the repo admin to define acceptable gitconfig keys.
Gitolite allows you to set git config values using the "config" keyword;
see [here][git-config] for details and syntax.
However, if you are in an installation where the repo admin does not (and
should not) have shell access to the server, then allowing him to set
arbitrary repo config options *may* be a security risk -- some config
settings allow executing arbitrary commands!
You have 3 choices. By default `$GIT_CONFIG_KEYS` is left empty, which
completely disables this feature (meaning you cannot set git configs via
the repo config).
The second choice is to give it a space separated list of settings you
consider safe. (These are actually treated as a set of [regular
expression][regex] patterns, and any one of them must match).
For example:
$GIT_CONFIG_KEYS = 'core\.logAllRefUpdates core\..*compression';
Each pattern should match the *whole* key (in other words, there
is an implicit `^` at the start of each pattern, and a `$` at the
end).
The third choice (which you may have guessed already if you're familiar
with regular expressions) is to allow anything and everything:
`$GIT_CONFIG_KEYS = '.*';`
* `DEFAULT_ROLE_PERMS`, string, default undef
This sets default wildcard permissions for newly created wildcard repos.
If set, this value will be used as the default role permissions for new
wildcard repositories. The user can change this value with the perms
command as desired after repository creation; it is only a default.
Please be aware this is potentially a multi-line variable. In most
setups, it will be left undefined. Some installations may benefit from
setting it to `READERS @all`.
If you want multiple roles to be assigned by default, here is how. Note
double quotes this time, due to the embedded newline, which in turn
require the '@' to be escaped:
DEFAULT_ROLE_PERMS => "READERS \@all\nWRITERS \@senior_devs",

34
doc/refex.mkd Normal file
View file

@ -0,0 +1,34 @@
## #refex matching a ref and a refex
A refex is a word I made up to mean "a regex that matches a ref". If you know
[regular expressions][regex] you're halfway there.
In addition:
* If no refex is supplied, it defaults to `refs/.*`, for example in a rule
like this:
RW = alice
* A refex not starting with `refs/` is assumed to start with `refs/heads/`.
This means normal branches can be conveniently written like this:
RW master = alice
# becomes 'refs/heads/master' internally
while tags will need to be fully qualified
RW refs/tags/v[0-9] = bob
* A refex is implicitly anchored at the start, but not at the end. In
regular expression lingo, a `^` is assumed at the start (but no `$` at the
end is assumed). So a refex of `master` will match all these:
refs/heads/master
refs/heads/master1
refs/heads/master2
refs/heads/master/full
If you want to restrict the match to just the one specific ref, use
RW master$ = alice

56
doc/repos.mkd Normal file
View file

@ -0,0 +1,56 @@
# adding and removing repos
**NOTE**: this page describes how to add new repos. To bring already existing
repos into gitolite control, click [here][existing].
> ----
> *WARNING: Do NOT add new repos directly on the server. Clone the
> 'gitolite-admin' repo to your workstation, make changes to it, then add,
> commit, and push. When the push hits the server, the server "acts" upon
> your changes.*
> ----
Just as for [users][], all operations are in a clone of the gitolite-admin
repo.
To **add** a new repo, edit `conf/gitolite.conf` and add it, along with at
least one user with some permissions. Or add it to an existing repo line:
repo gitolite tsh gitpod
RW+ = sitaram
RW dev = alice bob
R = @all
The "repo" line can have any number of repo names or repo group names in it.
However, it can only be one line; this will not work
repo foo
repo bar # WRONG; 'foo' is now forgotten
RW = alice
If you have too many, use a group name:
@myrepos = foo
@myrepos = bar
repo @myrepos
RW = alice
Finally, you add, commit, and push this change. Gitolite will create a bare,
empty, repo on the server that is ready to be cloned.
**Removing** a repo is not so straightforward. You certainly must remove the
appropriate lines from the `conf/gitolite.conf` file, but gitolite will not
automatically delete the repo from the server. You have to log on to the
server and do the dirty deed yourself :-)
It is best to make the change in the conf file, push it, and *then* go to the
server and do what you need to.
**Renaming** a repo is also not automatic. Here's what you do (and the order
is important):
* Go to the server and rename the repo at the Unix command line.
* Change the name in the conf/gitolite.conf file and add/commit/push.

151
doc/rules.mkd Normal file
View file

@ -0,0 +1,151 @@
## #rules access rules
This is arguably the most complex part of day-to-day gitolite. There are
several interconnected ideas that make this hard to lay out easily if you're
totally new to this, so read carefully.
We will use this as a running example:
@staff = dilbert alice wally bob
repo foo
RW+ = dilbert # line 1
RW+ dev = alice # line 2
- = wally # line 3
RW temp/ = @staff # line 4
R = ashok # line 5
### what does a rule look like?
A rule line has the structure
<permission> <zero or more refexes> = <one or more users/user groups>
The most common permissions used are:
* R, for read only
* RW, for push existing ref or create new ref
* RW+, for "push -f" or ref deletion allowed (i.e., destroy
information)
* `-` (the minus sign), to **deny** access.
There are also other, less commonly used, [types of permissions][write-types].
A refex is an expression that matches the ref (i.e., branch or tag) being
pushed. See [this][refex] for more info.
### when are the rules checked?
There are 2 places where access rules are checked.
The "pre-git" check is before git is invoked. Gitolite knows the repo name,
user name, and attempted access (R or W), but no ref name.
The "update" check is only for write operations, and it is just before git
updates a ref. This time gitolite knows the refname also.
### how are the rules matched?
For the **pre-git check**, any permission that contains "R" matches a read
operation, and any permission that contains "W" matches a write operation.
This is because we simply don't know enough to make finer distinctions at this
point.
In addition, *gitolite ignores deny rules during the pre-git check*. <font
color="gray">(You can [change this][deny-rules] if you wish, though it's
rarely needed)</font>. This means line 3 is ignored, and so Wally in our
example will pass the pre-git check.
For the **update check**, git gives us all the information we need. Then:
* All the rules for a repo are [accumulated][rule-accum].
* The rules pertaining to this repo *and* this user (or to a group to which
they belong, respectively) are kept; the rest are ignored.
* These rules are examined *in the sequence they appeared in the conf file*.
For each rule:
* If the ref does not match the [refex][], the rule is skipped.
* If it's a deny rule (the permissions field is a `-`), access is
**rejected** and the matching stops.
* If the permission field matches the specific [type of
write][write-types] operation, access is **allowed** and the matching
stops.
* If no rule ends with a decision, ("fallthru"), access is **rejected**.
Now you need to understand how [refex][] matching happens and how the
permissions match the various [types of write operations][write-types].
Using these, you can see, in our example, that:
* Everyone, even wally, can read the repo.
* Dilbert can push, rewind, or delete any ref.
* Alice can push, rewind, or delete any ref whose name starts with 'dev';
see [refex][] for details.
* Alice can also push (but not rewind or delete) any ref whose name starts
with 'temp/'. This applies to bob also.
* If it weren't for line 3, the previous statement would apply to wally
also.
Interestingly, wally can get past the pre-git check because gitolite ignores
deny rules for pre-git, but having got past it, he can't actually do anything.
That's by design, and as I said if you don't like it you can ask gitolite to
[deny at pre-git][deny-rules].
### #permsum summary of permissions
The full set of permissions, in regex syntax: `-|R|RW+?C?D?M?`. This expands
to one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`, or
`RW+CD`, all but the first two optionally followed by an `M`. And by now you
know what they all mean.
## additional topics
### #rule-accum rule accumulation
Gitolite was meant to collect rules from multiple places and apply them all.
For example, this:
repo foo
RW = u1
@gr1 = foo bar
repo @gr1
RW = u2
R = u3
repo @all
R = gitweb
is effectively the same as this, for repo foo:
repo foo
RW = u1
RW = u2
R = u3
R = gitweb
This extends to patterns also, but I'll leave an example for later.
### #deny-rules applying deny rules during the pre-git check
The access rules section above describes the problem in one scenario. Here's
another. Let's say you have this at the end of your gitolite.conf file:
repo @all
R = gitweb daemon
but you don't want the gitolite-admin repo showing up on gitweb. How do you
do that? Here's how:
repo gitolite-admin
- = gitweb daemon
option deny-rules = 1
repo @all
R = gitweb daemon
Note that the order matters; the `-` rule must come *before* the `R` rule.

48
doc/setup.mkd Normal file
View file

@ -0,0 +1,48 @@
# setting up gitolite
This is the second step in using gitolite, after [install][]. This also
happens on the server, (The next step is [clone][]).
----
Installing the software gets you ready to use it, but the first "use" of it is
always the "setup" command.
The first time you run it, you need to have a public key file (usually from
the admin's workstation) ready. If the main gitolite admin's username is
"alice", this file should be named "alice.pub". Then run
gitolite setup -pk alice.pub
If that command completes without any warnings, you should be done. If it had
a warning, you probably supplied a key which already has shell access to the
server. That won't work.
> ----
> Normally, gitolite is hosted on a user that no one accesses directly --
> you log on to the server using some other userid, and then `su - git`. In
> this scenario, there *is* no key being used for shell access, so there is
> no conflict.
> An alternative method is to use two different keys, and a [host
> alias][ssh-ha] to distinguish the two.
> [common errors][ce] has some links to background information on this
> issue.
> ----
The 'setup' command has other uses, so you will be running it at other times
after the install as well:
* To setup the update hook when you move [existing][] repos to gitolite.
This also applies if someone has been fiddling with the hooks on some
repos and you want to put them all right quickly.
* To replace a [lost admin key][lost-key].
* To setup gitolite for http mode (run 'gitolite setup -h' for more info).
When in doubt, run 'gitolite setup' anyway; it doesn't do any harm, though it
may take a minute or so if you have more than a few thousand repos!

48
doc/special.mkd Normal file
View file

@ -0,0 +1,48 @@
# special features and setups
----
[[TOC]]
----
## #writable disabling pushes to take backups
The `writable` command allows you to disable pushes to all repos or just the
named repo, in order to do file-system level things to the repo directory that
require it not to change, like using normal backup software.
Run `gitolite writable -h` for more info.
## #pers "personal" branches
"personal" branches are great for environments where developers need to share
work but can't directly pull from each other (usually due to either a
networking or authentication related reason, both common in corporate setups).
Personal branches exist **in a namespace** of their own. The syntax is
RW+ personal/USER/ = @userlist
where the "personal" can be anything you like (but cannot be empty), and the
"/USER/" part is **necessary (including both slashes)**.
A user "alice" (if she's in the userlist) can then push any branches inside
`personal/alice/`. Which means she can push `personal/alice/foo` and
`personal/alice/bar`, but NOT `personal/alice`.
(Background: at runtime the "USER" component will be replaced by the name of
the invoking user. Access is determined by the right hand side, as usual).
Compared to using arbitrary branch names on the same server, this:
* Reduces namespace pollution by corralling all these ad hoc branches into
the "personal/" namespace.
* Reduces branch name collision by giving each developer her own
sub-hierarchy within that.
* Removes the need to think about access control, because a user can push
only to his own sub-hierarchy.
## delegating access control responsibilities
See [this][deleg].

242
doc/sskm.mkd Normal file
View file

@ -0,0 +1,242 @@
# changing keys -- self service key management
[Note on g3 version: this has been manually spot-tested; there is no test suite. Changes from g2 version are minimal so it should all work fine but please report errors!]
Follow this guide to add keys to or remove keys from your account. Note that you cannot use this method to add your *first* key to the account; you must still email your initial key to your admin.
The key management is done using a command called `sskm`. This command must be enabled for remote use by the admin (see [here][commands] for more on this).
----
[[TOC]]
----
## Important!
There are a few things that you should know before using the key management system. Please do not ignore this section!
### Key fingerprints
Keys are identified in some of these subcommands by their fingerprints. To see the fingerprint for a public key on your computer, use the following syntax:
ssh-keygen -l -f <path_to_public_key.pub>
You'll get output like:
jeff@baklava ~ $ ssh-keygen -l -f .ssh/jeffskey.pub
2048 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 .ssh/jeffskey.pub (RSA)
### Active keys
Any keys that you can use to interact with the system are active keys. (Inactive keys are keys that are, for instance, scheduled to be added or removed.) Keys are identified with their `keyid`; see the section below on listing keys.
If you have no current active keys, you will be locked out of the system (in which case email your admin for help). Therefore, be sure that you are never removing your only active key!
### Selecting which key to use
Although you can identify yourself to the Gitolite system with any of your active keys on the server, at times it is necessary to specifically pick which key you are identifying with. To pick the key to use, pass the `-i` flag into `ssh`:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git info
hello jeff, the gitolite version here is v2.0.1-11-g1cd3414
the gitolite config gives you the following access:
@C R W [a-zA-Z0-9][a-zA-Z0-9_\-\.]+[a-zA-Z0-9]
....
*N.B.*: If you have any keys loaded into `ssh-agent` (i.e., `ssh-add -l` shows
at least one key), then this may not work properly. `ssh` has a bug which
makes it ignore `-i` values when that key has not been loaded into the agent.
One solution is to add the key you want to use (e.g., `ssh-add
.ssh/jeffskey`). The other is to remove *all* the keys from the agent or
disable the agent, using one of these commands:
* Terminate `ssh-agent` or use `ssh-add -D` flag to remove identities from it
* If using `keychain`, run `keychain --clear` to remove identities
* Unset the `SSH_AUTH_SOCK` and `SSH_AGENT_PID` variables in the current shell
### Public vs. private keys
In this guide, all keys are using their full suffix. In other words, if you see a `.pub` at the end of a key, it's the public key; if you don't, it's the private key. For instance, when using the `-i` flag with `ssh`, you are specifying private keys to use. When you are submitting a key for addition to the system, you are using the public key.
## Listing your existing keys
To see a list of your existing keys, use the `list` argument to `sskm`:
jeff@baklava ~ $ ssh git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
Notice the `@` sign in each key's name? That sign and the text after that up until the `.pub` is the `keyid`. This is what you will use when identifying keys to the system. Above, for instance, one of my keys has the `keyid` of `@key3`.
A keyid may be *empty*; in fact to start with you may only have a single
`jeff.pub` key, depending on how your admin added your initial key. You can
use any keyid you wish when adding keys (like `@home`, `@laptop`, ...); the
only rules are that it must start with the `@` character and after that
contain only digits, letters, or underscores.
## Adding or Replacing a key
### Step 1: Adding the Key
Adding and replacing a key is the same process. What matters is the `keyid`. When adding a new key, use a new `keyid`; when replacing a key, pass in the `keyid` of the key you want to replace, as found by using the `list` subcommand. Pretty simple!
To add a key, pipe in the text of your new key using `cat` to the `add` subcommand. In the example below, I explicitly select which existing, active pubkey to identify with for the command (using the `-i` parameter to ssh) for clarity:
jeff@baklava ~ $ cat .ssh/newkey.pub | ssh -i .ssh/jeffskey git@git sskm add @key4
hello jeff, you are currently using a normal ("active") key
please supply the new key on STDIN. (I recommend you
don't try to do this interactively, but use a pipe)
If you now run the `list` command you'll see that it's scheduled for addition:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
== keys marked for addition/replacement ==
1: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
### Step 2: Confirming the addition
Gitolite uses Git internally to store the keys. Just like with Git, where you commit locally before `push`-ing up to the server, you need to confirm the key addition (see the next section if you made a mistake). We use the `confirm-add` subcommand to do this, *but*: to verify that you truly have ownership of the corresponding private key, you *must* use the key you are adding itself to do the confirmation! (Inconvenient like most security, but very necessary from a security perspective.) This is where using the `-i` flag of `ssh` comes in handy:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm confirm-add @key4
hello jeff, you are currently using a key in the 'marked for add' state
Listing keys again shows that all four keys are now active:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
### Optional: Undoing a mistaken add (before confirmation)
Another advantage of Gitolite using Git internally is that that if we mistakenly add the wrong key, we can undo it before it's confirmed by passing in the `keyid` we want to remove into the `undo-add` subcommand:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm undo-add @key4
hello jeff, you are currently using a normal ("active") key
Listing the keys shows that that new key has been removed:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
## Removing a key
### Step 1: Mark the key for deletion
Deleting a key works very similarly to adding a key, with `del` substituted for `add`.
Let's say that I have my four keys from the example above:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
I would like to remove the key that on my box is called `newkey` and in the Gitolite system is known as `@key4`.
I simply pass in the identifier to the `del` subcommand of `sskm`:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm del @key4
hello jeff, you are currently using a normal ("active") key
Listing the keys now shows that it is marked for deletion:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
hello jeff, you are currently using a key in the 'marked for del' state
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
== keys marked for deletion ==
1: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
### Step 2: Confirming the deletion
Just like with Git, where you commit locally before `push`-ing up to the server, you need to confirm the key addition (see the next section if you made a mistake). We use the `confirm-del` subcommand to do this, *but*: unlike the `confirm-add` subcommand, you *must* use a *different* key than the key you are deleting to do the confirmation! This prevents you from accidentally locking yourself out of the system by removing all active keys:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm confirm-del @key4
hello jeff, you are currently using a normal ("active") key
Listing keys again shows that the fourth key has been removed:
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
### Optional: Undoing a mistaken delete (before confirmation)
Another advantage of Gitolite using Git internally is that that if we mistakenly delete the wrong key, we can undo it before it's confirmed by passing in the `keyid` we want to keep into the `undo-del` subcommand. Note that this operation *must* be performed using the private key that corresponds to the key you are trying to keep! (Security reasons, similar to the reason that you must confirm an addition this way; it prevents anyone from undoing a deletion, and therefore keeping in the system, a key that they cannot prove (by having the corresponding private key) should stay in the system):
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm undo-del @key4
hello jeff, you are currently using a key in the 'marked for del' state
You're undeleting a key that is currently marked for deletion.
Hit ENTER to undelete this key
Hit Ctrl-C to cancel the undelete
Please see documentation for caveats on the undelete process as well as how to
actually delete it.
(Go ahead and hit ENTER there; the caveats are really only on the administrative side of things.)
Listing the keys shows that that new key is now marked active again:
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
hello jeff, you are currently using a normal ("active") key
you have the following keys:
== active keys ==
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
----
## important notes for the admin
These are the things that can break if you allow your users to use this
command:
* "sskm" clones, changes, and pushes back the gitolite-admin repo. This
means, even if you're the only administrator, you should never 'git push
-f', in case you end up overwriting something sskm did.
* There is no way to distinguish `foo/alice.pub` from `bar/alice.pub` using
this command. You can distinguish `foo/alice.pub` from
`bar/alice@home.pub`, but that's not because of the foo and bar, it's
because the two files have different keyids.
In other words, sskm only works with the older style, not with the
"subdirectory" style of [multi-key][] management.
* Keys placed in specific folders (for whatever reasons), will probably not
stay in those folders if this command is used. Even a key delete, followed
by undoing the delete, will cause the key to effectively move to the root
of the key store (i.e., the `keydir` directory in the gitolite-admin repo).

46
doc/syntax.mkd Normal file
View file

@ -0,0 +1,46 @@
# #syntax basic syntax
In general, everything is **space separated**; there are no commas,
semicolons, etc., in the syntax.
**Comments** are in the usual shell-ish style.
**User names** and **repo names** are as simple as possible; they must start
with an alphanumeric, but after that they can also contain `.`, `_`, or `-`.
Usernames can optionally be followed by an `@` and a domainname containing at
least one `.` (this allows you to use an email address as someone's username).
Reponames can contain `/` characters (this allows you to put your repos in a
tree-structure for convenience)
There are no continuation lines by default. You do not need them; the section
on "groups" will tell you how you can break up large lists of names in a group
definition into multiple lines.
<font color="gray">If you *must* have them, you can optionally enable them;
see the syntactic [sugar][] section.</font>
## #include include files
Gitolite allows you to break up the configuration into multiple files and
include them in the main file for convenience.
include "foo.conf"
will include the contents of the file "foo.conf".
Details:
* You can also use a glob (`include "*.conf"`), or put your include files
into subdirectories of "conf" (`include "foo/bar.conf"`), or both
(`include "repos/*.conf"`).
* Included files are always searched from the gitolite-admin repo's "conf/"
directory, unless you supplied an absolute path. (Note: in the interests
of cloning the admin-repo sanely you should avoid absolute paths!)
* If you ended up recursing, files that have been already processed once are
skipped, with a warning.
<font color="gray">Advanced users: `subconf`, a command that is very closely
related to `include`, is documented [here][subconf].</font>

48
doc/testing.mkd Normal file
View file

@ -0,0 +1,48 @@
# testing gitolite
Here's how to *run* the tests.
<font color="red">**WARNING: they will clobber lots of things in your `$HOME`,
so be sure to use a throwaway userid**.</font>
git clone git://github.com/sitaramc/gitolite
cd gitolite
prove
(Make sure sshd allows incoming ssh to this userid at least from localhost.
Out of scope for this document: sshd config, 'AllowUsers', etc...)
Gitolite's test suite is mostly written using [tsh][] -- the "testing shell".
Take a look at some of the scripts and you will see what it looks like. It
has a few quirks and nuances; if you really care, email me.
[tsh]: http://github.com/sitaramc/tsh
The tests also use a somewhat convoluted system of environment variables in
order to run *entirely* as a local user, without going through ssh at all.
This lets a complete test suite run in about a fifth or less of the time it
would otherwise take.
If you think that defeats the purpose of the testing, you haven't read
[this][auth] yet.
## #trying trying out gitolite
It's easy to take gitolite for a trial run, in ssh mode, and play with all of
its features (except mirroring).
Create a **throw-away userid**, log in to it, then do what the "testing
gitolite" section above says, except instead of running `prove`, you run
`prove t/ssh*`.
When this is done, you get a gitolite installation with 7 gitolite users
("admin", and "u1" through "u6").
Don't forget that the client and the server are all on the same user on the
same machine; we're *simulating* 7 gitolite users using ssh keys! (How?
Maybe `~/.ssh/config` will give you a hint).
URLs look like `user:repo`, so for example you can clone the admin repo by
`git clone admin:gitolite-admin`. Remote commands look like `ssh u1 info`.
So start by cloning the admin repo, and try out whatever you want!

99
doc/triggers.mkd Normal file
View file

@ -0,0 +1,99 @@
# gitolite triggers
## intro and sample rc excerpt
Gitolite fires off external commands at 7 different times. The [rc][] file
specifies what commands to run at each trigger point, but for illustration,
here's an excerpt:
%RC = (
<...several lines later...>
# comment out or uncomment as needed
# these will run in sequence after post-update
POST_COMPILE =>
[
'post-compile/ssh-authkeys',
'post-compile/update-git-configs',
'post-compile/update-gitweb-access-list',
'post-compile/update-git-daemon-access-list',
],
# comment out or uncomment as needed
# these will run in sequence after a new wild repo is created
POST_CREATE =>
[
'post-compile/update-git-configs',
'post-compile/update-gitweb-access-list',
'post-compile/update-git-daemon-access-list',
],
(As you can see, post-create runs 3 programs that also run from post-compile.
This is perfectly fine, by the way)
## manually firing triggers
...from the server command line is easy. For example:
gitolite trigger POST_COMPILE
However if the triggered code depends on arguments (see next section) this
won't work. (The `POST_COMPILE` trigger programs all just happen to not
require any arguments, so it works).
## common arguments
Triggers receive the following arguments:
1. any arguments mentioned in the rc file (for an example, see the renice
command in the PRE_GIT trigger sequence),
2. the name of the trigger as a string (example, `"POST_COMPILE"`), so you
can call the same program from multiple triggers and it can know where it
was called from,
3. followed by zero or more arguments specific to the trigger, as given in
the next section.
## trigger-specific arguments and other details
Here are the **rest of** the arguments for each trigger, plus a brief
description of when the trigger runs:
* `ACCESS_1` runs after the first access check. Arguments:
* repo
* user
* 'R' or 'W'
* 'any'
* result: this is the result of the access() function. If it contains
the uppercase word "DENIED", the access was rejected. Otherwise
result contains the refex that caused the access to succeed.
* `ACCESS_2` runs after the second access check, in the update hook.
Arguments:
* repo
* user
* any of W, +, C, D, WM, +M, CM, DM
* the ref being updated (e.g., 'refs/heads/master')
* result (see above)
* `PRE_GIT` and `POST_GIT` run just before and after the git command.
Arguments:
* repo
* user
* 'R' or 'W'
* 'any'
* the git command ('git-receive-pack', 'git-upload-pack', or
'git-upload-archive') being invoked.
* `PRE_CREATE` and `POST_CREATE` run just before and after a new "[wild][]"
repo is created by user action. Arguments:
* repo
* user
* `POST_COMPILE` runs after an admin push has successfully "compiled" the
config file. By default, the next thing is to update the ssh authkeys
file, then all the 'git-config's, gitweb access, and daemon access.
No arguments.

97
doc/user.mkd Normal file
View file

@ -0,0 +1,97 @@
# what users (not admins) need to know about gitolite
...written for the one guy in the world no one will think of as "just a normal
user" ;-)
----
[[TOC]]
----
## accessing gitolite
The most common setup is based on ssh, where your admin asks you to send him
your public key, and uses that to setup your access.
Your actual access is either a git command (like `git clone
git@server:reponame`, and we won't be discussing these any more in this
document), or an ssh command (like `ssh git@server info`).
Note that you do *not* get a shell on the server -- the whole point of
gitolite is to prevent that!
## #info the info command
The only command that is *always* available to every user is the `info`
command (run `ssh git@host info -h` for help), which tells you what version of
gitolite and git are on the server, and what repositories you have access to.
The list of repos is very useful if you have doubts about the spelling of some
new repo that you know was setup.
## digression: two kinds of repos
Gitolite has two kinds of repos. Normal repos are specified by their full
names in the config file. "Wildcard" repos are specified by a regex in the
config file. Try the [`info` command][info] and see if it shows any lines
that look like regex patterns, (with a "C" permission).
If you see any, it means you are allowed to create brand new repos whose names
fit that pattern. When you create such a repo, your "ownership" of it (as far
as gitolite is concerned) is automatically recorded by gitolite.
## other commands
### #perms set/get additional permissions for repos you created
The gitolite config may have several permissions lines for your repo, like so:
repo pub/CREATOR/..*
RW+ = CREATOR
RW = user1 user2
R = user3
If that's all it had, you really can't do much. Any changes to access must be
done by the administrator. (Note that "CREATOR" is a reserved word that gets
expanded to your userid in some way, so the admin can literally add just the
first two lines, and *every* authenticated user now has his own personal repo
namespace, starting with `pub/<username>/`).
To give some flexibility to users, the admin could add rules like this:
RW = WRITERS
R = READERS
(he could also add other roles but then he needs to read the documentation).
Once he does this, you can then use the `perms` command (run `ssh git@host
perms -h` for help) to set permissions for other users by specifying which
users are in the list of "READERS", and which in "WRITERS".
If you think of READERS and WRITERS as "roles", it will help. You can't
change what access a role has, but you *can* say which users have that role.
**Note**: there isn't a way for you to see the actual rule set unless you're
given read access to the special 'gitolite-admin' repo. Sorry. The idea is
that your admin will tell you what "roles" he added into rules for your repos,
and what permissions those roles have.
### #desc adding a description to repos you created
The `desc` command is extremely simple. Run `ssh git@host desc -h` for help.
## "site-local" commands
The main purpose of gitolite is to prevent you from getting a shell. But
there are commands that you often need to run on the server (i.e., cannot be
done by pushing something to a repo).
To enable this, gitolite allows the admin to setup scripts in a special
directory that users can then run. Gitolite comes with a set of working
scripts that your admin may install, or may use as a starting point for his
own, if he chooses.
Think of these commands as equivalent to those in `COMMAND_DIR` in `man
git-shell`.
You can get a list of available commands by running `ssh git@host help`.

67
doc/users.mkd Normal file
View file

@ -0,0 +1,67 @@
# adding and removing users
Strictly speaking, gitolite doesn't know where users come from. If that
surprises you, read [this][auth]. However, gitolite does help with ssh-based
authentication, so here's some info on adding and removing users.
> ----
> *WARNING: Do NOT add users directly on the server. Clone the
> 'gitolite-admin' repo to your workstation, make changes to it, then add,
> commit, and push. When the push hits the server, the server "acts" upon
> your changes.*
> ----
All operations are in a clone of the gitolite-admin repo.
To **add** a user, say Alice, obtain her public key (typically
`$HOME/.ssh/id_rsa.pub` on her workstation), copy it to `keydir` with the user
name as the basename (e.g., 'alice.pub' for user alice), then `git add
keydir/alice.pub`. (All keys files must have names ending in ".pub", and must
be in openssh's default format).
To **remove** a user, `git rm keydir/alice.pub`.
In both cases, you must commit and push. On receiving the push, gitolite will
carry out the changes specified.
The user name is simply the base name of the public key file name. So
'alice.pub', 'foo/alice.pub' and 'bar/alice.pub', all resolve to user "alice".
## #multi-key multiple keys per user
The simplest and most understandable is to put their keys in different
subdirectories, (alice.pub, home/alice.pub, laptop/alice.pub, etc).
### old style multi-keys
There is another way that involves creating key files like `alice@home.pub`
and `alice@laptop.pub`, but there is a complication because gitolite also
allows *full email addresses* as user names. (I.e., `sitaramc@gmail.com.pub`
denotes the user called `sitaramc@gmail.com`).
This older method of enabling multi-keys was developed to deal with that. It
will continue to work and be supported in *code*, simply because I prefer it.
But I will not accept questions or doc patches for it, because it seems it is
too difficult to understand for a lot of people. This table of sample pubkey
filenames and the corresponding derived usernames is all you get:
* plain username, no multikey
sitaramc.pub sitaramc
* plain username, with multikeys
sitaramc@laptop.pub sitaramc
sitaramc@desktop.pub sitaramc
* email address as username, no multikey
sitaramc@gmail.com.pub sitaramc@gmail.com
* email address as username, with multikeys
sitaramc@gmail.com@laptop.pub sitaramc@gmail.com
sitaramc@gmail.com@desktop.pub sitaramc@gmail.com

307
doc/vref.mkd Normal file
View file

@ -0,0 +1,307 @@
# virtual refs
**IMPORTANT**: fallthru is success in VREFs, unlike the normal refs. That
won't make sense until you read further, but I had to put it up here for folks
who stop reading halfway!
----
[[TOC]]
----
Here's an example to start you off.
repo r1
RW+ = lead_dev dev2 dev3
- VREF/COUNT/9 = dev2 dev3
- VREF/COUNT/3/NEWFILES = dev2 dev3
Now dev2 and dev3 cannot push changes that affect more than 9 files at a time,
nor those that have more than 3 new files.
----
## rule matching recap
You won't get any joy out of this if you don't understand at least
[refex][]es and how [rules][] are processed.
But VREFs have one **very important difference** from normal rules. With
VREFs, a **fallthru results in success**. You'll see why this is more
convenient as you read on.
----
## what is a virtual ref
A ref like `refs/heads/master` is the main property of a push that gitolite
uses to make its yes/no decision. I call this a "real" ref.
Any *other* property of the push that you want to use to help in the decision
is therefore a *virtual* ref. This could be a property that git knows about,
like in the example above, or comes from outside git like, say, the current
time; see examples section later for some ideas.
## #vref-fallthru fallthru is success here
Notice that you didn't need to add an `RW+ VREF/...` rule for user `lead_dev`
in our example. This section explains why.
**Virtual refs are best used as additional "deny" rules**, performing extra
checks that core gitolite cannot.
Making fallthru be a "fail" forces you to add rules for all users, instead of
just the ones who should have those extra checks. Worse, since every virtual
ref involves calling an external program, many of these calls may be wasted.
There's another advantage to doing it this way: a VREF can choose to simply
die if things look bad, and it will have the same effect, assuming you used
the VREF only in "deny" rules.
This in turn means any existing update hook can be used as a VREF *as-is*, as
long as it (a) prints nothing on success and (b) dies on failure. See the
email-check example later.
## how it works -- overview
Briefly, a refex starting with `VREF/FOO` triggers a call to a program called
`FOO` in `$GL_BINDIR/VREF`.
That program is expected to print zero or more lines to its STDOUT; each line
that starts with `VREF/` is taken by gitolite as a new "ref" to be matched
against all the refexes for this user in the config. Including the refex that
caused the vref call, of course.
Normally, you send back the refex itself, if the test determines that the rule
should be matched, otherwise nothing. So, in our example, we print
`VREF/COUNT/9` if the count was indeed greater than 9. Otherwise we just
exit.
## how it works -- details
* The VREF code is only called if there are any VREF rules for the user,
which means when the lead developer pushes, the VREF is not called at all.
Side note: this is enormously more efficient than adding additional
`update` hooks, which will get executed whether they are needed or not,
for every repo and every user!
* When dev2 or dev3 push, gitolite first checks the real ref
(`ref/heads/master` or whatever). After this it looks at VREF rules, and
calls an external program for every one it finds. Specifically, in a line
like
- VREF/COUNT/3/NEWFILES = user
COUNT is the vref name, so the program called is
`$GL_BINDIR/VREF/COUNT`.
The program is passed **nine arguments** in this case (see next section
for details).
* The script can print anything it wants to STDOUT. Lines not starting with
`VREF/` are printed as is (so your VREF can do mostly-normal printing to
STDOUT).
For lines starting with `VREF/`, the first word in each such line will be
treated as a virtual ref to be matched against all the rules, while the
rest, if any, is a message to be added to the standard "...DENIED..."
message that gitolite prints if that refex matches.
Usually it only makes sense to either
* Print nothing that starts with `VREF/` -- if you don't want the rule
that triggered it to match (ie., whatever condition being tested was
not violated; like if the count of changed files did not exceed 9, in
our earlier example).
* Print the refex itself (plus an optional message), so that it matches
the line which invoked it.
### arguments passed to the vref code
* Arguments **1, 2, 3**: the 'ref', 'oldsha', and 'newsha' that git passed
to the update hook (see 'man githooks').
This, combined with the fact that non-zero exits are detected, mean that
you can simply use an existing update.secondary as a new VREF as-is, no
changes needed.
* Arguments **4 and 5**: the 'oldtree' and 'newtree' SHAs. These are the
same as the oldsha and newsha values, except if one of them is all-0.
(indicating a ref creation or deletion). In that case the corresponding
'tree' SHA is set (by gitolite, as a courtesy) to the special SHA
`4b825dc642cb6eb9a060e54bf8d69288fbee4904`, which is the hash of an empty
tree.
(None of these shenanigans would have been needed if `git diff $oldsha
$newsha` would not error out when passed an all-0 SHA.)
* Argument **6**: the attempted access flag. Typically `W` or `+`, but
could also be `C`, `D`, or any of these 4 followed by `M`. If you have to
ask what they mean, you haven't read enough gitolite documentation to be
able to make virtual refs work.
* Argument **7**: is the entire refex; in our example
`VREF/COUNT/3/NEWFILES`.
* Arguments **8 onward**: are the split out (by `/`) portions of the refex,
excluding the first two components. In our example they would be `3`
followed by `NEWFILES`.
Yes, argument 7 is redundant if you have 8 and 9. It's meant to make it easy
to write vref scripts in any language. See script examples in source.
## what (else) can the vref code pass back
Actually, the vref code can pass anything back; each line in its output that
starts with `VREF/` will be matched against all the rules as usual (with the
exception that fallthru is not failure).
For example, you could have a ruleset like this:
repo r1
# ... normal rules ...
- VREF/TIME/WEEKEND = @interns
- VREF/TIME/WEEKNIGHT = @interns
- VREF/TIME/HOLIDAY = @interns
and you could write the TIME vref code to passback any or all
of the times that match. Then if an intern tried to access the system, each
rule would trigger a call to gl-bindir/VREF/TIME.
The script should send back any of the applicable times (even more than one,
or none at all, as the case may be). So even if it was invoked using the
first rule, it might pass back (to gitolite) a virtual ref saying
'VREF/TIME/HOLIDAY', which would promptly cause the request to be denied.
## VREFs shipped with gitolite
### #NAME restricting pushes by dir/file name
The "NAME" VREF allows you to restrict pushes by the names of dirs and files
changed.
Here's an example. Say you don't want junior developers pushing changes to
the Makefile, because it's quite complex:
repo foo
RW+ = @senior_devs
RW = @junior_devs
- VREF/NAME/Makefile = @junior_devs
When a senior dev pushes, the VREF is not invoked at all. But when a junior
dev pushes, the VREF is invoked, and it returns a list of files changed **as
refs**, looking like this:
VREF/NAME/file-1
VREF/NAME/dir-2/file-3
...etc...
Each of these refs is matched against the access rules. If one of them
happens to be the Makefile, then the ref returned (VREF/NAME/Makefile) will
match the deny rule and kill the push.
Another way to use this is when you know what is allowed instead of what is
not allowed. Let's say the QA person is only allowed to touch a file called
CHANGELOG and any files in a directory called ReleaseNotes:
repo foo
RW+ = @senior_devs
RW = @junior_devs
RW+ = QA-guy
RW+ VREF/NAME/CHANGELOG = QA-guy
RW+ VREF/NAME/ReleaseNotes/ = QA-guy
- VREF/NAME/ = QA-guy
### number of new files
The COUNT VREF is used like this:
- VREF/COUNT/9 = @junior-developers
In response, if anyone in the user list pushes a commit series that
changes more than 9 files, a vref of "VREF/COUNT/9" is returned. Gitolite
uses that as a "ref" to match against all the rules, hits the same rule
that invoked it, and denies the request.
If the user did not push more than 9 files, the VREF code returns nothing,
and nothing happens.
COUNT can take one more argument:
- VREF/COUNT/9/NEWFILES = @junior-developers
This is the same as before, but have to be more than 9 *new* files not
just changed files.
### advanced filetype detection
Note: this is more for illustration than use; it's rather specific to one of
the projects I manage but the idea is the important thing.
Sometimes a file has a standard extension (that cannot be 'gitignore'd), but
it is actually automatically generated. Here's one way to catch it:
- VREF/FILETYPE/AUTOGENERATED = @all
You can look at `src/VREF/FILETYPE` to see how it handles the
'AUTOGENERATED' option. You could also have a more generic option, like
perhaps BINARY, and handle that in the FILETYPE vref too.
### checking author email
Some people want to ensure that "you can only push your own commits".
If you force it on everyone, this is a very silly idea (see "Philosophical
Notes" section of `src/VREF/EMAIL-CHECK`).
But there may be value in enforcing it just for the junior developers.
The neat thing is that the existing `contrib/update.email-check` was just
copied to `src/VREF/EMAIL-CHECK` and it works, because VREFs get
the same first 3 arguments and those are all that it cares about. (Note: you
have to change one subroutine in that script if you want to use it)
### #votes voting on commits
Although gitolite can't/won't do the whole "code review + workflow
enforcement" thing that Gerrit Code Review does, a basic implementation of
voting on a commit is surprisingly easy. See src/VREF/VOTES for details (and
note that the actual *code* is just 2-3 lines; the rest is inline
documentation).
## other ideas -- code welcome!
### "no non-merge first-parents"
Shruggar on #gitolite wanted this. Possible code to implement it would be
something like this (untested)
[ -z "$(git rev-list --first-parent --no-merges $2..$3)" ]
This can be implemented using `src/VREF/MERGE-CHECK` as a model. That script
does what the 'M' qualifier does in access rules (see last part of
[this][write-types]), although the syntax to be used in conf/gitolite will be
quite different.
### other ideas for VREFs
Here are some more ideas:
* Number of commits (`git rev-list --count $old $new`).
* Number of binary files in commit (currently I only know to count
occurrences of ` Bin ` in the output of `git diff --stat`.
* Number of *new* binary files (count ` Bin 0 ->` in `git diff --stat`
output).
* Time of day/day of week (see example snippet somewhere above).
* IP address.
* Phase of the moon.
Note that pretty much anything that involves `$oldsha..$newsha` will have to
deal with the issue that when you push a new tag or branch, the "old" part
is all 0's, and unless you consider `--all` existing branches and tags it
becomes meaningless in terms of "number of new files" etc.

97
doc/why.mkd Normal file
View file

@ -0,0 +1,97 @@
# why might you need gitolite
[[TOC]]
----
## basic use case
Gitolite is useful in any server that is going to host multiple git
repositories, each with many developers, where "anyone can do anything to any
repo" is not a good idea. Here're two examples to illustrate.
Example 1, 3 repos, 3 developers with different levels of access to each repo:
repo foo
RW+ = alice
R = bob
repo bar
RW+ = bob
R = alice
repo baz
RW+ = carol
R = alice bob
Example 2, one repo, but different levels of access to different branches and
tags for different developers:
repo foo
RW+ master = alice
RW+ dev/ = bob
RW refs/heads/tags/v[0-9] = ashok
## #alt alternatives
### unix permissions and ACLs
If you're a masochist, you could probably do example 1 with Unix permissions
and facls. But you can't do example 2 -- git is not designed to allow that!
Here are some other disadvantages of the Unix ACL method:
* Every user needs a userid and password on the server.
* Changing access rights involves complex `usermod -G ...` mumblings
(I.e., the "pain" mentioned above is not a one-time pain!)
* *Viewing* the current set of permissions requires running multiple
commands to list directories and their permissions/ownerships, users and
their group memberships, and then correlating all these manually.
* Auditing historical permissions or permission changes is impossible.
### #gcr Gerrit Code Review
The best real alternative to gitolite is Gerrit Code Review. If code review
is an essential part of your workflow, you should use Gerrit.
Here're some high level differences between gitolite and Gerrit:
**Size**: 3000+ lines of perl versus of 56,000+ lines of Java
**Architecture**: Gitolite sits on top of "standard" git and openssh, which
are assumed to already be installed. Gerrit includes its own git stack (jgit)
and sshd (Apache Mina). In Java tradition, they all come bundled together.
(Corollary: As far as I know jgit does not support the same hooks that 'man
githooks' talks about).
Gitolite uses a plain text config file; gerrit uses a database.
**User view**: Gitolite is invisible to users except when access is denied. I
think Gerrit is much more visible to devs.
On a related note, gitolite does not do anything special with signed or
annotated tags, nor does it check author/committer identity. However, it is
trivial to add your own code to do either (or if someone contributes it, to
just "enable" what ships with gitolite in a disabled state).
### gitorious
Anecdotally, gitorious is very hard to install. Comparison with gitolite may
be useless because I believe it doesn't have branch/tag level access control.
However, I can't confirm or deny this because I can't find any documentation
on the website.
In addition, the main website hides the source code very well, so you already
have a challenge! [The only link I could find was tucked away at the bottom
of the About page, in the License section].
### gitlab
Gitlab is built on top of gitolite, but I don't know more than that as yet.
Patches welcome.
### others
Please send in patches to this doc if you know of other open source git
hosting solutions that do access control.

157
doc/wild.mkd Normal file
View file

@ -0,0 +1,157 @@
# "wild" repos (user created repos)
## quick introduction
The wildrepos feature allows you to specify access control rules using regular
expression patterns, so you can have many actual repos being served by a
single set of rules in the config file. The regex pattern can also include
the word `CREATOR` in it, allowing you to parametrise the name of the user
creating the repo.
See the section on "repo patterns" later for additional information on what
counts as a "wild" repo pattern and how it is matched.
## (admin) declaring wild repos in the conf file
Here's an example:
@prof = u1
@TAs = u2 u3
@students = u4 u5 u6
repo assignments/CREATOR/a[0-9][0-9]
C = @students
RW+ = CREATOR
RW = WRITERS @TAs
R = READERS @prof
Note the "C" permission. This is a standalone "C", which gives the named
users the right to *create a repo*. <font color="gray">This is not to be
confused with the "RWC" or its variants described elsewhere, which are about
*branches*, not *repos*.</font>
## #create (user) creating a specific repo
For now, ignore the special usernames READERS and WRITERS, and just create a
new repo, as user "u4" (a student):
$ git clone git@server:assignments/u4/a12
Initialized empty Git repository in /home/sitaram/a12/.git/
Initialized empty Git repository in /home/git/repositories/assignments/u4/a12.git/
warning: You appear to have cloned an empty repository.
Notice the *two* empty repo inits, and the order in which they occur ;-)
## a slightly different example
Here's how the same example would look if you did not want the CREATOR's name
to be part of the actual repo name.
repo assignments/a[0-9][0-9]
C = @students
RW+ = CREATOR
RW = WRITERS @TAs
R = READERS @prof
We haven't changed anything except the repo name pattern. This means that the
first student that creates, say, `assignments/a12` becomes the owner.
Mistakes (such as claiming a12 instead of a13) need to be rectified by an
admin logging on to the back end, though it's not too difficult.
You could also repace the C line like this:
C = @TAs
and have a TA create the repos in advance.
## repo patterns
### pattern versus normal repo
Due to projects like `gtk+`, the `+` character is now considered a valid
character for an *ordinary* repo. Therefore, a pattern like `foo/.+` does not
look like a regex to gitolite. Use `foo/..*` if you want that.
Also, `..*` by itself is not considered a valid repo pattern. Try
`[a-zA-Z0-9].*`.
### line-anchored regexes
A regex like
repo assignments/S[0-9]+/A[0-9]+
would match `assignments/S02/A37`. It will not match `assignments/S02/ABC`,
or `assignments/S02/a37`, obviously.
But you may be surprised to find that it does not match even
`assignments/S02/A37/B99`. This is because internally, gitolite
*line-anchors* the given regex; so that regex actually becomes
`^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending
metacharacters.
> ----
> *Side-note: contrast with refexes*
> Just for interest, note that this is in contrast to the refexes for the
> normal "branch" permissions, as described in `doc/gitolite.conf.mkd` and
> elsewhere. These "refexes" are only anchored at the start; a pattern like
> `refs/heads/master` actually can match `refs/heads/master01/bar` as well,
> even if no one will actually push such a branch! You can anchor both
> sides if you really care, by using `master$` instead of `master`, but that
> is *not* the default for refexes.
> ----
## roles
The tokens READERS and WRITERS are called "role" names. The access rules in
the conf file decide what permissions these roles have, but they don't say
what users are in each of these roles.
That needs to be done by the creator of the repo, using the `perms` command.
You can run `ssh git@host perms -h` for detailed help, but in brief, that
command lets you give and take away roles to users. [This][perms] has some
more detail.
## adding other roles
If you want to have more than just the 2 default roles, say something like:
repo foo/..*
C = u1
RW refs/tags/ = TESTERS
- refs/tags/ = @all
RW+ = WRITERS
RW = INTERNS
R = READERS
RW+D = MANAGERS
You can add the new names to the ROLES hash in the [rc][] file. Be sure to
run the 2 commands mentioned there after you have added the roles.
file. The rc file documentation (`doc/gitolite.rc.mkd`) explains how.
#### #rolenamewarn **IMPORTANT WARNING ABOUT THIS FEATURE**
Please make sure that none of the role names conflict with any of the user
names or group names in the system. For example, if you have a user called
"foo" or a group called "@foo", make sure you do not include "foo" as a valid
role in the ROLES hash.
You can keep things sane by using UPPERCASE names for roles, while keeping all
your user and group names lowercase; then you don't have to worry about this
problem.
## listing wild repos
In order to see what repositories were created from a wildcard, use the 'info'
command. Try `ssh git@host info -h` to get help on the info command.
## deleting a wild repo
Run the whimsically named "D" command -- try `ssh git@host D -h` for more info
on how to delete a wild repo. (Yes the command is "D"; it's meant to be a
counterpart to the "C" permission that allowed you to create the repo in the
first place). Of course this only works if your admin has enabled the command
(gitolite ships with the command disabled for remote use).

31
doc/write-types.mkd Normal file
View file

@ -0,0 +1,31 @@
## #write-types different types of write operations
Git supplies enough information to the update hook to be able to distinguish
several types of writes.
The most common are:
* `RW` -- create a ref or fast-forward push a ref. No rewinds or deletes.
* `RW+` -- create, fast-forward push, rewind push, or delete a ref.
Sometimes you want to allow people to push, but not *create* a ref. Or
rewind, but not *delete* a ref. The `C` and `D` qualifiers help here.
* When a rule specifies `RWC` or `RW+C`, then *rules that do NOT have the C
qualifier will no longer permit **creating** a ref*.
<font color="gray">Please do not confuse this with the standalone `C`
permission that allows someone to [create][] a **repo**</font>
* When a rule specifies `RWD` or `RW+D`, then *rules that do NOT have the C
qualifier will no longer permit **deleting** a ref*.
Note: These two can be combined, so you can have `RWCD` and `RW+CD` as well.
One very rare need is to reject merge commits (a commit series that is not a
straight line of commits). The `M` qualifier helps here:
* When a rule has `M` appended to the permissions, *rules that do NOT have
it will reject a commit sequence that contains a merge commit* (i.e., they
only accept a straight line series of commits).

31
dot.pl Normal file
View file

@ -0,0 +1,31 @@
#!/usr/bin/perl
use strict;
use warnings;
my @a = `grep -r use.Gitolite . | grep -i '^./gitolite'`;
# chomp(@a);
open( my $fh, "|-", "tee module-tree.gv | dot -Tpng | tee module-tree.png | display" );
@a = map {
print $fh "#$_";
s/^\.\/gitolite\///i;
s/-/_/g;
s/\.\///;
s/\//_/g;
s/\.pm:/ -> /;
s/use Gitolite:://;
s/::/_/g;
s/:/ -> /;
s/;//;
s/^(\S+) -> \1$//;
s/.* -> Rc//;
s/.* -> Common//;
$_;
} @a;
# open(my $fh, "|-", "cat > /tmp/junkg3");
print $fh "digraph G {\n";
print $fh $_ for @a;
print $fh "}\n";
close $fh;

View file

@ -36,7 +36,7 @@ Usage (from gitolite clone directory):
Simplest use, if $HOME/bin exists and is in $PATH, is:
git clone git://github.com/sitaramc/gitolite
git clone -b g3 git://github.com/sitaramc/gitolite
gitolite/install -ln
# now run setup

View file

@ -1,4 +1,5 @@
#!/bin/sh
#!/bin/bash
# TODO: convert to perl!
# gitolite VREF to count number of changed/new files in a push
@ -31,9 +32,9 @@ nf=
# count files against all the other commits in the system not just $oldsha
# (why? consider what is $oldtree when you create a new branch, or what is
# $oldsha when you update an old feature branch from master and then push it
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | perl -ne '}{print "$."'`
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | wc -l`
[ $count -gt $max ] && {
[[ $count -gt $max ]] && {
# count has been exceeded. If $9 was NO_SIGNOFF there's still a chance
# for redemption -- if the top commit has a proper signed-off by line
[ "$9" = "NO_SIGNOFF" ] && {

View file

@ -1,4 +1,5 @@
#!/bin/sh
#!/bin/bash
# TODO: convert to perl!
# gitolite VREF to find autogenerated files

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# gitolite VREF to count votes before allowing pushes to certain branches.

View file

@ -1,36 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Common;
# gitolite VREF to lock and unlock (binary) files. Requires companion command
# 'lock' to be enabled; see doc/locking.mkd for details.
# ----------------------------------------------------------------------
# see gitolite docs for what the first 7 arguments mean
die "not meant to be run manually" unless $ARGV[6];
my $ff = "$ENV{GL_REPO_BASE}/$ENV{GL_REPO}.git/gl-locks";
exit 0 unless -f $ff;
our %locks;
my $t = slurp($ff);
eval $t;
_die "do '$ff' failed with '$@', contact your administrator" if $@;
my ( $oldtree, $newtree, $refex ) = @ARGV[ 3, 4, 6 ];
for my $file (`git diff --name-only $oldtree $newtree` ) {
chomp($file);
if ($locks{$file} and $locks{$file}{USER} ne $ENV{GL_USER}) {
print "$refex '$file' locked by '$locks{$file}{USER}'";
last;
}
}
exit 0

View file

@ -23,15 +23,6 @@ main=`git config --file $GL_REPO_BASE/$repo.git/config --get gitolite.partialCop
rand=$$
export GL_BYPASS_ACCESS_CHECKS=1
if [ "$new" = "0000000000000000000000000000000000000000" ]
then
# special case for deleting a ref (this is why it is important to put this
# VREF as the last one; if we got this far he is allowed to delete it)
git push -f $GL_REPO_BASE/$main.git :$ref || die "FATAL: failed to delete $ref"
exit 0
fi
git push -f $GL_REPO_BASE/$main.git $new:refs/partial/br-$rand || die "FATAL: failed to send $new"
cd $GL_REPO_BASE/$main.git

View file

@ -1,75 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
my $rule = $ARGV[7];
die "\n\nFATAL: GL_REFEX_EXPR_ doesn't exist\n(your admin probably forgot the rc file change needed for this to work)\n\n"
unless exists $ENV{"GL_REFEX_EXPR_" . $rule};
my $res = $ENV{"GL_REFEX_EXPR_" . $rule} || 0;
print "$ARGV[6] ($res)\n" if $res;
exit 0;
__END__
Documentation for the refex-expression evaluation feature
First, make sure you have both the VREF and the trigger scripts
(src/VREF/refex-expr and src/lib/Gitolite/Triggers/RefexExpr.pm)
Next, add this to the ACCESS_2 list in the rc file:
'RefexExpr::access_2',
For the rest, we'll use this example:
* user u1 can push foo to some other branch, and anything else to the master
branch, but not foo to the master branch
* user u2 is allowed to push either 'doc/' or 'src/' but not both
Here's the conf file extract:
repo testing
RW+ master = u1 # line 1
RW+ = @all # line 2
RW+ VREF/NAME/foo = u1
RW+ VREF/NAME/doc/ = u2
RW+ VREF/NAME/src/ = u2
# set up 2 refex expressions, named e1, e2
option refex-expr.e1 = master and VREF/NAME/foo
option refex-expr.e2 = VREF/NAME/doc/ and VREF/NAME/src/
# now deny users if the corresponding expression is true
- VREF/refex-expr/e1 = u1
- VREF/refex-expr/e2 = u2
Here are some IMPORTANT notes:
* You MUST place VREF/refex-expr rules at the end. (Only 'partial-copy', if
you use it, must come later).
* You MUST explicitly permit the refexes used in your refex expressions. If
you have more generic rules, the specific ones must come first.
For example, without line 1, the refex recorded for user u1 will come from
line 2, (so it will be 'refs/.*'), and 'master' in the refex expressions
will never have a true value.
* (corollary) make sure you use the exact same refex in the expression as
you did on the original rule line. E.g., a missing slash at the end will
mess things up.
* You can use any logical expression using refexes as operands and using
these operators:
and not xor or
Parens are not allowed.
If a refex has passed, it will have a 'true' value, else it will be false.
The result of the evaluation, after these substitutions, will be the
result of the refex-expr VREF.

View file

@ -12,17 +12,13 @@
# - run a cron job to delete old repos based on age (the TRASH_SUFFIX has a
# timestamp); your choice how/how often you do that
# - you can completely disable the 'rm' command by setting an rc variable
# called D_DISABLE_RM to "1".
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Usage: ssh git@host D <subcommand> <argument>
#
# The whimsically named "D" command deletes repos ("D" is a counterpart to the
# "C" permission which lets you create repos. Which also means that, just
# like "C", it only works for wild repos).
# "C" permission which lets you create repos!)
#
# There are two kinds of deletions: 'rm' removes a repo completely, while
# 'trash' moves it to a trashcan which can be recovered later (upto a time
@ -49,12 +45,11 @@ die() { echo "$@" >&2; exit 1; }
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
[ -z "$1" ] && usage
[ "$1" = "-h" ] && usage
[ "$1" != "list-trash" ] && [ -z "$2" ] && usage
[ -z "$GL_USER" ] && die GL_USER not set
# ----------------------------------------------------------------------
cmd=$1
repo=$2
cmd=$1; shift
repo=$1; shift # may not be present for 'list-trash' command
# ----------------------------------------------------------------------
RB=`gitolite query-rc GL_REPO_BASE`; cd $RB
TRASH_CAN=`gitolite query-rc TRASH_CAN`; tcan=Trash; TRASH_CAN=${TRASH_CAN:-$tcan}
@ -70,8 +65,6 @@ owner_or_die() {
if [ "$cmd" = "rm" ]
then
gitolite query-rc -q D_DISABLE_RM && die "sorry, 'unlock' and 'rm' are disabled"
owner_or_die
[ -f $repo.git/gl-rm-ok ] || die "'$repo' is locked!"
rm -rf $repo.git
@ -87,8 +80,6 @@ then
elif [ "$cmd" = "unlock" ]
then
gitolite query-rc -q D_DISABLE_RM && die "sorry, 'unlock' and 'rm' are disabled"
owner_or_die
touch $repo.git/gl-rm-ok
echo "'$repo' is now unlocked"

View file

@ -1,15 +0,0 @@
#!/bin/bash
# Usage: ssh git@host create <repo>
#
# Create wild repo.
die() { echo "$@" >&2; exit 1; }
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
[ -z "$1" ] && usage
[ -z "$2" ] || usage
[ "$1" = "-h" ] && usage
[ -z "$GL_USER" ] && die GL_USER not set
# ----------------------------------------------------------------------
exec $GL_BINDIR/commands/perms -c "$@" < /dev/null

View file

@ -10,6 +10,10 @@
# traffic but because it uses git clone's "-l" option to share the object
# store also, so it is likely to be almost instantaneous, regardless of how
# big the repo actually is.
#
# The only caveat is that the repo you cloned *from* must not later become
# unavailable in any way. If you cannot be sure of this, take the scenic
# route (clone repo1, push to repo2).
die() { echo "$@" >&2; exit 1; }
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
@ -44,7 +48,6 @@ echo "$from forked to $to" >&2
cd $GL_REPO_BASE/$to.git
echo $GL_USER > gl-creator
touch gl-perms
if gitolite query-rc -q DEFAULT_ROLE_PERMS
then
gitolite query-rc DEFAULT_ROLE_PERMS > gl-perms
@ -56,4 +59,4 @@ ln -sf `gitolite query-rc GL_ADMIN_BASE`/hooks/common/* hooks
echo "$from" > gl-forked-from
# trigger post_create
gitolite trigger POST_CREATE $to $GL_USER fork
gitolite trigger POST_CREATE $to $GL_USER

View file

@ -20,23 +20,20 @@ usage() if @ARGV;
my $user = $ENV{GL_USER} || '';
print "hello" . ( $user ? " $user" : "" ) . ", this is gitolite3 " . version() . " on git " . substr( `git --version`, 12 ) . "\n";
_chdir("$ENV{GL_BINDIR}/commands");
print "list of " . ( $user ? "remote" : "gitolite" ) . " commands available:\n\n";
my %list = (list_x( $ENV{GL_BINDIR}), list_x($rc{LOCAL_CODE} || ''));
for (sort keys %list) {
print "\t$list{$_}" if $ENV{D};
print "\t$_\n" if not $user or $rc{COMMANDS}{$_};
}
for my $c (`find . -type f|sort`) {
chomp($c);
$c =~ s(^./)();
next unless -x $c;
# if it's from a remote client, show only what he is allowed
next if $user and not $rc{COMMANDS}{$c};
print "\t$c\n";
}
print "\n";
exit 0;
# ------------------------------------------------------------------------------
sub list_x {
my $d = shift;
return unless $d;
return unless -d "$d/commands";
_chdir "$d/commands";
return map { $_ => $d } grep { -x $_ } map { chomp; s(^./)(); $_ } `find . -type f -o -type l|sort`;
}

View file

@ -80,17 +80,13 @@ sub print_phy_repos {
sub listem {
my ( $repos, $lc, $ld, @aa ) = @_;
my $creator = '';
my $desc = '';
for my $repo (@$repos) {
next unless $repo =~ /$patt/;
my $perm = '';
$creator = creator($repo) if $lc;
my $desc = '';
for my $d ("$ENV{GL_REPO_BASE}/$repo.git/description") {
next unless $ld and -r $d;
$desc = slurp($d);
chomp($desc);
}
$desc = slurp("$ENV{GL_REPO_BASE}/$repo.git/description") if $ld;
chomp($desc);
for my $aa (@aa) {
my $ret = access( $repo, $ENV{GL_USER}, $aa, 'any' );

View file

@ -1,55 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Common;
use Gitolite::Conf::Load;
=for usage
Usage: gitolite list-dangling-repos
List all existing repos that no one can access remotely any more. They could
be normal repos that were taken out of "repo" statements in the conf file, or
wildcard repos whose matching "wild" pattern was taken out or changed so it no
longer matches.
I would advise caution if you use this as a basis for deleting repos from the
file system. A bug in this program could cause you to lose important data!
=cut
usage() if @ARGV and $ARGV[0] eq '-h';
# get the two lists we need. %repos is the list of repos in "repo" statements
# in the conf file. %phy_repos is the list of actual repos on disk. Our job
# is to cull %phy_repos of all keys that have a matching key in %repos, where
# "matching" means "string equal" or "regex match".
my %repos = map { chomp; $_ => 1 } `gitolite list-repos`;
for my $r ( grep /^@/, keys %repos ) {
map { chomp; $repos{$_} = 1; } `gitolite list-members $r`;
}
my %phy_repos = map { chomp; $_ => 1 } `gitolite list-phy-repos`;
# Remove exact matches. But for repo names like "gtk+", you could have
# collapsed this into the next step (the regex match).
for my $pr (keys %phy_repos) {
next unless exists $repos{$pr};
delete $repos{$pr};
delete $phy_repos{$pr};
}
# Remove regex matches.
for my $pr (keys %phy_repos) {
my $matched = 0;
my $pr2 = Gitolite::Conf::Load::generic_name($pr);
for my $r (keys %repos) {
if ($pr =~ /^$r$/ or $pr2 =~ /^$r$/) {
$matched = 1;
next;
}
}
delete $phy_repos{$pr} if $matched;
}
# what's left in %phy_repos are dangling repos.
print join("\n", sort keys %phy_repos), "\n";

View file

@ -1,124 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Conf::Load;
# gitolite command to lock and unlock (binary) files and deal with locks.
=for usage
Usage: ssh git@host lock -l <repo> <file> # lock a file
ssh git@host lock -u <repo> <file> # unlock a file
ssh git@host lock --break <repo> <file> # break someone else's lock
ssh git@host lock -ls <repo> # list locked files for repo
See doc/locking.mkd for other details.
=cut
usage() if not @ARGV or $ARGV[0] eq '-h';
$ENV{GL_USER} or _die "GL_USER not set";
my $op = '';
$op = 'lock' if $ARGV[0] eq '-l';
$op = 'unlock' if $ARGV[0] eq '-u';
$op = 'break' if $ARGV[0] eq '--break';
$op = 'list' if $ARGV[0] eq '-ls';
usage() if not $op;
shift;
my $repo = shift;
_die "You are not authorised" if access( $repo, $ENV{GL_USER}, 'W', 'any' ) =~ /DENIED/;
_die "You are not authorised" if $op eq 'break' and access( $repo, $ENV{GL_USER}, '+', 'any' ) =~ /DENIED/;
my $file = shift || '';
usage() if $op ne 'list' and not $file;
_chdir( $ENV{GL_REPO_BASE} );
_chdir("$repo.git");
my $ff = "gl-locks";
if ( $op eq 'lock' ) {
f_lock( $repo, $file );
} elsif ( $op eq 'unlock' ) {
f_unlock( $repo, $file );
} elsif ( $op eq 'break' ) {
f_break( $repo, $file );
} elsif ( $op eq 'list' ) {
f_list($repo);
}
# ----------------------------------------------------------------------
# everything below assumes we have already chdir'd to "$repo.git". Also, $ff
# is used as a global.
sub f_lock {
my ( $repo, $file ) = @_;
my %locks = get_locks();
_die "'$file' locked by '$locks{$file}{USER}' since " . localtime( $locks{$file}{TIME} ) if $locks{$file}{USER};
$locks{$file}{USER} = $ENV{GL_USER};
$locks{$file}{TIME} = time;
put_locks(%locks);
}
sub f_unlock {
my ( $repo, $file ) = @_;
my %locks = get_locks();
_die "'$file' not locked by '$ENV{GL_USER}'" if ( $locks{$file}{USER} || '' ) ne $ENV{GL_USER};
delete $locks{$file};
put_locks(%locks);
}
sub f_break {
my ( $repo, $file ) = @_;
my %locks = get_locks();
_die "'$file' was not locked" unless $locks{$file};
push @{ $locks{BREAKS} }, time . " $ENV{GL_USER} $locks{$file}{USER} $locks{$file}{TIME} $file";
delete $locks{$file};
put_locks(%locks);
}
sub f_list {
my $repo = shift;
my %locks = get_locks();
print "\n# locks held:\n\n";
map { print "$locks{$_}{USER}\t$_\t(" . scalar(localtime($locks{$_}{TIME})) . ")\n" } grep { $_ ne 'BREAKS' } sort keys %locks;
print "\n# locks broken:\n\n";
for my $b ( @{ $locks{BREAKS} } ) {
my ( $when, $who, $whose, $how_old, $what ) = split ' ', $b;
print "$who\t$what\t(" . scalar( localtime($when) ) . ")\t(locked by $whose at " . scalar( localtime($how_old) ) . ")\n";
}
}
sub get_locks {
if ( -f $ff ) {
our %locks;
my $t = slurp($ff);
eval $t;
_die "do '$ff' failed with '$@', contact your administrator" if $@;
return %locks;
}
return ();
}
sub put_locks {
my %locks = @_;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
my $dumped_data = Data::Dumper->Dump( [ \%locks ], [qw(*locks)] );
_print( $ff, $dumped_data );
}

View file

@ -45,19 +45,12 @@ if ( $cmd eq 'push' ) {
_chdir( $rc{GL_REPO_BASE} );
_chdir("$repo.git");
if (-f "gl-creator") {
# try to propagate the wild repo, including creator name and gl-perms
my $creator = `cat gl-creator`; chomp($creator);
trace(1, `cat gl-perms 2>/dev/null | ssh $host CREATOR=$creator perms -c \\'$repo\\' 2>/dev/null`);
}
my $errors = 0;
for (`git push --mirror $host:$repo 2>&1`) {
$errors = 1 if $?;
print STDERR "$_" if -t STDERR or exists $ENV{GL_USER};
chomp;
if (/FATAL/) {
$errors = 1;
$errors++;
gl_log( 'mirror', $_ );
} else {
trace( 1, "mirror: $_" );

View file

@ -21,21 +21,12 @@ Examples:
ssh git@host perms foo + READERS user2
ssh git@host perms foo + READERS user3
----
There is also a batch mode useful for scripting and bulk loading. Do not
combine this with the +/- mode above. This mode also accepts an optional "-c"
flag to create the repo if it does not already exist (assuming $GL_USER has
permissions to create it).
Examples:
cat copy-of-backed-up-gl-perms | ssh git@host perms <repo>
cat copy-of-backed-up-gl-perms | ssh git@host perms -c <repo>
(Note: a legacy mode of piping in the entire permissions text directly is also
supported. If you want to use it, don't mix it with the new "+/-" modes).
=cut
usage() if not @ARGV or $ARGV[0] eq '-h';
$ENV{GL_USER} or _die "GL_USER not set";
my $list = 0;
if ( $ARGV[0] eq '-l' ) {
$list++;
@ -43,34 +34,15 @@ if ( $ARGV[0] eq '-l' ) {
getperms(@ARGV); # doesn't return
}
my $generic_error = "repo does not exist, or you are not authorised";
# auto-create the repo if -c passed and repo doesn't exist
if ( $ARGV[0] eq '-c' ) {
shift;
my $repo = $ARGV[0] or usage();
_die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;
if (not -d "$rc{GL_REPO_BASE}/$repo.git") {
my $ret = access( $repo, $ENV{GL_USER}, '^C', 'any' );
_die $generic_error if $ret =~ /DENIED/;
require Gitolite::Conf::Store;
Gitolite::Conf::Store->import;
new_wild_repo( $repo, $ENV{GL_USER}, 'perms-c' );
gl_log( 'create', $repo, $ENV{GL_USER}, 'perms-c' );
}
}
my $repo = shift;
setperms(@ARGV);
_system( "gitolite", "trigger", "POST_CREATE", $repo, $ENV{GL_USER}, 'perms' );
_system( "gitolite", "trigger", "POST_CREATE", $repo, $ENV{GL_USER} );
# ----------------------------------------------------------------------
sub getperms {
my $repo = shift;
_die $generic_error if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
_die "sorry you are not authorised" if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";
print slurp($pf) if -f $pf;
@ -79,20 +51,17 @@ sub getperms {
}
sub setperms {
_die $generic_error if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
_die "sorry you are not authorised" if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";
if ( not @_ ) {
# legacy mode; pipe data in
print STDERR "'batch' mode started, waiting for input (run with '-h' for details).\n";
print STDERR "Please hit Ctrl-C if you did not intend to do this.\n";
@ARGV = ();
my @a;
for (<>) {
_die "Invalid role '$1'; check the rc file" if /(\S+)/ and not $rc{ROLES}{$1};
push @a, $_;
}
print STDERR "\n"; # make sure Ctrl-C gets caught
_print( $pf, @a );
return;
}

View file

@ -1,149 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Easy;
=for admins
BUNDLE SUPPORT
(1) For each repo in gitolite.conf for which you want bundle support (or
'@all', if you wish), add the following line:
option bundle = 1
Or you can say:
option bundle.ttl = <number>
A bundle file that is more than <number> seconds old (default value
86400, i.e., 1 day) is recreated on the next bundle request. Increase
this if your repo is not terribly active.
Note: a bundle file is also deleted and recreated if it contains a ref
that was then either deleted or rewound in the repo. This is checked
on every invocation.
(2) Add 'rsync' to the COMMANDS list in the rc file
GENERIC RSYNC SUPPORT
TBD
=cut
=for usage
rsync helper for gitolite
BUNDLE SUPPORT
Admins: see src/commands/rsync for setup instructions
Users:
rsync -P git@host:repo.bundle .
# downloads a file called "<basename of repo>.bundle"; repeat as
# needed till the whole thing is downloaded
git clone repo.bundle repo
cd repo
git remote set-url origin git@host:repo
git fetch origin # and maybe git pull, etc. to freshen the clone
GENERIC RSYNC SUPPORT
TBD
=cut
usage() if not @ARGV or $ARGV[0] eq '-h';
# rsync driver program. Several things can be done later, but for now it
# drives just the 'bundle' transfer.
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^rsync --server --sender (-[-\w=.]+ )+\. (\S+)\.bundle$/ ) {
my $repo = $2;
$repo =~ s/\.git$//;
# all errors have the same message to avoid leaking info
can_read($repo) or _die "you are not authorised";
my %config = config( $repo, "gitolite-options.bundle" ) or _die "you are not authorised";
my $ttl = $config{'gitolite-options.bundle.ttl'} || 86400; # in seconds (default 1 day)
my $bundle = bundle_create( $repo, $ttl );
$ENV{SSH_ORIGINAL_COMMAND} =~ s( \S+\.bundle)( $bundle);
trace( 1, "rsync bundle", $ENV{SSH_ORIGINAL_COMMAND} );
Gitolite::Common::_system( split ' ', $ENV{SSH_ORIGINAL_COMMAND} );
exit 0;
}
_warn "invalid rsync command '$ENV{SSH_ORIGINAL_COMMAND}'";
usage();
# ----------------------------------------------------------------------
# helpers
# ----------------------------------------------------------------------
sub bundle_create {
my ( $repo, $ttl ) = @_;
my $bundle = "$repo.bundle";
$bundle =~ s(.*/)();
my $recreate = 0;
my ( %b, %r );
if ( -f $bundle ) {
%b = map { chomp; reverse split; } `git ls-remote --heads --tags $bundle`;
%r = map { chomp; reverse split; } `git ls-remote --heads --tags .`;
for my $ref ( sort keys %b ) {
my $mtime = ( stat $bundle )[9];
if ( time() - $mtime > $ttl ) {
trace( 1, "bundle too old" );
$recreate++;
last;
}
if ( not $r{$ref} ) {
trace( 1, "ref '$ref' deleted in repo" );
$recreate++;
last;
}
if ( $r{$ref} eq $b{$ref} ) {
# same on both sides; ignore
delete $r{$ref};
delete $b{$ref};
next;
}
`git rev-list --count --left-right $b{$ref}...$r{$ref}` =~ /^(\d+)\s+(\d+)$/ or _die "git too old";
if ($1) {
trace( 1, "ref '$ref' rewound in repo" );
$recreate++;
last;
}
}
} else {
trace( 1, "no bundle found" );
$recreate++;
}
return $bundle if not $recreate;
trace( 1, "creating bundle for '$repo'" );
-f $bundle and ( unlink $bundle or die "a horrible death" );
system("git bundle create $bundle --branches --tags >&2");
return $bundle;
}
sub trace {
Gitolite::Common::trace(@_);
}

View file

@ -73,7 +73,7 @@ for my $pkf (@pubkeyfiles) {
my $fp = fprint($pkf);
next unless $fp;
msg 1, "$pkfsn appears to be a COPY of $pkf_by_fp{$fp}\n" if $pkf_by_fp{$fp};
$pkf_by_fp{$fp} ||= $pkfsn;
$pkf_by_fp{$fp} ||= $pkf;
my $fpu = ( $seen_fprints{$fp}{user} || 'no access' );
msg 0, "$pkfsn maps to $fpu\n";
}
@ -170,20 +170,17 @@ sub fprint {
sub usage {
print <<EOF;
Usage: gitolite sshkeys-lint [-q] [optional list of pubkey filenames]
(optionally, STDIN can be a pipe or redirected from a file; see below)
sshkeys-lint expects
- the contents of an authorized_keys file via STDIN
- one or more pubkey filenames as arguments
Look for potential problems in ssh keys.
sample use to check all keys on gitolite server:
cd ~/.gitolite/keydir
cat ~/.ssh/authorized_keys | sshkeys-lint `find . -name "*.pub"`
# or supply only one pubkey file to check only that:
cat ~/.ssh/authorized_keys | sshkeys-lint YourName.pub
sshkeys-lint expects:
- the contents of an authorized_keys file via STDIN, otherwise it uses
\$HOME/.ssh/authorized_keys
- one or more pubkey filenames as arguments, otherwise it uses all the keys
found (recursively) in \$HOME/.gitolite/keydir
The '-q' option will print only warnings instead of all mappings.
Note that this runs ssh-keygen -l for each line in the authkeys file and each
Note that it runs ssh-keygen -l for each line in the authkeys file and each
pubkey in the argument list, so be wary of running it on something huge. This
is meant for troubleshooting.

View file

@ -1,24 +0,0 @@
#!/bin/sh
# Usage: ssh git@host sudo <user> <command> <arguments>
#
# Let super-user run commands as any other user. "Super-user" is defined as
# "have write access to the gitolite-admin repo".
die() { echo "$@" >&2; exit 1; }
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
[ -z "$2" ] && usage
[ "$1" = "-h" ] && usage
[ -z "$GL_USER" ] && die GL_USER not set
gitolite access -q gitolite-admin $GL_USER W any || die "You are not authorised"
user="$1"; shift
cmd="$1"; shift
# switch user
GL_USER="$user"
# figure out if the command is allowed from a remote user
gitolite query-rc -q COMMANDS $cmd || die "Command '$cmd' not allowed"
gitolite $cmd "$@"

View file

@ -1,31 +0,0 @@
#!/bin/sh
# Usage: ssh git@host symbolic-ref <repo> <arguments to git-symbolic-ref>
#
# allow 'git symbolic-ref' over a gitolite connection
# Security: remember all arguments to commands must match a very conservative
# pattern. Once that is assured, the symbolic-ref command has no security
# related side-effects, so we don't check arguments at all.
# Note: because of the restriction on allowed characters in arguments, you
# can't supply an arbitrary string to the '-m' option. The simplest
# work-around is-to-just-use-join-up-words-like-this if you feel the need to
# supply a "reason" string. In any case this is useless by default; you'd
# have to have core.logAllRefUpdates set for it to have any meaning.
die() { echo "$@" >&2; exit 1; }
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
[ -z "$1" ] && usage
[ "$1" = "-h" ] && usage
[ -z "$GL_USER" ] && die GL_USER not set
# ----------------------------------------------------------------------
repo=$1; shift
repo=${repo%.git}
gitolite access -q "$repo" $GL_USER W any || die You are not authorised
# change head
cd $GL_REPO_BASE/$repo.git
git symbolic-ref "$@"

View file

@ -1,57 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Easy;
=for usage
Usage: ssh git@host who-pushed <repo> <SHA>
Determine who pushed the given commit. The first few hex digits of the SHA
should suffice.
Each line of the output contains the following fields: timestamp, a
transaction ID, username, refname, and the old and new SHAs for the ref.
We assume the logfile names have been left as default, or if changed, in such
a way that they come up oldest first when sorted.
The program searches ALL the log files, in reverse sorted order (i.e., newest
first). This means it could take a long time if your log directory is large
and contains lots of old log files. Patches to limit the search to an
optional date range are welcome.
Note on the "transaction ID" field: if looking at the log file doesn't help
you figure out what its purpose is, please just ignore it.
=cut
usage() if not @ARGV or @ARGV < 2 or $ARGV[0] eq '-h';
usage() if $ARGV[1] !~ /^[0-9a-f]+$/i;
my $repo = shift;
my $sha = shift; $sha =~ tr/A-F/a-f/;
$ENV{GL_USER} and ( can_read($repo) or die "no read permissions on '$repo'" );
# ----------------------------------------------------------------------
my $repodir = "$ENV{GL_REPO_BASE}/$repo.git";
chdir $repodir or die "repo '$repo' missing";
(my $logdir = $ENV{GL_LOGFILE}) =~ s(/[^/]+$)();
for my $logfile ( reverse glob("$logdir/*") ) {
@ARGV = ($logfile);
for my $line ( reverse grep { m(\tupdate\t($repo|$repodir)\t) } <> ) {
chomp($line);
my @fields = split /\t/, $line;
my ($ts, $pid, $who, $ref, $d_old, $new) = @fields[ 0, 1, 4, 6, 7, 8];
# d_old is what you display
my $old = $d_old;
$old = "" if $d_old eq ("0" x 40);
$old = "$old.." if $old;
system("git rev-list $old$new 2>/dev/null | grep ^$sha >/dev/null && echo '$ts $pid $who $ref $d_old $new'");
}
}

View file

@ -73,10 +73,9 @@ if ( $command eq 'setup' ) {
} elsif ( $command eq 'trigger' ) {
trigger(@args);
} elsif ( my $c = _which("commands/$command", 'x' ) ) {
trace( 2, "attempting gitolite command $c" );
_system( $c, @args );
exit 0;
} elsif ( -x "$rc{GL_BINDIR}/commands/$command" ) {
trace( 2, "attempting gitolite command $command" );
run_command( $command, @args );
} elsif ( $command eq 'list-phy-repos' ) {
_chdir( $rc{GL_REPO_BASE} );
@ -100,3 +99,11 @@ sub args {
}
# ----------------------------------------------------------------------
sub run_command {
my $pgm = shift;
my $fullpath = "$ENV{GL_BINDIR}/commands/$pgm";
_die "$pgm not found or not executable" if not -x $fullpath;
_system( $fullpath, @_ );
exit 0;
}

View file

@ -31,12 +31,6 @@ if ( exists $ENV{G3T_USER} ) {
_die "who the *heck* are you?";
}
# sanity...
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
$soc =~ s/[\n\r]+/<<newline>>/g;
_die "I don't like newlines in the command: '$soc'\n" if $ENV{SSH_ORIGINAL_COMMAND} ne $soc;
# the INPUT trigger massages @ARGV and $ENV{SSH_ORIGINAL_COMMAND} as needed
trigger('INPUT');
main($id);
@ -81,6 +75,9 @@ sub in_ssh {
gl_log( 'ssh', "ARGV=" . join( ",", @ARGV ), "SOC=" . ( $ENV{SSH_ORIGINAL_COMMAND} || '' ), "FROM=$ip" );
$ENV{SSH_ORIGINAL_COMMAND} ||= '';
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
$soc =~ s/[\n\r]+/<<newline>>/g;
_die "I don't like newlines in the command: $soc\n" if $ENV{SSH_ORIGINAL_COMMAND} ne $soc;
return $ip;
}
@ -107,8 +104,8 @@ sub main {
if ( repo_missing($repo) and access( $repo, $user, '^C', 'any' ) !~ /DENIED/ ) {
require Gitolite::Conf::Store;
Gitolite::Conf::Store->import;
new_wild_repo( $repo, $user, $aa );
gl_log( 'create', $repo, $user, $aa );
new_wild_repo( $repo, $user );
gl_log( 'create', $repo, $user );
}
# a ref of 'any' signifies that this is a pre-git check, where we don't
@ -161,14 +158,14 @@ sub parse_soc {
exit 0;
}
_die "unknown git/gitolite command: '$soc'";
_die "unknown git/gitolite command: $soc";
}
sub sanity {
my $repo = shift;
_die "'$repo' contains bad characters" if $repo !~ $REPONAME_PATT;
_die "'$repo' ends with a '/'" if $repo =~ m(/$);
_die "'$repo' contains '..'" if $repo =~ m(\.\.);
_die "'$repo' contains '..'" if $repo =~ m(\.\.$);
}
# ----------------------------------------------------------------------
@ -212,7 +209,6 @@ sub http_simulate_ssh_connection {
my ($verb) = ( $ENV{PATH_INFO} =~ m(^/(\S+)) );
my $args = $ENV{QUERY_STRING};
$args =~ s/\+/ /g;
$args =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
$ENV{SSH_ORIGINAL_COMMAND} = $verb;
$ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args;
http_print_headers(); # in preparation for the eventual output!
@ -234,6 +230,5 @@ sub http_print_headers {
print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n";
print "Pragma: no-cache\r\n";
print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n";
print "Content-Type: text/plain\r\n";
print "\r\n";
}

View file

@ -12,10 +12,6 @@ package Gitolite::Common;
usage tsh_run
gen_lfn
gl_log
dd
t_start
t_lap
);
#>>>
use Exporter 'import';
@ -67,26 +63,6 @@ sub dbg {
}
}
sub dd {
local $ENV{D} = 1;
dbg(@_);
}
{
use Time::HiRes;
my %start_times;
sub t_start {
my $name = shift || 'default';
$start_times{$name} = [ Time::HiRes::gettimeofday() ];
}
sub t_lap {
my $name = shift || 'default';
return Time::HiRes::tv_interval( $start_times{$name} );
}
}
sub _warn {
gl_log( 'warn', @_ );
if ( $ENV{D} and $ENV{D} >= 3 ) {
@ -275,18 +251,17 @@ sub gl_log {
my $tid = $ENV{GL_TID} ||= $$;
my $fh;
logger_plus_stderr( "errors found before logging could be setup", "$msg" ) if not $ENV{GL_LOGFILE};
open my $lfh, ">>", $ENV{GL_LOGFILE}
or logger_plus_stderr( "errors found but logfile could not be created", "$ENV{GL_LOGFILE}: $!", "$msg" );
logger_plus_stderr( "$ts no GL_LOGFILE env var", "$ts $msg" ) if not $ENV{GL_LOGFILE};
open my $lfh, ">>", $ENV{GL_LOGFILE} or logger_plus_stderr( "open log failed: $!", $msg );
print $lfh "$ts\t$tid\t$msg\n";
close $lfh;
}
sub logger_plus_stderr {
open my $fh, "|-", "logger" or confess "it's really not my day is it...?\n";
for ( @_ ) {
print STDERR "FATAL: $_\n";
print $fh "FATAL: $_\n";
for ( "FATAL: have errors but logging failed!\n", @_ ) {
print STDERR "$_\n";
print $fh "$_\n";
}
exit 1;
}

View file

@ -34,10 +34,6 @@ sub compile {
# place to put the individual gl-conf files
new_repos();
store();
for my $repo ( @{ $rc{NEW_REPOS_CREATED} } ) {
trigger( 'POST_CREATE', $repo );
}
}
sub parse {
@ -66,7 +62,7 @@ sub parse {
my @validkeys = split( ' ', ( $rc{GIT_CONFIG_KEYS} || '' ) );
push @validkeys, "gitolite-options\\..*";
my @matched = grep { $key =~ /^$_$/ } @validkeys;
_die "git config '$key' not allowed\ncheck GIT_CONFIG_KEYS in the rc file" if ( @matched < 1 );
_die "git config $key not allowed\ncheck GIT_CONFIG_KEYS in the rc file" if ( @matched < 1 );
_die "bad value '$value'" if $value =~ $UNSAFE_PATT;
add_config( 1, $key, $value );
} elsif ( $line =~ /^subconf (\S+)$/ ) {

View file

@ -27,7 +27,7 @@ sub explode {
my ( $file, $subconf, $out ) = @_;
# seed the 'seen' list if it's empty
$included{ device_inode("gitolite.conf") }++ unless %included;
$included{ device_inode("conf/gitolite.conf") }++ unless %included;
my $fh = _open( "<", $file );
while (<$fh>) {
@ -52,7 +52,7 @@ sub incsub {
my $is_subconf = ( +shift eq 'subconf' );
my ( $new_subconf, $include_glob, $current_subconf, $out ) = @_;
_die "subconf '$current_subconf' attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master';
_die "subconf $current_subconf attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master';
_die "invalid include/subconf file/glob '$include_glob'"
unless $include_glob =~ /^"(.+)"$/
@ -63,7 +63,7 @@ sub incsub {
for my $file ( glob($include_glob) ) {
_warn("included file not found: '$file'"), next unless -f $file;
_die "invalid include/subconf filename '$file'" unless $file =~ m(([^/]+).conf$);
_die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$);
my $basename = $1;
next if already_included($file);

View file

@ -32,7 +32,6 @@ our $data_version = '';
our %repos;
our %one_repo;
our %groups;
our %patterns;
our %configs;
our %one_config;
our %split_conf;
@ -68,18 +67,13 @@ my $last_repo = '';
sub access {
my ( $repo, $user, $aa, $ref ) = @_;
_die "invalid repo '$repo'" if not( $repo and $repo =~ $REPOPATT_PATT );
_die "invalid user '$user'" if not( $user and $user =~ $USERNAME_PATT );
sanity($repo);
my @rules;
my $deny_rules;
my $deny_rules = option( $repo, 'deny-rules' );
load($repo);
@rules = rules( $repo, $user );
$deny_rules = option( $repo, 'deny-rules' );
# sanity check the only piece the user can control
_die "invalid characters in ref or filename: '$ref'\n" unless $ref =~ $REF_OR_FILENAME_PATT;
_die "invalid characters in ref or filename: $ref\n" unless $ref =~ $REF_OR_FILENAME_PATT;
# when a real repo doesn't exist, ^C is a pre-requisite for any other
# check to give valid results.
@ -94,6 +88,7 @@ sub access {
return "$aa $ref $repo $user DENIED by existence";
}
my @rules = rules( $repo, $user );
trace( 2, scalar(@rules) . " rules found" );
for my $r (@rules) {
my $perm = $r->[1];
@ -124,11 +119,8 @@ sub git_config {
my ( $repo, $key, $empty_values_OK ) = @_;
$key ||= '.';
if (repo_missing($repo)) {
load_common();
} else {
load($repo);
}
return {} if repo_missing($repo);
load($repo);
# read comments bottom up
my %ret =
@ -168,14 +160,6 @@ sub git_config {
}
}
my($k, $v);
my $creator = creator($repo);
while (($k, $v) = each %ret) {
$v =~ s/%GL_REPO/$repo/g;
$v =~ s/%GL_CREATOR/$creator/g if $creator;
$ret{$k} = $v;
}
trace( 3, map { ( "$_" => "-> $ret{$_}" ) } ( sort keys %ret ) );
return \%ret;
}
@ -188,18 +172,8 @@ sub option {
return $ret->{$option};
}
sub sanity {
my $repo = shift;
_die "invalid repo '$repo'" if not( $repo and $repo =~ $REPOPATT_PATT );
_die "'$repo' ends with a '/'" if $repo =~ m(/$);
_die "'$repo' contains '..'" if $repo =~ $REPONAME_PATT and $repo =~ m(\.\.);
}
sub repo_missing {
my $repo = shift;
sanity($repo);
return not -d "$rc{GL_REPO_BASE}/$repo.git";
}
@ -219,11 +193,11 @@ sub load_common {
my $cc = "conf/gitolite.conf-compiled.pm";
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
if ( data_version_mismatch() ) {
_system("gitolite setup");
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
_die "data version update failed; this is serious" if data_version_mismatch();
}
}
@ -234,7 +208,7 @@ sub load_1 {
trace( 3, $repo );
if ( repo_missing($repo) ) {
trace( 1, "repo '$repo' missing" ) if $repo =~ $REPONAME_PATT;
trace( 1, "repo '$repo' missing" );
return;
}
_chdir("$rc{GL_REPO_BASE}/$repo.git");
@ -246,10 +220,10 @@ sub load_1 {
}
if ( -f "gl-conf" ) {
return if not $split_conf{$repo};
_warn "split conf not set, gl-conf present for '$repo'" if not $split_conf{$repo};
my $cc = "./gl-conf";
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
my $cc = "gl-conf";
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
$last_repo = $repo;
$repos{$repo} = $one_repo{$repo};
@ -278,7 +252,7 @@ sub load_1 {
for my $r (@repos) {
for my $u (@users) {
push @rules, @{ $repos{$r}{$u} } if exists $repos{$r} and exists $repos{$r}{$u};
push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u};
}
}
@ -308,11 +282,9 @@ sub load_1 {
sub memberships {
trace( 3, @_ );
my ( $type, $base, $repo ) = @_;
$repo ||= '';
my @ret;
my $base2 = '';
@ret = ( $base, '@all' );
my @ret = ( $base, '@all' );
if ( $type eq 'repo' ) {
# first, if a repo, say, pub/sitaram/project, has a gl-creator file
@ -327,16 +299,12 @@ sub memberships {
}
}
push @ret, @{ $groups{$base} } if exists $groups{$base};
push @ret, @{ $groups{$base2} } if $base2 and exists $groups{$base2};
for my $i ( keys %{ $patterns{groups} } ) {
if ( $base =~ /^$i$/ or $base2 and ( $base2 =~ /^$i$/ ) ) {
for my $i ( keys %groups ) {
if ( $base eq $i or $base =~ /^$i$/ or $base2 and ( $base2 eq $i or $base2 =~ /^$i$/ ) ) {
push @ret, @{ $groups{$i} };
}
}
push @ret, @{ ext_grouplist($base) } if $type eq 'user' and $rc{GROUPLIST_PGM};
if ( $type eq 'user' and $repo and not repo_missing($repo) ) {
# find the roles this user has when accessing this repo and add those
# in as groupnames he is a member of. You need the already existing
@ -344,6 +312,8 @@ sub memberships {
push @ret, user_roles( $base, $repo, @ret );
}
push @ret, @{ ext_grouplist($base) } if $type eq 'user' and $rc{GROUPLIST_PGM};
@ret = @{ sort_u( \@ret ) };
trace( 3, sort @ret );
return @ret;
@ -386,8 +356,6 @@ sub user_roles {
for (@roles) {
# READERS u3 u4 @g1
s/^\s+//; s/ +$//; s/=/ /; s/\s+/ /g; s/^\@//;
next if /^#/;
next unless /\S/;
my ( $role, @members ) = split;
# role = READERS, members = u3, u4, @g1
if ( $role ne 'CREATOR' and not $rc{ROLES}{$role} ) {
@ -420,7 +388,8 @@ sub generic_name {
$creator = creator($base);
$base2 = $base;
$base2 =~ s(\b$creator\b)(CREATOR) if $creator;
$base2 =~ s(/$creator/)(/CREATOR/) if $creator;
$base2 =~ s(^$creator/)(CREATOR/) if $creator;
$base2 = '' if $base2 eq $base; # if there was no change
return $base2;
@ -428,8 +397,6 @@ sub generic_name {
sub creator {
my $repo = shift;
sanity($repo);
return ( $ENV{GL_USER} || '' ) if repo_missing($repo);
my $f = "$rc{GL_REPO_BASE}/$repo.git/gl-creator";
my $creator = '';

View file

@ -63,20 +63,12 @@ sub add_to_group {
}
sub set_repolist {
@repolist = @_;
@repolist = ();
# ...sanity checks
for (@_) {
if ( check_subconf_repo_disallowed( $subconf, $_ ) ) {
(my $repo = $_) =~ s/^\@$subconf\./locally modified \@/;
$ignored{$subconf}{$repo} = 1;
next;
}
for (@repolist) {
_warn "explicit '.git' extension ignored for $_.git" if s/\.git$//;
_die "bad reponame '$_'" if $_ !~ $REPOPATT_PATT;
push @repolist, $_;
}
}
@ -111,6 +103,13 @@ sub add_rule {
$nextseq++;
for my $repo (@repolist) {
if ( check_subconf_repo_disallowed( $subconf, $repo ) ) {
my $repo = $repo;
$repo =~ s/^\@$subconf\./locally modified \@/;
$ignored{$subconf}{$repo} = 1;
next;
}
push @{ $repos{$repo}{$user} }, [ $nextseq, $perm, $ref ];
}
}
@ -138,7 +137,7 @@ sub expand_list {
for my $item (@list) {
if ( $item =~ /^@/ and $item ne '@all' ) # nested group
{
_die "undefined group '$item'" unless $groups{$item};
_die "undefined group $item" unless $groups{$item};
# add those names to the list
push @new_list, sort keys %{ $groups{$item} };
} else {
@ -156,7 +155,7 @@ sub new_repos {
# normal repos
my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos;
# add in members of repo groups
map { push @repos, keys %{ $groups{$_} } } grep { /^@/ and $_ ne '@all' } keys %repos;
map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos;
for my $repo ( @{ sort_u( \@repos ) } ) {
next unless $repo =~ $REPONAME_PATT; # skip repo patterns
@ -165,11 +164,7 @@ sub new_repos {
# use gl-conf as a sentinel
hook_1($repo) if -d "$repo.git" and not -f "$repo.git/gl-conf";
if (not -d "$repo.git") {
push @{ $rc{NEW_REPOS_CREATED} }, $repo;
trigger( 'PRE_CREATE', $repo );
new_repo($repo);
}
new_repo($repo) if not -d "$repo.git";
}
}
@ -185,14 +180,14 @@ sub new_repo {
}
sub new_wild_repo {
my ( $repo, $user, $aa ) = @_;
my ( $repo, $user ) = @_;
_chdir( $rc{GL_REPO_BASE} );
trigger( 'PRE_CREATE', $repo, $user, $aa );
trigger( 'PRE_CREATE', $repo, $user );
new_repo($repo);
_print( "$repo.git/gl-creator", $user );
_print( "$repo.git/gl-perms", ( $rc{DEFAULT_ROLE_PERMS} ? "$rc{DEFAULT_ROLE_PERMS}\n" : "" ) );
trigger( 'POST_CREATE', $repo, $user, $aa );
_print( "$repo.git/gl-perms", "$rc{DEFAULT_ROLE_PERMS}\n" ) if $rc{DEFAULT_ROLE_PERMS};
trigger( 'POST_CREATE', $repo, $user );
_chdir( $rc{GL_ADMIN_BASE} );
}
@ -258,18 +253,15 @@ sub store_1 {
# warning: writes and *deletes* it from %repos and %configs
my ($repo) = shift;
trace( 3, $repo );
return unless ( $repos{$repo} or $configs{$repo} ) and -d "$repo.git";
return unless $repos{$repo} and -d "$repo.git";
my ( %one_repo, %one_config );
open( my $compiled_fh, ">", "$repo.git/gl-conf" ) or return;
my $dumped_data = '';
if ($repos{$repo}) {
$one_repo{$repo} = $repos{$repo};
delete $repos{$repo};
$dumped_data = Data::Dumper->Dump( [ \%one_repo ], [qw(*one_repo)] );
}
$one_repo{$repo} = $repos{$repo};
delete $repos{$repo};
my $dumped_data = Data::Dumper->Dump( [ \%one_repo ], [qw(*one_repo)] );
if ( $configs{$repo} ) {
$one_config{$repo} = $configs{$repo};
@ -288,8 +280,6 @@ sub store_common {
my $cc = "conf/gitolite.conf-compiled.pm";
my $compiled_fh = _open( ">", "$cc.new" );
my %patterns = ();
my $data_version = glrc('current-data-version');
trace( 3, "data_version = $data_version" );
print $compiled_fh Data::Dumper->Dump( [$data_version], [qw(*data_version)] );
@ -303,16 +293,7 @@ sub store_common {
my %groups = %{ inside_out( \%groups ) };
$dumped_data = Data::Dumper->Dump( [ \%groups ], [qw(*groups)] );
print $compiled_fh $dumped_data;
# save patterns in %groups for faster handling of multiple repos, such
# as happens in the various POST_COMPILE scripts
for my $k (keys %groups) {
$patterns{groups}{$k} = 1 unless $k =~ $REPONAME_PATT;
}
}
print $compiled_fh Data::Dumper->Dump( [ \%patterns ], [qw(*patterns)] ) if %patterns;
print $compiled_fh Data::Dumper->Dump( [ \%split_conf ], [qw(*split_conf)] ) if %split_conf;
close $compiled_fh or _die "close compiled-conf failed: $!\n";
@ -338,13 +319,14 @@ sub store_common {
$hook_reset++;
}
# propagate user-defined (custom) hooks to all repos
ln_sf( "$rc{LOCAL_CODE}/hooks/common", "*", "$repo.git/hooks" ) if $rc{LOCAL_CODE};
# override/propagate gitolite defined hooks for all repos
# propagate user hooks
ln_sf( "$rc{GL_ADMIN_BASE}/hooks/common", "*", "$repo.git/hooks" );
# override/propagate gitolite defined hooks for the admin repo
# propagate admin hook
ln_sf( "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin';
# g2 diff: no "site-wide" hooks (the stuff in between gitolite hooks
# and user hooks) anymore. I don't think anyone used them anyway...
}
}

View file

@ -4,7 +4,7 @@ package SugarBox;
sub run_sugar_script {
my ( $ss, $lref ) = @_;
do $ss if -r $ss;
do $ss if -x $ss;
$lref = sugar_script($lref);
return $lref;
}
@ -52,9 +52,9 @@ sub sugar {
# perl-ism; apart from keeping the full path separate from the
# simple name, this also protects %rc from change by implicit
# aliasing, which would happen if you touched $s itself
my $sfp = _which("syntactic-sugar/$s", 'r');
my $sfp = "$ENV{GL_BINDIR}/syntactic-sugar/$s";
_warn("skipped sugar script '$s'"), next if not -r $sfp;
_warn("skipped sugar script '$s'"), next if not -x $sfp;
$lines = SugarBox::run_sugar_script( $sfp, $lines );
$lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ];
}
@ -131,17 +131,17 @@ sub owner_desc {
# -> config gitweb.description = some long description
for my $line (@$lines) {
if ( $line =~ /^desc = (\S.*)/ ) {
if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
push @ret, "repo $repo";
push @ret, "config gitweb.description = $desc";
push @ret, "config gitweb.owner = $owner" if $owner;
} elsif ( $line =~ /^desc = (\S.*)/ ) {
push @ret, "config gitweb.description = $1";
} elsif ( $line =~ /^owner = (\S.*)/ ) {
push @ret, "config gitweb.owner = $1";
} elsif ( $line =~ /^category = (\S.*)/ ) {
push @ret, "config gitweb.category = $1";
} elsif ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
push @ret, "repo $repo";
push @ret, "config gitweb.description = $desc";
push @ret, "config gitweb.owner = $owner" if $owner;
} else {
push @ret, $line;
}

View file

@ -5,25 +5,6 @@ package Gitolite::Easy;
# most/all functions in this module test $ENV{GL_USER}'s rights and
# permissions so it needs to be set.
# "use"-ing this module
# ----------------------------------------------------------------------
# Using this module from within a gitolite trigger or command is easy; you
# just need 'use lib $ENV{GL_LIBDIR};' before the 'use Gitolite::Easy;'.
#
# Using it from something completely outside gitolite requires a bit more
# work. First, run 'gitolite query-rc -a' to find the correct values for
# GL_BINDIR and GL_LIBDIR in your installation. Then use this code in your
# external program, using the paths you just found:
#
# BEGIN {
# $ENV{GL_BINDIR} = "/full/path/to/gitolite/src";
# $ENV{GL_LIBDIR} = "/full/path/to/gitolite/src/lib";
# }
# use lib $ENV{GL_LIBDIR};
# use Gitolite::Easy;
# API documentation
# ----------------------------------------------------------------------
# documentation for each function is at the top of the function.
# Documentation is NOT in pod format; just read the source with a nice syntax
# coloring text editor and you'll be happy enough. (I do not like POD; please

View file

@ -23,22 +23,13 @@ sub post_update {
# this is the *real* post_update hook for gitolite
tsh_try("git ls-tree --name-only master");
_die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/m;
_die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/;
my $hooks_changed = 0;
{
local $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE};
tsh_try("git diff --name-only master");
$hooks_changed++ if tsh_text() =~ m(/hooks/common/);
# the leading slash ensure that this hooks/common directory is below
# some top level directory, not *at* the top. That's LOCAL_CODE, and
# it's actual name could be anything but it doesn't matter to us.
tsh_try("git checkout -f --quiet master");
}
_system("gitolite compile");
_system("gitolite setup --hooks-only") if $hooks_changed;
_system("gitolite trigger POST_COMPILE");
exit 0;

View file

@ -29,7 +29,7 @@ sub update {
trace( 1, 'update', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, @ARGV );
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret, $oldsha, $newsha );
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret );
_die $ret if $ret =~ /DENIED/;
check_vrefs( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa );
@ -61,10 +61,10 @@ sub check_vrefs {
}
} else {
my ( $dummy, $pgm, @args ) = split '/', $vref;
$pgm = _which("VREF/$pgm", 'x');
$pgm or _die "'$vref': helper program missing or unexecutable";
$pgm = "$ENV{GL_BINDIR}/VREF/$pgm";
-x $pgm or _die "$vref: helper program missing or unexecutable";
open( my $fh, "-|", $pgm, @_, $vref, @args ) or _die "'$vref': can't spawn helper program: $!";
open( my $fh, "-|", $pgm, @_, $vref, @args ) or _die "$vref: can't spawn helper program: $!";
while (<$fh>) {
# print non-vref lines and skip processing (for example,
# normal STDOUT by a normal update hook)
@ -88,7 +88,6 @@ sub check_vref {
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
trace( 2, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref)", "-> $ret" );
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret );
_die "$ret" . ( $deny_message ? "\n$deny_message" : '' )
if $ret =~ /DENIED/ and $ret !~ /by fallthru/;
trace( 2, "remember, fallthru is success here!" ) if $ret =~ /by fallthru/;

View file

@ -9,7 +9,6 @@ package Gitolite::Rc;
query_rc
version
trigger
_which
$REMOTE_COMMAND_PATT
$REF_OR_FILENAME_PATT
@ -44,29 +43,24 @@ $rc{LOG_TEMPLATE} = "$ENV{HOME}/.gitolite/logs/gitolite-%y-%m.log";
# variables that should probably never be changed but someone will want to, I'll bet...
# ----------------------------------------------------------------------
#<<<
$REMOTE_COMMAND_PATT = qr(^[-0-9a-zA-Z._\@/+ :,\%=]*$);
$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][-0-9a-zA-Z._\@/+ :,]*$);
$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@/+]*$);
$REPOPATT_PATT = qr(^\@?[[0-9a-zA-Z][-0-9a-zA-Z._\@/+\\^$|()[\]*?{},]*$);
$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@+]*$);
$REMOTE_COMMAND_PATT = qr(^[- 0-9a-zA-Z\@\%_=+:,./]*$);
$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$);
$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$);
$REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$);
$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$);
$UNSAFE_PATT = qr([`~#\$\&()|;<>]);
#>>>
# ----------------------------------------------------------------------
# find the rc file and 'do' it
# ----------------------------------------------------------------------
my $current_data_version = "3.2";
my $current_data_version = "3.0";
my $rc = glrc('filename');
if (-r $rc and -s $rc) {
do $rc or die $@;
}
do $rc if -r $rc;
if ( defined($GL_ADMINDIR) ) {
say2 "";
say2 "FATAL: '$rc' seems to be for older gitolite; please see doc/g2migr.mkd\n" . "(online at http://sitaramc.github.com/gitolite/g2migr.html)";
say2 "FATAL: $rc seems to be for older gitolite; please see doc/g2migr.mkd\n" . "(online at http://sitaramc.github.com/gitolite/g3/g2migr.html)";
exit 1;
}
@ -86,12 +80,10 @@ unshift @{ $rc{ACCESS_1} }, 'Writable::access_1';
# use an env var that is highly unlikely to appear in real life :)
do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC};
# setup some perl/rc/env vars
# fix some env vars, setup gitolite internal "env" vars (aka rc vars)
# ----------------------------------------------------------------------
unshift @INC, "$rc{LOCAL_CODE}/lib" if $rc{LOCAL_CODE};
$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}" unless $ENV{PATH} =~ /^$ENV{GL_BINDIR}:/;
$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}";
{
$rc{GL_TID} = $ENV{GL_TID} ||= $$;
@ -137,7 +129,7 @@ sub glrc {
} elsif ( $cmd eq 'current-data-version' ) {
return $current_data_version;
} else {
_die "unknown argument to glrc: '$cmd'";
_die "unknown argument to glrc: $cmd";
}
}
@ -161,36 +153,11 @@ sub query_rc {
exit 0;
}
my $cv = \%rc; # current "value"
while (@vars) {
my $v = shift @vars;
# dig into the rc hash, using each var as a component
if (not ref($cv)) {
_warn "unused arguments...";
last;
} elsif (ref($cv) eq 'HASH') {
$cv = $cv->{$v} || '';
} elsif (ref($cv) eq 'ARRAY') {
$cv = $cv->[$v] || '';
} else {
_die "dont know what to do with " . ref($cv) . " item in the rc file";
}
}
# we've run out of arguments so $cv is what we have. If we're supposed to
# be quiet, we don't have to print anything so let's get that done first:
exit ( $cv ? 0 : 1 ) if $quiet; # shell truth
# print values (notice we ignore the '-n' option if it's a ref)
if (ref($cv) eq 'HASH') {
print join("\n", sort keys %$cv), "\n" if %$cv;
} elsif (ref($cv) eq 'ARRAY') {
print join("\n", @$cv), "\n" if @$cv;
} else {
print $cv . ( $nonl ? '' : "\n" ) if $cv;
}
exit ( $cv ? 0 : 1 ); # shell truth
my @res = map { $rc{$_} } grep { $rc{$_} } @vars;
print join( "\t", @res ) . ( $nonl ? '' : "\n" ) if not $quiet and @res;
# shell truth
exit 0 if @res;
exit 1;
}
sub version {
@ -208,7 +175,7 @@ sub trigger {
if ( exists $rc{$rc_section} ) {
if ( ref( $rc{$rc_section} ) ne 'ARRAY' ) {
_die "'$rc_section' section in rc file is not a perl list";
_die "$rc_section section in rc file is not a perl list";
} else {
for my $s ( @{ $rc{$rc_section} } ) {
my ( $pgm, @args ) = split ' ', $s;
@ -220,9 +187,9 @@ sub trigger {
Gitolite::Triggers::run( $module, $sub, @args, $rc_section, @_ );
} else {
$pgm = _which("triggers/$pgm", 'x');
$pgm = "$ENV{GL_BINDIR}/triggers/$pgm";
_warn("skipped command '$s'"), next if not $pgm;
_warn("skipped command '$s'"), next if not -x $pgm;
trace( 2, "command: $s" );
_system( $pgm, @args, $rc_section, @_ ); # they better all return with 0 exit codes!
}
@ -233,56 +200,25 @@ sub trigger {
trace( 2, "'$rc_section' not found in rc" );
}
sub _which {
# looks for a file in LOCAL_CODE or GL_BINDIR. Returns whichever exists
# (LOCAL_CODE preferred if defined) or 0 if not found.
my $file = shift;
my $mode = shift; # could be 'x' or 'r'
my @files = ("$rc{GL_BINDIR}/$file");
unshift @files, ("$rc{LOCAL_CODE}/$file") if $rc{LOCAL_CODE};
for my $f ( @files ) {
return $f if -x $f;
return $f if -r $f and $mode eq 'r';
}
return 0;
}
# ----------------------------------------------------------------------
=for args
Usage: gitolite query-rc -a
gitolite query-rc [-n] [-q] rc-variable
gitolite query-rc [-n] <list of rc variables>
-a print all variables and values (first level only)
-n do not append a newline if variable is scalar
-a print all variables and values
-n do not append a newline
-q exit code only (shell truth; 0 is success)
Query the rc hash. Second and subsequent arguments dig deeper into the hash.
The examples are for the default configuration; yours may be different.
Example:
Single values:
gitolite query-rc GL_ADMIN_BASE # prints "/home/git/.gitolite" or similar
gitolite query-rc UMASK # prints "63" (that's 0077 in decimal!)
gitolite query-rc GL_ADMIN_BASE UMASK
# prints "/home/git/.gitolite<tab>0077" or similar
Hashes:
gitolite query-rc COMMANDS
# prints "desc", "help", "info", "perms", "writable", one per line
gitolite query-rc COMMANDS help # prints 1
gitolite query-rc -q COMMANDS help # prints nothing; exit code is 0
gitolite query-rc COMMANDS fork # prints nothing; exit code is 1
Arrays (somewhat less useful):
gitolite query-rc POST_GIT # prints nothing; exit code is 0
gitolite query-rc POST_COMPILE # prints 4 lines
gitolite query-rc POST_COMPILE 0 # prints the first of those 4 lines
Explore:
gitolite query-rc -a
# prints all first level variables and values, one per line. Any that are
# listed as HASH or ARRAY can be explored further in subsequent commands.
# prints all known variables and values, one per line
Note: '-q' is best used with only one variable.
=cut
sub args {
@ -313,21 +249,11 @@ __DATA__
# (Tip: perl allows a comma after the last item in a list also!)
# HELP for commands (see COMMANDS list below) can be had by running the
# command with "-h" as the sole argument.
# HELP for all the other external programs (the syntactic sugar helpers and
# the various programs/functions in the 8 trigger lists), can be found in
# doc/non-core.mkd (http://sitaramc.github.com/gitolite/non-core.html) or in
# the corresponding source file itself.
%RC = (
# if you're using mirroring, you need a hostname. This is *one* simple
# word, not a full domain name. See documentation if in doubt
# HOSTNAME => 'darkstar',
UMASK => 0077,
# look in the "GIT-CONFIG" section in the README for what to do
GIT_CONFIG_KEYS => '',
# comment out if you don't need all the extra detail in the logfile
@ -376,16 +302,15 @@ __DATA__
SYNTACTIC_SUGAR =>
[
# 'continuation-lines',
# 'keysubdirs-as-groups',
],
# comment out or uncomment as needed
# these will run in sequence to modify the input (arguments and environment)
INPUT =>
[
# if you use this, make this the first item in the list
# 'CpuTime::input',
# 'Shell::input',
# 'Alias::input',
# 'Mirroring::input',
],
@ -399,8 +324,12 @@ __DATA__
# these will run in sequence just before the actual git command is invoked
PRE_GIT =>
[
# if you use this, make this the first item in the list
# 'renice 10',
# 'Mirroring::pre_git',
# see docs ("list of non-core programs shipped") for details
# 'partial-copy',
],
@ -415,6 +344,8 @@ __DATA__
POST_GIT =>
[
# 'Mirroring::post_git',
# if you use this, make this the last item in the list
# 'CpuTime::post_git',
],
@ -425,7 +356,7 @@ __DATA__
],
# comment out or uncomment as needed
# these will run in sequence after a new repo is created
# these will run in sequence after a new wild repo is created
POST_CREATE =>
[
'post-compile/update-git-configs',

View file

@ -6,30 +6,21 @@ package Gitolite::Setup;
=for args
Usage: gitolite setup [<option>]
Setup gitolite, compile conf, run the POST_COMPILE trigger (see rc file) and
propagate hooks.
-a, --admin <name> admin name
-pk, --pubkey <file> pubkey file name
-ho, --hooks-only skip other steps and just propagate hooks
-a, --admin <name> admin name
First run: either the pubkey or the admin name is *required*, depending on
whether you're using ssh mode or http mode.
Setup gitolite, compile conf, and fixup hooks. Either the pubkey or the admin
name is required on the first run, depending on whether you're using ssh mode
or http mode.
Subsequent runs:
- Without options, 'gitolite setup' is a general "fix up everything" command
(for example, if you brought in repos from outside, or someone messed
around with the hooks, or you made an rc file change that affects access
rules, etc.)
- '-pk' can be used to replace the admin key; useful if you lost the admin's
private key but do have shell access to the server.
- '-ho' is mainly for scripting use. Do not combine with other options.
- '-a' is ignored
- 'gitolite setup': fix up hooks if you brought in repos from outside, or if
someone has been playing around with the hooks and may have deleted some.
- 'gitolite setup -pk YourName.pub': replace keydir/YourName.pub and
recompile/push. Useful if you lost your key. In fact you can do this for
any key in keydir (but not in subdirectories).
=cut
# ----------------------------------------------------------------------
@ -51,15 +42,12 @@ use warnings;
# ----------------------------------------------------------------------
sub setup {
my ( $admin, $pubkey, $h_only, $argv ) = args();
my ( $admin, $pubkey, $argv ) = args();
setup_glrc();
setup_gladmin( $admin, $pubkey, $argv );
unless ($h_only) {
setup_glrc();
setup_gladmin( $admin, $pubkey, $argv );
_system("gitolite compile");
_system("gitolite trigger POST_COMPILE");
}
_system("gitolite compile");
_system("gitolite trigger POST_COMPILE");
hook_repos(); # all of them, just to be sure
}
@ -69,33 +57,30 @@ sub setup {
sub args {
my $admin = '';
my $pubkey = '';
my $h_only = 0;
my $help = 0;
my $argv = join( " ", @ARGV );
GetOptions(
'admin|a=s' => \$admin,
'pubkey|pk=s' => \$pubkey,
'hooks-only|ho' => \$h_only,
'help|h' => \$help,
'admin|a=s' => \$admin,
'pubkey|pk=s' => \$pubkey,
'help|h' => \$help,
) or usage();
usage() if $help or ( $pubkey and $admin );
usage() if $h_only and ($admin or $pubkey);
if ($pubkey) {
$pubkey =~ /\.pub$/ or _die "'$pubkey' name does not end in .pub";
$pubkey =~ /\@/ and _die "'$pubkey' name contains '\@'";
tsh_try("cat $pubkey") or _die "'$pubkey' not a readable file";
tsh_lines() == 1 or _die "'$pubkey' must have exactly one line";
tsh_try("ssh-keygen -l -f $pubkey") or _die "'$pubkey' does not seem to be a valid ssh pubkey file";
$pubkey =~ /\.pub$/ or _die "$pubkey name does not end in .pub";
$pubkey =~ /\@/ and _die "$pubkey name contains '\@'";
tsh_try("cat $pubkey") or _die "$pubkey not a readable file";
tsh_lines() == 1 or _die "$pubkey must have exactly one line";
tsh_try("ssh-keygen -l -f $pubkey") or _die "$pubkey does not seem to be a valid ssh pubkey file";
$admin = $pubkey;
$admin =~ s(.*/)();
$admin =~ s/\.pub$//;
}
return ( $admin || '', $pubkey || '', $h_only || 0, $argv );
return ( $admin || '', $pubkey || '', $argv );
}
sub setup_glrc {
@ -104,7 +89,7 @@ sub setup_glrc {
sub setup_gladmin {
my ( $admin, $pubkey, $argv ) = @_;
_die "'-pk' or '-a' required; see 'gitolite setup -h' for more"
_die "no existing conf file found, '-pk' or '-a' required"
if not $admin and not -f "$rc{GL_ADMIN_BASE}/conf/gitolite.conf";
# reminder: 'admin files' are in ~/.gitolite, 'admin repo' is

View file

@ -37,12 +37,6 @@ use warnings;
# ----------------------------------------------------------------------
# make sure the user is ready for it
if (not $ENV{GITOLITE_TEST} or $ENV{GITOLITE_TEST} ne 'y') {
print "Bail out! See t/README for information on how to run the tests.\n";
exit 255;
}
# required preamble for all tests
try "
DEF gsh = /TRACE: gsh.SOC=/

View file

@ -261,12 +261,7 @@ sub rc_lines {
$cmd = shift @cmds;
# is the current command a "testing" command?
my $testing_cmd = (
$cmd =~ m(^ok(?:\s+or\s+(.*))?$)
or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$)
or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$)
or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$)
);
my $testing_cmd = ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) );
# warn if the previous command failed but rc is not being checked
if ( $rc and not $testing_cmd ) {
@ -479,7 +474,7 @@ sub fail {
sub cmp {
# compare input string with second input string or text()
my $in = shift;
my $in = shift;
my $text = ( @_ ? +shift : text() );
if ( $text eq $in ) {
@ -588,7 +583,7 @@ sub dummy_commits {
test_tick();
next;
}
my $ts = ( $tick ? gmtime( $tick + 19800 ) : gmtime() );
my $ts = ( $tick ? gmtime($tick+19800) : gmtime() );
_sh("echo $f at $ts >> $f && git add $f && git commit -m '$f at $ts'");
}
}

4
src/lib/Gitolite/Triggers/Alias.pm Normal file → Executable file
View file

@ -61,7 +61,7 @@ sub input {
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
my $user = $ARGV[0] || '@all'; # user name is undocumented for now
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /(?:$git_commands) '\/?(\S+)'$/ ) {
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /(?:$git_commands) '(\S+)'$/ ) {
my $repo = $1;
( my $norm = $repo ) =~ s/\.git$//; # normalised repo name
@ -73,7 +73,7 @@ sub input {
_warn "'$norm' is an alias for '$target'";
$ENV{SSH_ORIGINAL_COMMAND} =~ s/'\/?$repo'/'$target'/;
$ENV{SSH_ORIGINAL_COMMAND} =~ s/'$repo'/'$target'/;
}
}

View file

@ -1,24 +0,0 @@
package Gitolite::Triggers::AutoCreate;
use strict;
use warnings;
# perl trigger set for stuff to do with auto-creating repos
# ----------------------------------------------------------------------
# to deny auto-create on read access, add 'AutoCreate::deny_R' to the
# PRE_CREATE trigger list
sub deny_R {
die "autocreate denied\n" if $_[3] and $_[3] eq 'R';
return;
}
# to deny auto-create on read *and* write access, add 'AutoCreate::deny_RW' to
# the PRE_CREATE trigger list. This means you can only create repos using the
# 'create' command, (which needs to be enabled in the COMMANDS list).
sub deny_RW {
die "autocreate denied\n" if $_[3] and ( $_[3] eq 'R' or $_[3] eq 'W' );
return;
}
1;

3
src/lib/Gitolite/Triggers/CpuTime.pm Normal file → Executable file
View file

@ -10,7 +10,6 @@ use warnings;
# cpu and elapsed times for gitolite+git operations
# ----------------------------------------------------------------------
# uncomment the appropriate lines in the rc file to enable this
# Ideally, you will (a) write your own code with a different filename so later
# gitolite upgrades won't overwrite your copy, (b) add appropriate variables
@ -19,6 +18,8 @@ use warnings;
# ----------------------------------------------------------------------
my $start_time;
# this trigger is not yet documented; it gets called at the start and does not
# receive any arguments.
sub input {
_warn "something wrong with the invocation of CpuTime::input" if $ENV{GL_TID} ne $$;
$start_time = [ Time::HiRes::gettimeofday() ];

View file

@ -10,38 +10,14 @@ use warnings;
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
my $hn = $rc{HOSTNAME};
my ( $mode, $master, %slaves, %trusted_slaves );
# ----------------------------------------------------------------------
sub input {
unless ($ARGV[0] =~ /^server-(\S+)$/) {
_die "'$ARGV[0]' is not a valid server name" if $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/;
return;
}
return unless $ARGV[0] =~ /^server-(\S+)$/;
# note: we treat %rc as our own internal "poor man's %ENV"
$rc{FROM_SERVER} = $1;
trace( 3, "from_server: $1" );
my $sender = $rc{FROM_SERVER} || '';
# custom peer-to-peer commands. At present the only one is 'perms -c',
# sent from a mirror command
if ($ENV{SSH_ORIGINAL_COMMAND} =~ /^CREATOR=(\S+) perms -c '(\S+)'$/) {
$ENV{GL_USER} = $1;
my $repo = $2;
details($repo);
_die "$hn: '$repo' is local" if $mode eq 'local';
_die "$hn: '$repo' is native" if $mode eq 'master';
_die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
# this expects valid perms content on STDIN
_system("gitolite perms -c $repo");
# we're done. Yes, really...
exit 0;
}
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/ ) {
# my ($user, $newsoc, $repo) = ($1, $2, $3);
@ -57,6 +33,8 @@ sub input {
# ----------------------------------------------------------------------
my ( $mode, $master, %slaves, %trusted_slaves );
sub pre_git {
return unless $hn;
# nothing, and I mean NOTHING, happens if HOSTNAME is not set

View file

@ -1,80 +0,0 @@
package Gitolite::Triggers::RefexExpr;
use strict;
use warnings;
# track refexes passed and evaluate expressions on them
# ----------------------------------------------------------------------
# see instructions for use at the bottom of src/VREF/refex-expr
use Gitolite::Easy;
my %passed;
my %rules;
my $init_done = 0;
sub access_2 {
# get out quick for repos that don't have any rules
return if $init_done and not %rules;
# but we don't really know that the first time, heh!
if (not $init_done) {
my $repo = $_[1];
init($repo);
return unless %rules;
}
my $refex = $_[5];
return if $refex =~ /DENIED/;
$passed{$refex}++;
# evaluate the rules each time; it's not very expensive
for my $k (sort keys %rules) {
$ENV{"GL_REFEX_EXPR_" . $k} = eval_rule($rules{$k});
}
}
sub eval_rule {
my $rule = shift;
my $e;
$e = join " ", map { convert($_) } split ' ', $rule;
my $ret = eval $e;
_die "eval '$e' -> '$@'" if $@;
Gitolite::Common::trace(1, "RefexExpr", "'$rule' -> '$e' -> '$ret'");
return "'$rule' -> '$e'" if $ret;
}
my %constant;
%constant = map { $_ => $_ } qw(1 not and or xor + - ==);
$constant{'-lt'} = '<';
$constant{'-gt'} = '>';
$constant{'-eq'} = '==';
$constant{'-le'} = '<=';
$constant{'-ge'} = '>=';
$constant{'-ne'} = '!=';
sub convert {
my $i = shift;
return $i if $i =~ /^-?\d+$/;
return $constant{$i} || $passed{$i} || $passed{"refs/heads/$i"} || 0;
}
# called only once
sub init {
$init_done = 1;
my $repo = shift;
# find all the rule expressions
my %t = config($repo, "^gitolite-options\\.refex-expr\\.");
my ($k, $v);
# get rid of the cruft and store just the rule name as the key
while ( ($k, $v) = each %t) {
$k =~ s/^gitolite-options\.refex-expr\.//;
$rules{$k} = $v;
}
}
1;

View file

@ -1,56 +0,0 @@
package Gitolite::Triggers::RepoUmask;
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Conf::Load;
use strict;
use warnings;
# setting a repo specific umask
# ----------------------------------------------------------------------
# this is for people who are too paranoid to trust e.g., gitweb's repo
# exclusion logic, but not paranoid enough to put it on a different server
=for usage
* In the rc file, add 'RepoUmask::pre_git' and 'RepoUmask::post_create' to
the corresponding trigger lists.
* For each repo that is to get a different umask than the default, add a
line like this:
option umask = 0027
=cut
# sadly option/config values are not available at pre_create time for normal
# repos. So we have to do a one-time fixup in a post_create trigger.
sub post_create {
my $repo = $_[1];
my $umask = option($repo, 'umask');
_chdir($rc{GL_REPO_BASE}); # because using option() moves us to ADMIN_BASE!
return unless $umask;
# unlike the one in the rc file, this is a string
$umask = oct($umask);
my $mode = "0" . sprintf("%o", $umask ^ 0777);
system("chmod -R $mode $repo.git >&2");
}
sub pre_git {
my $repo = $_[1];
my $umask = option($repo, 'umask');
_chdir($rc{GL_REPO_BASE}); # because using option() moves us to ADMIN_BASE!
return unless $umask;
# unlike the one in the rc file, this is a string
umask oct($umask);
}
1;

Some files were not shown because too many files have changed in this diff Show more